eco-helpers 3.0.18 → 3.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.gitignore +1 -0
 - data/CHANGELOG.md +34 -3
 - data/eco-helpers.gemspec +3 -3
 - data/lib/eco/api/common/loaders/config/session.rb +12 -0
 - data/lib/eco/api/common/loaders/config/workflow/mailer.rb +17 -4
 - data/lib/eco/api/common/loaders/config.rb +10 -2
 - data/lib/eco/api/common/loaders/parser.rb +10 -0
 - data/lib/eco/api/common/people/default_parsers/csv_parser.rb +21 -208
 - data/lib/eco/api/common/people/default_parsers/helpers/expected_headers.rb +206 -0
 - data/lib/eco/api/common/people/default_parsers/helpers/null_parsing.rb +36 -0
 - data/lib/eco/api/common/people/default_parsers/helpers.rb +15 -0
 - data/lib/eco/api/common/people/default_parsers/json_parser.rb +56 -0
 - data/lib/eco/api/common/people/default_parsers/xls_parser.rb +13 -14
 - data/lib/eco/api/common/people/default_parsers.rb +2 -0
 - data/lib/eco/api/common/people/entry_factory.rb +15 -4
 - data/lib/eco/api/common/session/sftp.rb +5 -0
 - data/lib/eco/api/custom/mailer.rb +1 -0
 - data/lib/eco/api/error.rb +4 -0
 - data/lib/eco/api/session/batch/job.rb +14 -16
 - data/lib/eco/api/session/batch/jobs.rb +6 -8
 - data/lib/eco/api/session/batch/launcher/mode_size.rb +5 -2
 - data/lib/eco/api/session/batch/launcher/retry.rb +6 -1
 - data/lib/eco/api/session/batch/launcher/status_handling.rb +4 -2
 - data/lib/eco/api/session/batch/launcher.rb +3 -3
 - data/lib/eco/api/session/config/api.rb +1 -0
 - data/lib/eco/api/session/config/apis/one_off.rb +6 -6
 - data/lib/eco/api/session/config/workflow.rb +16 -3
 - data/lib/eco/api/session.rb +13 -7
 - data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +1 -0
 - data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +2 -0
 - data/lib/eco/api/usecases/default/utils/cli/group_csv_cli.rb +26 -0
 - data/lib/eco/api/usecases/default/utils/cli/json_to_csv_cli.rb +10 -0
 - data/lib/eco/api/usecases/default/utils/cli/sort_csv_cli.rb +17 -0
 - data/lib/eco/api/usecases/default/utils/cli/split_json_cli.rb +15 -0
 - data/lib/eco/api/usecases/default/utils/group_csv_case.rb +213 -0
 - data/lib/eco/api/usecases/default/utils/json_to_csv_case.rb +71 -0
 - data/lib/eco/api/usecases/default/utils/sort_csv_case.rb +127 -0
 - data/lib/eco/api/usecases/default/utils/split_json_case.rb +224 -0
 - data/lib/eco/api/usecases/default/utils.rb +4 -0
 - data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +22 -15
 - data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +6 -6
 - data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +1 -0
 - data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +1 -1
 - data/lib/eco/api/usecases/ooze_samples/ooze_run_base_case.rb +8 -5
 - data/lib/eco/cli_default/workflow.rb +10 -4
 - data/lib/eco/csv/stream.rb +2 -0
 - data/lib/eco/csv.rb +3 -2
 - data/lib/eco/language/methods/delegate_missing.rb +4 -3
 - data/lib/eco/version.rb +1 -1
 - metadata +22 -9
 
| 
         @@ -71,7 +71,7 @@ module Eco 
     | 
|
| 
       71 
71 
     | 
    
         
             
                        tap_status(status: status, enviro: enviro, queue: data, method: method) do |overall_status|
         
     | 
| 
       72 
72 
     | 
    
         
             
                          pending_for_server_error = data.to_a[0..]
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                          batch_mode_on(*RETRY_ON, options: options, allow_job_mode: job_mode) do | 
     | 
| 
      
 74 
     | 
    
         
            +
                          batch_mode_on(*RETRY_ON, options: options, allow_job_mode: job_mode) do |as_job_mode, per_page|
         
     | 
| 
       75 
75 
     | 
    
         
             
                            iteration  = 0
         
     | 
| 
       76 
76 
     | 
    
         
             
                            done       = 0
         
     | 
| 
       77 
77 
     | 
    
         
             
                            iterations = (data.length.to_f / per_page).ceil
         
     | 
| 
         @@ -79,7 +79,7 @@ module Eco 
     | 
|
| 
       79 
79 
     | 
    
         
             
                            start_time = Time.now
         
     | 
| 
       80 
80 
     | 
    
         | 
| 
       81 
81 
     | 
    
         
             
                            data.each_slice(per_page) do |slice|
         
     | 
| 
       82 
     | 
    
         
            -
                              iteration 
     | 
| 
      
 82 
     | 
    
         
            +
                              iteration += 1
         
     | 
| 
       83 
83 
     | 
    
         | 
| 
       84 
84 
     | 
    
         
             
                              msg  = "starting batch '#{method}' iteration #{iteration}/#{iterations}, "
         
     | 
| 
       85 
85 
     | 
    
         
             
                              msg << "with #{slice.length} entries of #{data.length} -- #{done} done"
         
     | 
| 
         @@ -89,7 +89,7 @@ module Eco 
     | 
|
| 
       89 
89 
     | 
    
         
             
                              start_slice = Time.now
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
91 
     | 
    
         
             
                              offer_retry_on(*RETRY_ON, retries_left: TIMEOUT_RETRIES) do
         
     | 
| 
       92 
     | 
    
         
            -
                                people_api.batch(job_mode:  
     | 
| 
      
 92 
     | 
    
         
            +
                                people_api.batch(job_mode: as_job_mode) do |batch|
         
     | 
| 
       93 
93 
     | 
    
         
             
                                  slice.each do |person|
         
     | 
| 
       94 
94 
     | 
    
         
             
                                    batch.public_send(method, person) do |response|
         
     | 
| 
       95 
95 
     | 
    
         
             
                                      faltal("Request with no response") unless response
         
     | 
| 
         @@ -4,14 +4,14 @@ module Eco 
     | 
|
| 
       4 
4 
     | 
    
         
             
                  class Config
         
     | 
| 
       5 
5 
     | 
    
         
             
                    class Apis
         
     | 
| 
       6 
6 
     | 
    
         
             
                      module OneOff
         
     | 
| 
       7 
     | 
    
         
            -
                        private
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
7 
     | 
    
         
             
                        def one_off?
         
     | 
| 
       10 
     | 
    
         
            -
                          @is_one_off ||=
         
     | 
| 
      
 8 
     | 
    
         
            +
                          @is_one_off ||= # rubocop:disable Naming/MemoizedInstanceVariableName
         
     | 
| 
       11 
9 
     | 
    
         
             
                            SCR.get_arg('-api-key') ||
         
     | 
| 
       12 
10 
     | 
    
         
             
                            SCR.get_arg('-one-off')
         
     | 
| 
       13 
11 
     | 
    
         
             
                        end
         
     | 
| 
       14 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
                        private
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       15 
15 
     | 
    
         
             
                        def one_off_key
         
     | 
| 
       16 
16 
     | 
    
         
             
                          return @one_off_key if instance_variable_defined?(:@one_off_key)
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
         @@ -48,10 +48,10 @@ module Eco 
     | 
|
| 
       48 
48 
     | 
    
         
             
                          return @one_off_org if instance_variable_defined?(:@one_off_org)
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                          msg = "You should specify -org NAME when using -api-key or -one-off"
         
     | 
| 
       51 
     | 
    
         
            -
                          raise msg unless org = SCR.get_arg('-org', with_param: true)
         
     | 
| 
      
 51 
     | 
    
         
            +
                          raise msg unless (org = SCR.get_arg('-org', with_param: true))
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                          str_org = "#{org.downcase.split(/[^a-z]+/).join('_')}_#{one_off_enviro.gsub('.', '_')}"
         
     | 
| 
       54 
     | 
    
         
            -
                          @one_off_org 
     | 
| 
      
 54 
     | 
    
         
            +
                          @one_off_org ||= str_org.to_sym
         
     | 
| 
       55 
55 
     | 
    
         
             
                        end
         
     | 
| 
       56 
56 
     | 
    
         | 
| 
       57 
57 
     | 
    
         
             
                        def one_off_enviro
         
     | 
| 
         @@ -83,7 +83,7 @@ module Eco 
     | 
|
| 
       83 
83 
     | 
    
         | 
| 
       84 
84 
     | 
    
         
             
                          true
         
     | 
| 
       85 
85 
     | 
    
         
             
                        rescue StandardError => err
         
     | 
| 
       86 
     | 
    
         
            -
                          puts  
     | 
| 
      
 86 
     | 
    
         
            +
                          puts err.to_s
         
     | 
| 
       87 
87 
     | 
    
         
             
                          false
         
     | 
| 
       88 
88 
     | 
    
         
             
                        end
         
     | 
| 
       89 
89 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -141,7 +141,8 @@ module Eco 
     | 
|
| 
       141 
141 
     | 
    
         
             
                      # @yieldreturn [Eco::API::UseCases::BaseIO] the `io` input/output object carried througout all the _workflow_
         
     | 
| 
       142 
142 
     | 
    
         
             
                      # @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
         
     | 
| 
       143 
143 
     | 
    
         
             
                      def rescue(&block)
         
     | 
| 
       144 
     | 
    
         
            -
                        return @rescue unless  
     | 
| 
      
 144 
     | 
    
         
            +
                        return @rescue unless block_given?
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
       145 
146 
     | 
    
         
             
                        @rescue = block
         
     | 
| 
       146 
147 
     | 
    
         
             
                        self
         
     | 
| 
       147 
148 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -150,7 +151,8 @@ module Eco 
     | 
|
| 
       150 
151 
     | 
    
         | 
| 
       151 
152 
     | 
    
         
             
                      # Called on `SystemExit` exception
         
     | 
| 
       152 
153 
     | 
    
         
             
                      def exit_handle(&block)
         
     | 
| 
       153 
     | 
    
         
            -
                        return @exit_handle unless  
     | 
| 
      
 154 
     | 
    
         
            +
                        return @exit_handle unless block_given?
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
       154 
156 
     | 
    
         
             
                        @exit_handle = block
         
     | 
| 
       155 
157 
     | 
    
         
             
                        self
         
     | 
| 
       156 
158 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -171,6 +173,7 @@ module Eco 
     | 
|
| 
       171 
173 
     | 
    
         
             
                      # @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
         
     | 
| 
       172 
174 
     | 
    
         
             
                      def before(key = nil, &block)
         
     | 
| 
       173 
175 
     | 
    
         
             
                        raise ArgumentError, "A block should be given." unless block_given?
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
       174 
177 
     | 
    
         
             
                        if key
         
     | 
| 
       175 
178 
     | 
    
         
             
                          stage(key).before(&block)
         
     | 
| 
       176 
179 
     | 
    
         
             
                        else
         
     | 
| 
         @@ -195,6 +198,7 @@ module Eco 
     | 
|
| 
       195 
198 
     | 
    
         
             
                      # @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
         
     | 
| 
       196 
199 
     | 
    
         
             
                      def after(key = nil, &block)
         
     | 
| 
       197 
200 
     | 
    
         
             
                        raise ArgumentError, "A block should be given." unless block_given?
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
       198 
202 
     | 
    
         
             
                        if key
         
     | 
| 
       199 
203 
     | 
    
         
             
                          stage(key).after(&block)
         
     | 
| 
       200 
204 
     | 
    
         
             
                        else
         
     | 
| 
         @@ -267,6 +271,7 @@ module Eco 
     | 
|
| 
       267 
271 
     | 
    
         
             
                            io.evaluate(self, io, &c)
         
     | 
| 
       268 
272 
     | 
    
         
             
                          end
         
     | 
| 
       269 
273 
     | 
    
         
             
                        end
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
       270 
275 
     | 
    
         
             
                        io
         
     | 
| 
       271 
276 
     | 
    
         
             
                      end
         
     | 
| 
       272 
277 
     | 
    
         | 
| 
         @@ -276,6 +281,7 @@ module Eco 
     | 
|
| 
       276 
281 
     | 
    
         
             
                            io.evaluate(self, io, &c)
         
     | 
| 
       277 
282 
     | 
    
         
             
                          end
         
     | 
| 
       278 
283 
     | 
    
         
             
                        end
         
     | 
| 
      
 284 
     | 
    
         
            +
             
     | 
| 
       279 
285 
     | 
    
         
             
                        io
         
     | 
| 
       280 
286 
     | 
    
         
             
                      end
         
     | 
| 
       281 
287 
     | 
    
         | 
| 
         @@ -305,6 +311,7 @@ module Eco 
     | 
|
| 
       305 
311 
     | 
    
         
             
                            io.evaluate(self, io, &@on)
         
     | 
| 
       306 
312 
     | 
    
         
             
                          end
         
     | 
| 
       307 
313 
     | 
    
         
             
                        end
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
       308 
315 
     | 
    
         
             
                        io
         
     | 
| 
       309 
316 
     | 
    
         
             
                      ensure
         
     | 
| 
       310 
317 
     | 
    
         
             
                        @pending = false
         
     | 
| 
         @@ -341,7 +348,11 @@ module Eco 
     | 
|
| 
       341 
348 
     | 
    
         | 
| 
       342 
349 
     | 
    
         
             
                      def stage(key)
         
     | 
| 
       343 
350 
     | 
    
         
             
                        self.class.validate_stage(key)
         
     | 
| 
       344 
     | 
    
         
            -
                        @stages[key] ||= self.class.workflow_class(key).new( 
     | 
| 
      
 351 
     | 
    
         
            +
                        @stages[key] ||= self.class.workflow_class(key).new(
         
     | 
| 
      
 352 
     | 
    
         
            +
                          key,
         
     | 
| 
      
 353 
     | 
    
         
            +
                          _parent: self,
         
     | 
| 
      
 354 
     | 
    
         
            +
                          config:  config
         
     | 
| 
      
 355 
     | 
    
         
            +
                        )
         
     | 
| 
       345 
356 
     | 
    
         
             
                      end
         
     | 
| 
       346 
357 
     | 
    
         | 
| 
       347 
358 
     | 
    
         
             
                      # helper to treat trigger the exit and rescue handlers
         
     | 
| 
         @@ -354,6 +365,7 @@ module Eco 
     | 
|
| 
       354 
365 
     | 
    
         
             
                        io = io_result(io: io) do
         
     | 
| 
       355 
366 
     | 
    
         
             
                          io.evaluate(err, io, &exit_handle)
         
     | 
| 
       356 
367 
     | 
    
         
             
                        end
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
       357 
369 
     | 
    
         
             
                        exit err.status
         
     | 
| 
       358 
370 
     | 
    
         
             
                      rescue Interrupt => _int
         
     | 
| 
       359 
371 
     | 
    
         
             
                        raise
         
     | 
| 
         @@ -362,6 +374,7 @@ module Eco 
     | 
|
| 
       362 
374 
     | 
    
         
             
                        io = io_result(io: io) do
         
     | 
| 
       363 
375 
     | 
    
         
             
                          io.evaluate(err, io, &self.rescue)
         
     | 
| 
       364 
376 
     | 
    
         
             
                        end
         
     | 
| 
      
 377 
     | 
    
         
            +
             
     | 
| 
       365 
378 
     | 
    
         
             
                        raise
         
     | 
| 
       366 
379 
     | 
    
         
             
                      end
         
     | 
| 
       367 
380 
     | 
    
         
             
                    end
         
     | 
    
        data/lib/eco/api/session.rb
    CHANGED
    
    | 
         @@ -70,6 +70,7 @@ module Eco 
     | 
|
| 
       70 
70 
     | 
    
         
             
                  )
         
     | 
| 
       71 
71 
     | 
    
         
             
                    if live && api?(version: :graphql)
         
     | 
| 
       72 
72 
     | 
    
         
             
                      return live_tree(include_archived: include_archived, **kargs, &block) unless merge
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
       73 
74 
     | 
    
         
             
                      live_trees(include_archived: include_archived, **kargs, &block).inject(&:merge)
         
     | 
| 
       74 
75 
     | 
    
         
             
                    else
         
     | 
| 
       75 
76 
     | 
    
         
             
                      config.tagtree(recache: recache)
         
     | 
| 
         @@ -118,10 +119,12 @@ module Eco 
     | 
|
| 
       118 
119 
     | 
    
         
             
                  # @return [Eco::Data::Mapper] the mappings between the internal and external attribute/property names.
         
     | 
| 
       119 
120 
     | 
    
         
             
                  def fields_mapper
         
     | 
| 
       120 
121 
     | 
    
         
             
                    return @fields_mapper if instance_variable_defined?(:@fields_mapper)
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
       121 
123 
     | 
    
         
             
                    mappings = []
         
     | 
| 
       122 
124 
     | 
    
         
             
                    if (map_file = config.people.fields_mapper)
         
     | 
| 
       123 
125 
     | 
    
         
             
                      mappings = map_file ? file_manager.load_json(map_file) : []
         
     | 
| 
       124 
126 
     | 
    
         
             
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
       125 
128 
     | 
    
         
             
                    @fields_mapper = Eco::Data::Mapper.new(mappings)
         
     | 
| 
       126 
129 
     | 
    
         
             
                  end
         
     | 
| 
       127 
130 
     | 
    
         | 
| 
         @@ -132,7 +135,9 @@ module Eco 
     | 
|
| 
       132 
135 
     | 
    
         
             
                  #  If `schema` is `nil` or not provided it uses the currently associated to the `session`
         
     | 
| 
       133 
136 
     | 
    
         
             
                  def entry_factory(schema: nil)
         
     | 
| 
       134 
137 
     | 
    
         
             
                    schema = to_schema(schema) || self.schema
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
       135 
139 
     | 
    
         
             
                    return @entry_factories[schema&.id] if @entry_factories.key?(schema&.id)
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
       136 
141 
     | 
    
         
             
                    unless @entry_factories.empty?
         
     | 
| 
       137 
142 
     | 
    
         
             
                      @entry_factories[schema&.id] = @entry_factories.values.first.newFactory(schema: schema)
         
     | 
| 
       138 
143 
     | 
    
         
             
                      return @entry_factories[schema&.id]
         
     | 
| 
         @@ -164,9 +169,9 @@ module Eco 
     | 
|
| 
       164 
169 
     | 
    
         
             
                  # @param phase [Symbol] the phase when this parser should be active.
         
     | 
| 
       165 
170 
     | 
    
         
             
                  # @return [Object] the parsed attribute.
         
     | 
| 
       166 
171 
     | 
    
         
             
                  def parse_attribute(attr, source, phase = :internal, deps: {})
         
     | 
| 
       167 
     | 
    
         
            -
                     
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
      
 172 
     | 
    
         
            +
                    msg = "There are no parsers defined"
         
     | 
| 
      
 173 
     | 
    
         
            +
                    raise msg unless (parsers = entry_factory.person_parser)
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
       170 
175 
     | 
    
         
             
                    parsers.parse(attr, source, phase, deps: deps)
         
     | 
| 
       171 
176 
     | 
    
         
             
                  end
         
     | 
| 
       172 
177 
     | 
    
         | 
| 
         @@ -388,18 +393,19 @@ module Eco 
     | 
|
| 
       388 
393 
     | 
    
         | 
| 
       389 
394 
     | 
    
         
             
                  # from schema `id` or `name` to a PersonSchema object
         
     | 
| 
       390 
395 
     | 
    
         
             
                  def to_schema(value)
         
     | 
| 
       391 
     | 
    
         
            -
                    return nil unless value
         
     | 
| 
       392 
396 
     | 
    
         
             
                    sch = nil
         
     | 
| 
      
 397 
     | 
    
         
            +
                    return unless value
         
     | 
| 
      
 398 
     | 
    
         
            +
             
     | 
| 
       393 
399 
     | 
    
         
             
                    case value
         
     | 
| 
       394 
400 
     | 
    
         
             
                    when String
         
     | 
| 
       395 
     | 
    
         
            -
                       
     | 
| 
       396 
     | 
    
         
            -
             
     | 
| 
       397 
     | 
    
         
            -
                      end
         
     | 
| 
      
 401 
     | 
    
         
            +
                      msg = "The schema with id or name '#{value}' does not exist."
         
     | 
| 
      
 402 
     | 
    
         
            +
                      fatal msg unless (sch = schemas.schema(value))
         
     | 
| 
       398 
403 
     | 
    
         
             
                    when Ecoportal::API::V1::PersonSchema
         
     | 
| 
       399 
404 
     | 
    
         
             
                      sch = value
         
     | 
| 
       400 
405 
     | 
    
         
             
                    else
         
     | 
| 
       401 
406 
     | 
    
         
             
                      fatal "Required String or Ecoportal::API::V1::PersonSchema. Given: #{value}"
         
     | 
| 
       402 
407 
     | 
    
         
             
                    end
         
     | 
| 
      
 408 
     | 
    
         
            +
             
     | 
| 
       403 
409 
     | 
    
         
             
                    sch
         
     | 
| 
       404 
410 
     | 
    
         
             
                  end
         
     | 
| 
       405 
411 
     | 
    
         
             
                end
         
     | 
| 
         @@ -27,6 +27,7 @@ class Eco::API::UseCases::Default::Locations::TagtreeUpload < Eco::API::UseCases 
     | 
|
| 
       27 
27 
     | 
    
         
             
                    comms << insert_command(tree, pid: pid) unless top_id?(tree.id)
         
     | 
| 
       28 
28 
     | 
    
         
             
                    pid    = tree.id
         
     | 
| 
       29 
29 
     | 
    
         
             
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       30 
31 
     | 
    
         
             
                  tree.nodes.map do |node|
         
     | 
| 
       31 
32 
     | 
    
         
             
                    insert_commands(node, pid: pid)
         
     | 
| 
       32 
33 
     | 
    
         
             
                  end.flatten(1).tap do |subs|
         
     | 
| 
         @@ -54,6 +55,7 @@ class Eco::API::UseCases::Default::Locations::TagtreeUpload < Eco::API::UseCases 
     | 
|
| 
       54 
55 
     | 
    
         | 
| 
       55 
56 
     | 
    
         
             
              def top_id?(node_id = nil)
         
     | 
| 
       56 
57 
     | 
    
         
             
                return top_id.is_a?(String) if node_id.nil?
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       57 
59 
     | 
    
         
             
                node_id == top_id
         
     | 
| 
       58 
60 
     | 
    
         
             
              end
         
     | 
| 
       59 
61 
     | 
    
         | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::GroupCsv
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Cli < Eco::API::UseCases::Cli
         
     | 
| 
      
 3 
     | 
    
         
            +
                str_desc  = 'Groups the csv rows by a pivot field. '
         
     | 
| 
      
 4 
     | 
    
         
            +
                str_desc << 'It assumes the sorting field is sorted '
         
     | 
| 
      
 5 
     | 
    
         
            +
                str_desc << '(same values should be consecutive)'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                desc str_desc
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                callback do |_session, options, _usecase|
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if (file = SCR.get_file(cli_name, required: true, should_exist: true))
         
     | 
| 
      
 11 
     | 
    
         
            +
                    options.deep_merge!(input: {file: {name: file}})
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                add_option("-start-at", "Get only the last N-start_at rows") do |options|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  count = SCR.get_arg("-start-at", with_param: true)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  options.deep_merge!(input: {file: {start_at: count}})
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                add_option('-by', 'The column that should be used to group') do |options|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if (file = SCR.get_arg("-by", with_param: true))
         
     | 
| 
      
 22 
     | 
    
         
            +
                    options.deep_merge!(input: {group_by_field: file})
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,10 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::JsonToCsv
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Cli < Eco::API::UseCases::Cli
         
     | 
| 
      
 3 
     | 
    
         
            +
                desc "Transforms an input JSON file into a CSV one."
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                callback do |_sess, options, _case|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  file = SCR.get_file(cli_name, required: true, should_exist: true)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  options.deep_merge!(source: {file: file})
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::SortCsv
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Cli < Eco::API::UseCases::Cli
         
     | 
| 
      
 3 
     | 
    
         
            +
                desc 'Sorts the CSV by column -by'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                callback do |_session, options, _usecase|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if (file = SCR.get_file(cli_name, required: true, should_exist: true))
         
     | 
| 
      
 7 
     | 
    
         
            +
                    options.deep_merge!(input: {file: file})
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                add_option('-by', 'The column that should be used to sorting') do |options|
         
     | 
| 
      
 12 
     | 
    
         
            +
                  if (file = SCR.get_arg("-by", with_param: true))
         
     | 
| 
      
 13 
     | 
    
         
            +
                    options.deep_merge!(input: {sort_by: file})
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::SplitJson
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Cli < Eco::API::UseCases::Cli
         
     | 
| 
      
 3 
     | 
    
         
            +
                desc 'Splits a json input file into multiple files'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                callback do |_sess, options, _case|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  file = SCR.get_file(cli_name, required: true, should_exist: true)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  options.deep_merge!(source: {file: file})
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                add_option("-max-items", "The max count of items of the output files") do |options|
         
     | 
| 
      
 11 
     | 
    
         
            +
                  count = SCR.get_arg("-max-items", with_param: true)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  options.deep_merge!(output: {file: {max_items: count}})
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,213 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # This script assumes that for the `GROUP_BY_FIELD` rows are consecutive.
         
     | 
| 
      
 2 
     | 
    
         
            +
            # @note you might run first the `sort-csv` case.
         
     | 
| 
      
 3 
     | 
    
         
            +
            # @note you must inherit from this case and define the constants.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            #      GROUP_BY_FIELD = 'target_csv_field'.freeze
         
     | 
| 
      
 6 
     | 
    
         
            +
            #      GROUPED_FIELDS = [
         
     | 
| 
      
 7 
     | 
    
         
            +
            #        'joined_field_1',
         
     | 
| 
      
 8 
     | 
    
         
            +
            #        'joined_field_2',
         
     | 
| 
      
 9 
     | 
    
         
            +
            #        'joined_field_3',
         
     | 
| 
      
 10 
     | 
    
         
            +
            #      ].freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
            #
         
     | 
| 
      
 12 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::GroupCsv < Eco::API::Custom::UseCase
         
     | 
| 
      
 13 
     | 
    
         
            +
              name 'group-csv'
         
     | 
| 
      
 14 
     | 
    
         
            +
              type :other
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              require_relative 'cli/group_csv_cli'
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              def main(*_args)
         
     | 
| 
      
 19 
     | 
    
         
            +
                if simulate?
         
     | 
| 
      
 20 
     | 
    
         
            +
                  count = Eco::CSV.count(input_file)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  log(:info) { "CSV '#{input_file}' has #{count} rows." }
         
     | 
| 
      
 22 
     | 
    
         
            +
                else
         
     | 
| 
      
 23 
     | 
    
         
            +
                  generate_file
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              def generate_file # rubocop:disable Metrics/AbcSize
         
     | 
| 
      
 30 
     | 
    
         
            +
                row_count = 0
         
     | 
| 
      
 31 
     | 
    
         
            +
                in_index = nil
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                CSV.open(output_filename, 'wb') do |out_csv|
         
     | 
| 
      
 34 
     | 
    
         
            +
                  first = true
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  puts "\n"
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  streamed_input.for_each(start_at_idx: start_at) do |row, idx|
         
     | 
| 
      
 39 
     | 
    
         
            +
                    if first
         
     | 
| 
      
 40 
     | 
    
         
            +
                      first = false
         
     | 
| 
      
 41 
     | 
    
         
            +
                      headers!(row)
         
     | 
| 
      
 42 
     | 
    
         
            +
                      out_csv << headers
         
     | 
| 
      
 43 
     | 
    
         
            +
                      require_group_by_field!(row, file: input_file)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    in_index = idx
         
     | 
| 
      
 47 
     | 
    
         
            +
                    next unless !block_given? || yield(row, idx)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    next unless pivotable?(row, idx)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    next unless (last_group = pivot_row(row))
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    row_count += 1
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    if (row_count % 500).zero?
         
     | 
| 
      
 55 
     | 
    
         
            +
                      print "... Done #{row_count} rows          \r"
         
     | 
| 
      
 56 
     | 
    
         
            +
                      $stdout.flush
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    out_csv << last_group.values_at(*headers)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  # finalize
         
     | 
| 
      
 63 
     | 
    
         
            +
                  if (lrow = pivot_row)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    row_count += 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                    out_csv   << lrow.values_at(*headers)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 68 
     | 
    
         
            +
                  msg  = "Generated file '#{output_filename}' "
         
     | 
| 
      
 69 
     | 
    
         
            +
                  msg << "with #{row_count} rows (out of #{in_index})."
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  log(:info) { msg } unless simulate?
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
              # It tracks the current grouped row
         
     | 
| 
      
 76 
     | 
    
         
            +
              # @return [Nil, Hash] the last grouped row when `row` doesn't belong
         
     | 
| 
      
 77 
     | 
    
         
            +
              #   or `nil` otherwise
         
     | 
| 
      
 78 
     | 
    
         
            +
              def pivot_row(row = nil)
         
     | 
| 
      
 79 
     | 
    
         
            +
                @group ||= {}
         
     | 
| 
      
 80 
     | 
    
         
            +
                return @group unless row
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                pivot_value = row[group_by_field]
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                unless (last_pivot = @group[group_by_field])
         
     | 
| 
      
 85 
     | 
    
         
            +
                  last_pivot = @group[group_by_field] = pivot_value
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                last   = @group
         
     | 
| 
      
 89 
     | 
    
         
            +
                @group = {group_by_field => pivot_value} unless pivot_value == last_pivot
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                headers_rest.each do |field|
         
     | 
| 
      
 92 
     | 
    
         
            +
                  curr_values   = row[field].to_s.split('|').compact.uniq
         
     | 
| 
      
 93 
     | 
    
         
            +
                  pivot_values  = @group[field].to_s.split('|').compact.uniq
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @group[field] = (pivot_values | curr_values).join('|')
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                last unless last == @group
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              attr_reader :group
         
     | 
| 
      
 101 
     | 
    
         
            +
              attr_reader :headers, :headers_rest
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              def headers!(row)
         
     | 
| 
      
 104 
     | 
    
         
            +
                return if headers?
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                @headers_rest  = grouped_fields & row.headers
         
     | 
| 
      
 107 
     | 
    
         
            +
                @headers_rest -= [group_by_field]
         
     | 
| 
      
 108 
     | 
    
         
            +
                @headers       = [group_by_field, *headers_rest]
         
     | 
| 
      
 109 
     | 
    
         
            +
              end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
              def headers?
         
     | 
| 
      
 112 
     | 
    
         
            +
                instance_variable_defined?(:@headers)
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
              def pivotable?(row, idx)
         
     | 
| 
      
 116 
     | 
    
         
            +
                return true unless row[group_by_field].to_s.strip.empty?
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                msg  = "Row #{idx} doesn't have value for pivot field '#{group_by_field}'"
         
     | 
| 
      
 119 
     | 
    
         
            +
                msg << ". Skipping (discared) ..."
         
     | 
| 
      
 120 
     | 
    
         
            +
                log(:warn) { msg }
         
     | 
| 
      
 121 
     | 
    
         
            +
                false
         
     | 
| 
      
 122 
     | 
    
         
            +
              end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
              def streamed_input
         
     | 
| 
      
 125 
     | 
    
         
            +
                @streamed_input ||= Eco::CSV::Stream.new(input_file)
         
     | 
| 
      
 126 
     | 
    
         
            +
              end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
              def input_file
         
     | 
| 
      
 129 
     | 
    
         
            +
                options.dig(:input, :file, :name)
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
              def start_at
         
     | 
| 
      
 133 
     | 
    
         
            +
                return nil unless (num = options.dig(:input, :file, :start_at))
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                num = num.to_i
         
     | 
| 
      
 136 
     | 
    
         
            +
                num = nil if num.zero?
         
     | 
| 
      
 137 
     | 
    
         
            +
                num
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              def output_filename
         
     | 
| 
      
 141 
     | 
    
         
            +
                return nil unless input_name
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                File.join(input_dir, "#{input_name}_grouped#{input_ext}")
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
              def input_name
         
     | 
| 
      
 147 
     | 
    
         
            +
                @input_name ||= File.basename(input_basename, input_ext)
         
     | 
| 
      
 148 
     | 
    
         
            +
              end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
              def input_ext
         
     | 
| 
      
 151 
     | 
    
         
            +
                @input_ext ||= input_basename.split('.')[1..].join('.').then do |name|
         
     | 
| 
      
 152 
     | 
    
         
            +
                  ".#{name}"
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
              end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
              def input_basename
         
     | 
| 
      
 157 
     | 
    
         
            +
                @input_basename ||= File.basename(input_full_filename)
         
     | 
| 
      
 158 
     | 
    
         
            +
              end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              def input_dir
         
     | 
| 
      
 161 
     | 
    
         
            +
                @input_dir = File.dirname(input_full_filename)
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
              def input_full_filename
         
     | 
| 
      
 165 
     | 
    
         
            +
                @input_full_filename ||= File.expand_path(input_file)
         
     | 
| 
      
 166 
     | 
    
         
            +
              end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
              def require_group_by_field!(row, file:)
         
     | 
| 
      
 169 
     | 
    
         
            +
                return true if row.key?(group_by_field)
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                msg = "Pivot field '#{group_by_field}' missing in header of file '#{file}'"
         
     | 
| 
      
 172 
     | 
    
         
            +
                log(:error) { msg }
         
     | 
| 
      
 173 
     | 
    
         
            +
                raise msg
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
              def group_by_field
         
     | 
| 
      
 177 
     | 
    
         
            +
                return @group_by_field if instance_variable_defined?(:@group_by_field)
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                return (@group_by_field = opts_group_by) if opts_group_by
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                unless self.class.const_defined?(:GROUP_BY_FIELD)
         
     | 
| 
      
 182 
     | 
    
         
            +
                  msg = "(#{self.class}) You must define GROUP_BY_FIELD constant"
         
     | 
| 
      
 183 
     | 
    
         
            +
                  log(:error) { msg }
         
     | 
| 
      
 184 
     | 
    
         
            +
                  raise msg
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                @group_by_field = self.class::GROUP_BY_FIELD
         
     | 
| 
      
 188 
     | 
    
         
            +
              end
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
              def grouped_fields
         
     | 
| 
      
 191 
     | 
    
         
            +
                return @grouped_fields if instance_variable_defined?(:@grouped_fields)
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                unless self.class.const_defined?(:GROUPED_FIELDS)
         
     | 
| 
      
 194 
     | 
    
         
            +
                  msg = "(#{self.class}) You must define GROUPED_FIELDS constant"
         
     | 
| 
      
 195 
     | 
    
         
            +
                  log(:error) { msg }
         
     | 
| 
      
 196 
     | 
    
         
            +
                  raise msg
         
     | 
| 
      
 197 
     | 
    
         
            +
                end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                @grouped_fields ||= [self.class::GROUPED_FIELDS].flatten.compact.tap do |flds|
         
     | 
| 
      
 200 
     | 
    
         
            +
                  next unless flds.empty?
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                  log(:warn) {
         
     | 
| 
      
 203 
     | 
    
         
            +
                    msg  = "There were no fields to be grouped/joined. "
         
     | 
| 
      
 204 
     | 
    
         
            +
                    msg << "This is equivalent to launch a unique operation."
         
     | 
| 
      
 205 
     | 
    
         
            +
                    msg
         
     | 
| 
      
 206 
     | 
    
         
            +
                  }
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
              end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
              def opts_group_by
         
     | 
| 
      
 211 
     | 
    
         
            +
                options.dig(:input, :group_by_field)
         
     | 
| 
      
 212 
     | 
    
         
            +
              end
         
     | 
| 
      
 213 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Eco::API::UseCases::Default::Utils::JsonToCsv < Eco::API::Common::Loaders::UseCase
         
     | 
| 
      
 2 
     | 
    
         
            +
              require_relative 'cli/json_to_csv_cli'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              name 'json-to-csv'
         
     | 
| 
      
 5 
     | 
    
         
            +
              type :other
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def main(*_args)
         
     | 
| 
      
 8 
     | 
    
         
            +
                return if simulate?
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                CSV.open(out_filename, 'w') do |csv|
         
     | 
| 
      
 11 
     | 
    
         
            +
                  csv << all_keys
         
     | 
| 
      
 12 
     | 
    
         
            +
                  data.each do |item|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    csv << item.values_at(*all_keys)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 16 
     | 
    
         
            +
                  log(:info) {
         
     | 
| 
      
 17 
     | 
    
         
            +
                    "Generated output file: '#{File.expand_path(out_filename)}'."
         
     | 
| 
      
 18 
     | 
    
         
            +
                  }
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              private
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def all_keys
         
     | 
| 
      
 25 
     | 
    
         
            +
                @all_keys ||= data.each_with_object([]) do |item, head|
         
     | 
| 
      
 26 
     | 
    
         
            +
                  head.concat(item.keys - head)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def data
         
     | 
| 
      
 31 
     | 
    
         
            +
                @data ||= parse_json_file.tap do |dt|
         
     | 
| 
      
 32 
     | 
    
         
            +
                  ensure_array!(dt)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  log(:info) {
         
     | 
| 
      
 35 
     | 
    
         
            +
                    "Loaded #{dt.count} items (from file '#{File.basename(input_file)}')"
         
     | 
| 
      
 36 
     | 
    
         
            +
                  }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  exit 0 if simulate?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def out_filename
         
     | 
| 
      
 43 
     | 
    
         
            +
                @out_filename ||= ''.then do
         
     | 
| 
      
 44 
     | 
    
         
            +
                  input_basename = File.basename(input_file)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  base_name      = File.basename(input_basename, '.json')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  "#{base_name}.csv"
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
              def input_file
         
     | 
| 
      
 51 
     | 
    
         
            +
                options.dig(:source, :file)
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              def ensure_array!(data)
         
     | 
| 
      
 55 
     | 
    
         
            +
                return if data.is_a?(Array)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                msg = "Expecting JSON file to contain an Array. Given: #{data.class}"
         
     | 
| 
      
 58 
     | 
    
         
            +
                log(:error) { msg }
         
     | 
| 
      
 59 
     | 
    
         
            +
                raise msg
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def parse_json_file(filename = input_file)
         
     | 
| 
      
 63 
     | 
    
         
            +
                fd = File.open(filename)
         
     | 
| 
      
 64 
     | 
    
         
            +
                JSON.load fd # rubocop:disable Security/JSONLoad
         
     | 
| 
      
 65 
     | 
    
         
            +
              rescue JSON::ParserError => err
         
     | 
| 
      
 66 
     | 
    
         
            +
                log(:error) { "Parsing error on file '#{filename}'" }
         
     | 
| 
      
 67 
     | 
    
         
            +
                raise err
         
     | 
| 
      
 68 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 69 
     | 
    
         
            +
                fd&.close
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     |