eco-helpers 2.7.16 → 2.7.17
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/CHANGELOG.md +26 -4
- data/lib/eco/api/common/loaders/config/workflow/mailer.rb +10 -1
- data/lib/eco/api/microcases/people_cache.rb +3 -4
- data/lib/eco/api/microcases/people_load.rb +11 -8
- data/lib/eco/api/microcases/people_refresh.rb +8 -7
- data/lib/eco/api/microcases/people_search.rb +28 -22
- data/lib/eco/api/usecases/cli/dsl.rb +8 -6
- data/lib/eco/api/usecases/cli/option.rb +1 -0
- data/lib/eco/api/usecases/default/utils/cli/split_csv_cli.rb +5 -0
- data/lib/eco/api/usecases/default/utils/split_csv_case.rb +24 -4
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -12
- data/lib/eco/cli/config/filters.rb +13 -3
- data/lib/eco/cli/config/help.rb +11 -9
- data/lib/eco/cli/config/input.rb +6 -6
- data/lib/eco/cli/config/options_set.rb +34 -21
- data/lib/eco/cli/config/use_cases.rb +22 -6
- data/lib/eco/cli/config.rb +19 -11
- data/lib/eco/cli/scripting/args_helpers.rb +40 -25
- data/lib/eco/cli/scripting/arguments.rb +24 -3
- data/lib/eco/cli.rb +0 -1
- data/lib/eco/cli_default/workflow.rb +3 -2
- data/lib/eco/csv/split.rb +23 -10
- data/lib/eco/csv/stream.rb +1 -0
- data/lib/eco/csv.rb +28 -6
- data/lib/eco/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35f96561e2b58978c8949c744ac79c87888f067def10f63e00e3393e94cb992c
|
4
|
+
data.tar.gz: 7dba34b74428c53f7167eb1075b11bbd306432b3687c05986c66610da5b69f5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35223b9e52a315cf78b65393b509675a5ab515976e36535906b1a1e4588ccab178272ce209ab964b4f0365f74adccae9536db778edc5b524fa0cc70b327f0ccf
|
7
|
+
data.tar.gz: 0b356c4f3ca79f4ae57cae1db37f97455199c0772a165f294577bf7cfe3b4f1249a3c6cbee34c8903cb2d637a3b4b28658a3897680d61b2494aeffdd026efb8f
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
-
## [2.7.
|
5
|
+
## [2.7.18] - 2024-06-xx
|
6
6
|
|
7
7
|
### Added
|
8
8
|
|
@@ -10,14 +10,36 @@ All notable changes to this project will be documented in this file.
|
|
10
10
|
|
11
11
|
### Fixed
|
12
12
|
|
13
|
-
-
|
14
|
-
|
15
|
-
## [2.7.15] - 2024-06-18
|
13
|
+
## [2.7.17] - 2024-06-22
|
16
14
|
|
17
15
|
### Added
|
18
16
|
|
17
|
+
- **Stop on uknown** options will try to offer suggestions from now on
|
18
|
+
- `Eco::CSV::count` class method to stream-count the number of rows
|
19
|
+
- Option `-start-at` allows to start the count at certain row idx
|
20
|
+
|
19
21
|
### Changed
|
20
22
|
|
23
|
+
- Added options to case `-split-csv`
|
24
|
+
- `-start-at`
|
25
|
+
- `-simulate` a dry-run will return the count and won't generate the files.
|
26
|
+
- `Eco::CSV::split`
|
27
|
+
- the `block` allows to filter what rows should be included
|
28
|
+
|
29
|
+
### Fixed
|
30
|
+
|
31
|
+
- `Workflow::Mailer`: shouldn't send notification when there is an **error** that doesn't really need to be notified.
|
32
|
+
- `to-csv` case should accepte target folder as an argument
|
33
|
+
- `options_set` sort namespaces
|
34
|
+
|
35
|
+
## [2.7.16] - 2024-06-18
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
|
39
|
+
- `RegisterUpdateCase` filters
|
40
|
+
|
41
|
+
## [2.7.15] - 2024-06-18
|
42
|
+
|
21
43
|
### Fixed
|
22
44
|
|
23
45
|
- `Eco::API::Common::People::EntryFactor` super must be called before
|
@@ -17,7 +17,10 @@ class Eco::API::Common::Loaders::Workflow::Mailer < Eco::API::Common::Loaders::W
|
|
17
17
|
next unless session.mailer?
|
18
18
|
next if session.config.dry_run?
|
19
19
|
next unless session.config.run_mode_remote?
|
20
|
-
|
20
|
+
|
21
|
+
# temporary contingency
|
22
|
+
maybe_error_pages_or_tree_updates = other_case?(io) && error?
|
23
|
+
next unless some_update?(io) || maybe_error_pages_or_tree_updates
|
21
24
|
|
22
25
|
subject = base_subject
|
23
26
|
|
@@ -53,6 +56,12 @@ class Eco::API::Common::Loaders::Workflow::Mailer < Eco::API::Common::Loaders::W
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
def other_case?(io)
|
60
|
+
cli.config.usecases.active(io: io).any? do |usecase, _data|
|
61
|
+
%i[other].any? { |type| usecase.type == type }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
56
65
|
def error?
|
57
66
|
!!error
|
58
67
|
end
|
@@ -7,18 +7,17 @@ module Eco
|
|
7
7
|
def people_cache(filename = enviro.config.people.cache)
|
8
8
|
logger.info("Going to get all the people via API")
|
9
9
|
|
10
|
-
start
|
11
|
-
people
|
10
|
+
start = Time.now
|
11
|
+
people = session.batch.get_people
|
12
12
|
secs = (Time.now - start).round(3)
|
13
13
|
cnt = people.count
|
14
14
|
per_sec = (cnt.to_f / secs).round(2)
|
15
15
|
logger.info("Loaded #{cnt} people in #{secs} seconds (#{per_sec} people/sec)")
|
16
16
|
|
17
|
-
file
|
17
|
+
file = file_manager.save_json(people, filename, :timestamp)
|
18
18
|
logger.info("#{people.length} people loaded and saved locally to #{file}.")
|
19
19
|
Eco::API::Organization::People.new(people)
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -15,19 +15,23 @@ module Eco
|
|
15
15
|
# - `:file` if it is supposed to load people from a file.
|
16
16
|
# - `:save` if it is supposed to cache/save the data locally once obtained people from the server (`:api`)
|
17
17
|
# @return [Eco::API::Organization::People] the `People` object with the data.
|
18
|
-
def people_load(filename = enviro.config.people.cache, modifier: [
|
18
|
+
def people_load(filename = enviro.config.people.cache, modifier: %i[newest api]) # rubocop:disable Metrics/AbcSize
|
19
19
|
modifier = [modifier].flatten
|
20
|
-
load_file = [
|
20
|
+
load_file = %i[file newest].any? {|flag| modifier.include?(flag)}
|
21
|
+
|
21
22
|
case
|
22
23
|
when filename && load_file
|
23
|
-
|
24
|
+
file = people_load_filename(filename, newest: modifier.include?(:newest))
|
25
|
+
|
26
|
+
if file
|
24
27
|
file_manager.load_json(file).tap do |people|
|
25
28
|
logger.info("#{people&.length} people loaded from file #{file}") if people.is_a?(Array)
|
26
29
|
end
|
27
30
|
else
|
28
31
|
logger.error("could not find the file #{file_manager.dir.file(filename)}")
|
29
32
|
exit unless modifier.include?(:api)
|
30
|
-
|
33
|
+
|
34
|
+
people_load(modifier: modifier - %i[newest file])
|
31
35
|
end
|
32
36
|
when modifier.include?(:api)
|
33
37
|
logger.info("Going to get all the people via API (load)")
|
@@ -39,12 +43,12 @@ module Eco
|
|
39
43
|
per_sec = (cnt.to_f / secs).round(2)
|
40
44
|
logger.info("Loaded #{cnt} people in #{secs} seconds (#{per_sec} people/sec)")
|
41
45
|
|
42
|
-
if modifier.include?(:save) && people && people.length
|
46
|
+
if modifier.include?(:save) && people && people.length.positive?
|
43
47
|
file = file_manager.save_json(people, filename, :timestamp)
|
44
|
-
logger.info("#{people.length
|
48
|
+
logger.info("#{people.length} people saved to file #{file}.")
|
45
49
|
end
|
46
50
|
end
|
47
|
-
end.
|
51
|
+
end.then do |people|
|
48
52
|
Eco::API::Organization::People.new(people)
|
49
53
|
end
|
50
54
|
end
|
@@ -61,7 +65,6 @@ module Eco
|
|
61
65
|
file_manager.dir.file(filename, should_exist: true)
|
62
66
|
end
|
63
67
|
end
|
64
|
-
|
65
68
|
end
|
66
69
|
end
|
67
70
|
end
|
@@ -9,24 +9,26 @@ module Eco
|
|
9
9
|
# @param people [Eco::API::Organization::People] the people that needs refresh.
|
10
10
|
# @param include_created [Boolean] include people created during this session? (will check `:create` batch jobs).
|
11
11
|
# @return [Eco::API::Organization::People] the `People` object with the data.
|
12
|
-
def people_refresh(people:, include_created: true)
|
12
|
+
def people_refresh(people:, include_created: true) # rubocop:disable Metrics/AbcSize
|
13
13
|
people = people.newFrom people.select do |person|
|
14
14
|
!person.new? || !person.dirty?
|
15
15
|
end
|
16
|
+
|
16
17
|
ini = people.length
|
18
|
+
|
17
19
|
if include_created
|
18
20
|
session.job_groups.find_jobs(type: :create).map do |job|
|
19
|
-
to_add = job.people.
|
21
|
+
to_add = job.people.reject(&:dirty?)
|
20
22
|
people = people.merge(to_add)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
created = people.length - ini
|
25
|
-
msg
|
26
|
-
msg
|
27
|
+
msg = "Going to refresh #{people.length} people with server data"
|
28
|
+
msg += " (including #{created} that were created)" if created.positive?
|
27
29
|
logger.info(msg)
|
28
30
|
|
29
|
-
start
|
31
|
+
start = Time.now
|
30
32
|
entries = session.batch.get_people(people, silent: true)
|
31
33
|
secs = (Time.now - start).round(3)
|
32
34
|
cnt = entries.count
|
@@ -34,11 +36,10 @@ module Eco
|
|
34
36
|
logger.info("Re-loaded #{cnt} people (out of #{people.length}) in #{secs} seconds (#{per_sec} people/sec)")
|
35
37
|
|
36
38
|
missing = people.length - entries.length
|
37
|
-
logger.error("Missed to obtain #{missing} people during the refresh") if missing
|
39
|
+
logger.error("Missed to obtain #{missing} people during the refresh") if missing.positive?
|
38
40
|
|
39
41
|
Eco::API::Organization::People.new(entries)
|
40
42
|
end
|
41
|
-
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
@@ -5,52 +5,57 @@ module Eco
|
|
5
5
|
# @note
|
6
6
|
# - this helper is normally used to **get partial** part of the people manager.
|
7
7
|
# - therefore, normally used with _**delta** input files_ (files with only the differences).
|
8
|
-
# @param data [Eco::API::Organization::People, Enumerable<Person>, Enumerable<Hash>]
|
8
|
+
# @param data [Eco::API::Organization::People, Enumerable<Person>, Enumerable<Hash>]
|
9
|
+
# `People` to search against the server.
|
9
10
|
# @param options [Hash] the options.
|
10
11
|
# @param silent [Boolean] `false` if low level search messages should be shown.
|
11
12
|
# @return [Eco::API::Organization::People] the `People` object with the found persons.
|
12
|
-
def people_search(data, options: {}, silent: true)
|
13
|
+
def people_search(data, options: {}, silent: true) # rubocop:disable Metrics/AbcSize
|
13
14
|
session.logger.info("Going to api get #{data.length} entries...")
|
14
15
|
|
15
16
|
start = Time.now
|
16
|
-
people = session.batch.search(data, silent: silent).
|
17
|
-
secs
|
18
|
-
Eco::API::Organization::People.new(status.people).tap do |people|
|
17
|
+
people = session.batch.search(data, silent: silent).then do |status|
|
18
|
+
secs = (Time.now - start).round(3)
|
19
|
+
Eco::API::Organization::People.new(status.people).tap do |people| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
19
20
|
cnt = people.count
|
20
21
|
per_sec = (cnt.to_f / secs).round(2)
|
21
|
-
msg = "... could get #{cnt} people
|
22
|
+
msg = "... could get #{cnt} people "
|
23
|
+
msg << "(out of #{data.length} entries) in #{secs} seconds (#{per_sec} people/sec)"
|
22
24
|
session.logger.info(msg)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
# get the supervisors of found people (current supervisors)
|
27
29
|
supers = people_search_prepare_supers_request(people)
|
28
|
-
if supers.length
|
30
|
+
if supers.length.positive?
|
29
31
|
session.logger.info(" Going to api get #{supers.length} current supervisors...")
|
30
32
|
start = Time.now
|
31
|
-
people = session.batch.search(supers, silent: silent).
|
33
|
+
people = session.batch.search(supers, silent: silent).then do |status|
|
32
34
|
secs = (Time.now - start).round(3)
|
33
35
|
found = status.people
|
34
36
|
cnt = found.count
|
35
37
|
per_sec = (cnt.to_f / secs).round(2)
|
36
|
-
msg = "... could find #{cnt} current supers
|
38
|
+
msg = "... could find #{cnt} current supers "
|
39
|
+
msg << "(out of #{supers.length}) in #{secs} seconds (#{per_sec} people/sec)"
|
37
40
|
session.logger.info(msg)
|
41
|
+
|
38
42
|
people.merge(found, strict: micro.strict_search?(options))
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# get the supervisors referred in the input data (future supervisors)
|
43
47
|
supers = people_search_prepare_supers_request(data, people)
|
44
|
-
if supers.length
|
48
|
+
if supers.length.positive?
|
45
49
|
session.logger.info(" Going to api get #{supers.length} supervisors as per input entries...")
|
46
50
|
start = Time.now
|
47
51
|
|
48
|
-
people = session.batch.search(supers, silent: silent).
|
52
|
+
people = session.batch.search(supers, silent: silent).then do |status|
|
49
53
|
secs = (Time.now - start).round(3)
|
50
54
|
found = status.people
|
51
55
|
cnt = found.count
|
52
56
|
per_sec = (cnt.to_f / secs).round(2)
|
53
|
-
msg = "... could find #{cnt} input supers
|
57
|
+
msg = "... could find #{cnt} input supers "
|
58
|
+
msg << "(out of #{supers.length}) in #{secs} seconds (#{per_sec} people/sec)"
|
54
59
|
session.logger.info(msg)
|
55
60
|
people.merge(found, strict: micro.strict_search?(options))
|
56
61
|
end
|
@@ -66,25 +71,26 @@ module Eco
|
|
66
71
|
def people_search_prepare_supers_request(data, people = data)
|
67
72
|
data.each_with_object([]) do |entry, request|
|
68
73
|
spr = {"id" => (sup_id = people_search_super_id(entry))}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
next if !sup_id || request.include?(spr)
|
75
|
+
|
76
|
+
micro.with_supervisor(sup_id, people) do |supervisor|
|
77
|
+
request.push(spr) unless supervisor
|
73
78
|
end
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
77
82
|
# Gets the `supervisor_id` from `value`
|
78
83
|
def people_search_super_id(value)
|
79
|
-
sup_id =
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
sup_id =
|
85
|
+
if value.respond_to?(:supervisor_id)
|
86
|
+
value.supervisor_id
|
87
|
+
elsif value.is_a?(Hash) && value.key("supervisor_id")
|
88
|
+
value["supervisor_id"]
|
89
|
+
end
|
90
|
+
|
84
91
|
sup_id = nil if sup_id.to_s.strip.empty?
|
85
92
|
sup_id
|
86
93
|
end
|
87
|
-
|
88
94
|
end
|
89
95
|
end
|
90
96
|
end
|
@@ -7,9 +7,10 @@ module Eco
|
|
7
7
|
def apply!(arg_case = cli_name)
|
8
8
|
#puts "DEFINING CLI for '#{arg_case}' via #{self}"
|
9
9
|
if applied?(arg_case)
|
10
|
-
puts
|
10
|
+
puts "Warning: (#{self}) Tried to call again cli.apply! on '#{arg_case}'"
|
11
11
|
return self
|
12
12
|
end
|
13
|
+
|
13
14
|
cli_config_case(arg_case)
|
14
15
|
apply_options(arg_case)
|
15
16
|
applied!(arg_case)
|
@@ -29,10 +30,11 @@ module Eco
|
|
29
30
|
end
|
30
31
|
|
31
32
|
attr_writer :usecase
|
33
|
+
|
32
34
|
# Unless specified, assume Cli class hangs from its case namespace
|
33
35
|
def usecase
|
34
|
-
raise "#{self} is to use to extend a class" unless
|
35
|
-
@usecase ||= Kernel.const_get(
|
36
|
+
raise "#{self} is to use to extend a class" unless is_a?(Class)
|
37
|
+
@usecase ||= Kernel.const_get(to_s.split('::')[0..-2].join('::'))
|
36
38
|
end
|
37
39
|
|
38
40
|
def description(value = nil)
|
@@ -50,7 +52,7 @@ module Eco
|
|
50
52
|
|
51
53
|
# It defaults to the use case preceded by dash
|
52
54
|
def cli_name(arg_name = nil)
|
53
|
-
@cli_name = (arg_name.nil? ? @cli_name : arg_name).
|
55
|
+
@cli_name = (arg_name.nil? ? @cli_name : arg_name).then do |value|
|
54
56
|
value = "-#{name}" if value.nil?
|
55
57
|
value
|
56
58
|
end
|
@@ -66,7 +68,7 @@ module Eco
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def add_option(arg, desc = nil, &block)
|
69
|
-
|
71
|
+
tap do
|
70
72
|
"Overriding option '#{arg}' on case '#{name}'" if options.key?(arg)
|
71
73
|
@options[arg] = Eco::API::UseCases::Cli::Option.new(arg, desc, &block)
|
72
74
|
end
|
@@ -75,7 +77,7 @@ module Eco
|
|
75
77
|
private
|
76
78
|
|
77
79
|
def apply_options(arg_case)
|
78
|
-
options.
|
80
|
+
options.each_value do |option|
|
79
81
|
option.link_case(cli_config_case(arg_case))
|
80
82
|
end
|
81
83
|
end
|
@@ -13,6 +13,7 @@ class Eco::API::UseCases::Cli
|
|
13
13
|
|
14
14
|
def link_case(cli_config_case)
|
15
15
|
raise ArgumentError, "cli_config_case must have an 'add_option' method. Given: #{cli_config_case.class}" unless cli_config_case.respond_to?(:add_option)
|
16
|
+
|
16
17
|
cli_config_case.add_option(name, desc, &callback)
|
17
18
|
end
|
18
19
|
end
|
@@ -11,5 +11,10 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv
|
|
11
11
|
count = SCR.get_arg("-max-rows", with_param: true)
|
12
12
|
options.deep_merge!(output: {file: {max_rows: count}})
|
13
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!(output: {file: {start_at: count}})
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
@@ -7,15 +7,27 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv < Eco::API::Common::L
|
|
7
7
|
type :other
|
8
8
|
|
9
9
|
def main(*_args)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
if simulate?
|
11
|
+
count = Eco::CSV.count(input_file, start_at: start_at)
|
12
|
+
log(:info) { "CSV '#{input_file}' has #{count} rows." }
|
13
|
+
else
|
14
|
+
Eco::CSV.split(
|
15
|
+
input_file,
|
16
|
+
max_rows: max_rows,
|
17
|
+
start_at: start_at,
|
18
|
+
&filter
|
19
|
+
).each do |file|
|
20
|
+
log(:info) { "Generated file '#{file}'" }
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
16
24
|
|
17
25
|
private
|
18
26
|
|
27
|
+
def filter
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
19
31
|
def input_file
|
20
32
|
options.dig(:source, :file)
|
21
33
|
end
|
@@ -31,4 +43,12 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv < Eco::API::Common::L
|
|
31
43
|
num = nil if num.zero?
|
32
44
|
num
|
33
45
|
end
|
46
|
+
|
47
|
+
def start_at
|
48
|
+
return nil unless (num = options.dig(:output, :file, :start_at))
|
49
|
+
|
50
|
+
num = num.to_i
|
51
|
+
num = nil if num.zero?
|
52
|
+
num
|
53
|
+
end
|
34
54
|
end
|
@@ -2,6 +2,8 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
2
2
|
name "to-csv"
|
3
3
|
type :export
|
4
4
|
|
5
|
+
OUT_FILENAME = 'pm'
|
6
|
+
|
5
7
|
attr_reader :people
|
6
8
|
|
7
9
|
def main(people, _session, options, _usecase)
|
@@ -16,8 +18,9 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
16
18
|
if options.dig(:export, :options, :split_schemas)
|
17
19
|
by_schema.each do |id, people|
|
18
20
|
sch_name = schemas.to_name(id)
|
19
|
-
prefix
|
20
|
-
|
21
|
+
prefix = sch_name ? sch_name.gsub(" ", "_").downcase : "no_schema"
|
22
|
+
filename = in_folder("#{prefix}_#{File.basename(file)}")
|
23
|
+
create_file!(filename, people)
|
21
24
|
end
|
22
25
|
else
|
23
26
|
create_file!(file, people)
|
@@ -61,15 +64,13 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def nice_header_names(header, schema: nil)
|
64
|
-
schema
|
65
|
-
name_maps = schema.fields_by_alt_id.
|
66
|
-
mappings[alt_id] = fld.name
|
67
|
-
end.merge(nice_header_maps)
|
67
|
+
schema ||= session.schema
|
68
|
+
name_maps = schema.fields_by_alt_id.transform_values(&:name).merge(nice_header_maps)
|
68
69
|
header.map {|name| name_maps[name] || name}
|
69
70
|
end
|
70
71
|
|
71
72
|
def to_entry_type(person)
|
72
|
-
session.new_entry(person, dependencies: deps).
|
73
|
+
session.new_entry(person, dependencies: deps).then do |person_entry|
|
73
74
|
options.dig(:export, :options, :internal_names) ? person_entry.mapped_entry : person_entry.external_entry
|
74
75
|
end
|
75
76
|
end
|
@@ -79,14 +80,37 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def file
|
82
|
-
@file ||=
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
@file ||= out_filename.tap do |filename|
|
84
|
+
next if filename
|
85
|
+
|
86
|
+
log(:error) { "Destination file not specified" }
|
87
|
+
return false
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
91
|
+
def out_filename
|
92
|
+
return options_file unless options_folder?
|
93
|
+
|
94
|
+
File.join(options_file, "#{config.active_enviro}_#{OUT_FILENAME}.csv")
|
95
|
+
end
|
96
|
+
|
97
|
+
def in_folder(filename)
|
98
|
+
basename = File.basename(filename)
|
99
|
+
return basename unless options_folder?
|
100
|
+
|
101
|
+
File.join(options_file, basename)
|
102
|
+
end
|
103
|
+
|
104
|
+
def options_folder?
|
105
|
+
return false unless (value = options_file)
|
106
|
+
|
107
|
+
File.directory?(value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def options_file
|
111
|
+
options[:file] || options.dig(:export, :file, :name)
|
112
|
+
end
|
113
|
+
|
90
114
|
def by_schema
|
91
115
|
people.group_by do |person|
|
92
116
|
if (details = person.details)
|
@@ -15,7 +15,8 @@ module Eco
|
|
15
15
|
def help(msg = nil, refine: nil)
|
16
16
|
refinement = refine.is_a?(String)? " (containing: '#{refine}')" : ""
|
17
17
|
msg ||= "The following are the available filters#{refinement}:"
|
18
|
-
|
18
|
+
|
19
|
+
[msg].then do |lines|
|
19
20
|
max_len = keys_max_len(@filters.keys)
|
20
21
|
@filters.keys.sort.select do |key|
|
21
22
|
!refine.is_a?(String) || key.include?(refine)
|
@@ -26,10 +27,19 @@ module Eco
|
|
26
27
|
end.join("\n")
|
27
28
|
end
|
28
29
|
|
30
|
+
def available(keys: false)
|
31
|
+
return @filters.keys if keys
|
32
|
+
|
33
|
+
@filters.keys.map do |key|
|
34
|
+
[key, @filters[key], @description[key]]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
# @param option [String] the command line option that activates this filter.
|
30
39
|
# @param desc [String] description of the filter.
|
31
40
|
def add(option, desc = nil, &block)
|
32
|
-
raise "Missing block to define the filters builder" unless block_given?
|
41
|
+
raise ArgumentError, "Missing block to define the filters builder" unless block_given?
|
42
|
+
|
33
43
|
callback = block
|
34
44
|
[option].flatten.compact.each do |opt|
|
35
45
|
@filters[opt] = callback
|
@@ -38,7 +48,7 @@ module Eco
|
|
38
48
|
self
|
39
49
|
end
|
40
50
|
|
41
|
-
def process(
|
51
|
+
def process(*)
|
42
52
|
raise "You need to override this method in child classes"
|
43
53
|
end
|
44
54
|
end
|
data/lib/eco/cli/config/help.rb
CHANGED
@@ -2,8 +2,7 @@ module Eco
|
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
4
|
module Help
|
5
|
-
|
6
|
-
def help(*args)
|
5
|
+
def help(*_args)
|
7
6
|
raise "this needs to be reimplemented in the child class and return a string"
|
8
7
|
end
|
9
8
|
|
@@ -16,11 +15,12 @@ module Eco
|
|
16
15
|
# Creatas a well aligned line
|
17
16
|
def help_line(key, desc, keys_max_len = key.length, line_len = 100)
|
18
17
|
blanks = keys_max_len + 3 - key.length
|
19
|
-
blanks = blanks
|
18
|
+
blanks = [blanks, 0].max
|
20
19
|
top_line = " #{key}#{" "*blanks} "
|
21
20
|
indent = top_line.length
|
22
21
|
first = true
|
23
|
-
|
22
|
+
|
23
|
+
each_slice_words(desc, line_len - indent).each_with_object([]) do |line, lines| # rubocop:disable Style/RedundantEach
|
24
24
|
lines << (first ? "#{top_line}#{line}" : "#{" " * indent}#{line}")
|
25
25
|
first = false
|
26
26
|
end.join("\n")
|
@@ -33,14 +33,16 @@ module Eco
|
|
33
33
|
liner << part
|
34
34
|
else
|
35
35
|
yield(liner) if block_given?
|
36
|
-
|
36
|
+
|
37
|
+
out << liner
|
37
38
|
liner = part.strip
|
38
39
|
end
|
39
40
|
end.tap do |out|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
next unless out.empty? || !liner.empty?
|
42
|
+
|
43
|
+
yield(liner) if block_given?
|
44
|
+
|
45
|
+
out << liner if liner
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
data/lib/eco/cli/config/input.rb
CHANGED
@@ -2,14 +2,13 @@ module Eco
|
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
4
|
class Input
|
5
|
-
|
6
5
|
attr_reader :core_config
|
7
6
|
attr_reader :default_option
|
8
7
|
|
9
8
|
def initialize(core_config:, default_option: nil)
|
10
9
|
@core_config = core_config
|
11
10
|
@default_option = default_option
|
12
|
-
@callbacks
|
11
|
+
@callbacks = {}
|
13
12
|
end
|
14
13
|
|
15
14
|
def default_option?
|
@@ -20,16 +19,17 @@ module Eco
|
|
20
19
|
option ||= default_option
|
21
20
|
raise "Missing option to identify the input (no default defined)" unless !!option
|
22
21
|
raise "Missing block to define the input obtention process" unless block
|
22
|
+
|
23
23
|
@callbacks[option] = block
|
24
24
|
end
|
25
25
|
|
26
26
|
def get(io:, option: nil)
|
27
|
-
|
28
|
-
|
29
|
-
end
|
27
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
28
|
+
raise ArgumentError, msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
30
29
|
|
31
30
|
option ||= default_option
|
32
|
-
raise "Missing option to identify the input (no default defined)" unless
|
31
|
+
raise "Missing option to identify the input (no default defined)" unless option
|
32
|
+
|
33
33
|
callback = @callbacks[option] || @callbacks[default_option]
|
34
34
|
callback.call(io.session, option, io.options)
|
35
35
|
end
|
@@ -3,10 +3,10 @@ module Eco
|
|
3
3
|
class Config
|
4
4
|
class OptionsSet
|
5
5
|
include Eco::CLI::Config::Help
|
6
|
+
|
6
7
|
attr_reader :core_config
|
7
8
|
|
8
|
-
|
9
|
-
end
|
9
|
+
OptConfig = Struct.new(:name, :namespace, :description, :callback)
|
10
10
|
|
11
11
|
def initialize(core_config:)
|
12
12
|
@core_config = core_config
|
@@ -15,19 +15,21 @@ module Eco
|
|
15
15
|
|
16
16
|
# @return [String] summary of the options.
|
17
17
|
def help(refine: nil)
|
18
|
-
indent
|
19
|
-
spaces
|
18
|
+
indent = 2
|
19
|
+
spaces = any_non_general_space_active? ? active_namespaces : namespaces
|
20
20
|
refinement = refine.is_a?(String)? " (containing: '#{refine}')" : ""
|
21
|
-
|
21
|
+
|
22
|
+
["The following are the available options#{refinement}:"].then do |lines|
|
22
23
|
max_len = keys_max_len(options_args(spaces)) + indent
|
23
24
|
spaces.each do |namespace|
|
24
25
|
is_general = (namespace == :general)
|
25
26
|
str_indent = is_general ? "" : " " * indent
|
26
27
|
lines << help_line(namespace, "", max_len) unless is_general
|
27
|
-
|
28
|
+
|
29
|
+
options_set(namespace).select do |_arg, option| # rubocop:disable Style/HashEachMethods
|
28
30
|
!refine.is_a?(String) || option.name.include?(refine)
|
29
|
-
end.each do |
|
30
|
-
lines << help_line("
|
31
|
+
end.each do |_arg, option|
|
32
|
+
lines << help_line("#{str_indent}#{option.name}", option.description, max_len)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
lines
|
@@ -41,6 +43,19 @@ module Eco
|
|
41
43
|
end.uniq
|
42
44
|
end
|
43
45
|
|
46
|
+
# Options that are known for active namespaces (CLI-present)
|
47
|
+
def available(keys: false)
|
48
|
+
sets.select do |namespace, _opts_set|
|
49
|
+
active_namespace?(namespace)
|
50
|
+
end.each_value.with_object([]) do |opts_set, options|
|
51
|
+
options.concat(opts_set.values)
|
52
|
+
end.then do |options|
|
53
|
+
next options unless keys
|
54
|
+
|
55
|
+
options.map(&:name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
44
59
|
# @param option [String, Array<String>] the command line option(s).
|
45
60
|
# @param namespace [String] preceding command(s) argument that enables this option.
|
46
61
|
# @param desc [String] description of the option.
|
@@ -51,17 +66,18 @@ module Eco
|
|
51
66
|
unless opts.empty?
|
52
67
|
callback = block
|
53
68
|
opts.each do |opt|
|
54
|
-
|
69
|
+
msg = "Overriding CLI option '#{option}' in '#{namespace}' CLI case / namespace"
|
70
|
+
puts msg if option_exists?(opt, namespace)
|
55
71
|
options_set(namespace)[opt] = OptConfig.new(opt, namespace, desc, callback)
|
56
72
|
end
|
57
73
|
end
|
74
|
+
|
58
75
|
self
|
59
76
|
end
|
60
77
|
|
61
78
|
def process(io:)
|
62
|
-
|
63
|
-
|
64
|
-
end
|
79
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
80
|
+
raise ArgumentError, msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
65
81
|
|
66
82
|
active_options.each do |option|
|
67
83
|
option.callback.call(io.options, io.session)
|
@@ -70,8 +86,9 @@ module Eco
|
|
70
86
|
io.options
|
71
87
|
end
|
72
88
|
|
89
|
+
# Options that have been invoked via CLI
|
73
90
|
def active_options
|
74
|
-
@active_options ||= sets.select do |namespace,
|
91
|
+
@active_options ||= sets.select do |namespace, _opts_set|
|
75
92
|
active_namespace?(namespace)
|
76
93
|
end.each_with_object([]) do |(namespace, opts_set), options|
|
77
94
|
opts_set.each do |arg, option|
|
@@ -81,19 +98,20 @@ module Eco
|
|
81
98
|
end
|
82
99
|
|
83
100
|
def all_options
|
84
|
-
sets.each_with_object([]) do |(
|
101
|
+
sets.each_with_object([]) do |(_namespace, opts_set), options|
|
85
102
|
options << opts_set.values
|
86
103
|
end
|
87
104
|
end
|
88
105
|
|
89
106
|
def namespaces
|
90
107
|
sets.keys.sort_by do |key|
|
91
|
-
key == :general
|
108
|
+
next 1 if key == :general
|
109
|
+
0
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
95
113
|
def any_non_general_space_active?
|
96
|
-
(active_namespaces - [:general]).length
|
114
|
+
(active_namespaces - [:general]).length.positive?
|
97
115
|
end
|
98
116
|
|
99
117
|
def active_namespaces
|
@@ -104,7 +122,6 @@ module Eco
|
|
104
122
|
end
|
105
123
|
end
|
106
124
|
|
107
|
-
|
108
125
|
private
|
109
126
|
|
110
127
|
def active_namespace?(namespace)
|
@@ -132,10 +149,6 @@ module Eco
|
|
132
149
|
}
|
133
150
|
end
|
134
151
|
|
135
|
-
def namespaces
|
136
|
-
@sets.keys
|
137
|
-
end
|
138
|
-
|
139
152
|
def options_set(namespace = :general)
|
140
153
|
@sets[namespace] ||= {}
|
141
154
|
end
|
@@ -39,6 +39,15 @@ module Eco
|
|
39
39
|
end.join("\n")
|
40
40
|
end
|
41
41
|
|
42
|
+
# The linked cases
|
43
|
+
def available(keys: false)
|
44
|
+
@linked_cases.values.then do |cases|
|
45
|
+
next cases unless keys
|
46
|
+
|
47
|
+
cases.map(&:option)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
# Integrates a usecase to the command line.
|
43
52
|
# @param option_case [String] the command line option to invoke the usecase.
|
44
53
|
# @param type [Symbol] the type of usecase.
|
@@ -46,10 +55,12 @@ module Eco
|
|
46
55
|
# @param case_name [String, nil] the name of the usecase as defined.
|
47
56
|
def add(option_case, type, desc = nil, case_name: nil, &callback)
|
48
57
|
Eco::API::UseCases::UseCase.validate_type(type)
|
58
|
+
|
49
59
|
unless block_given?
|
50
60
|
raise "You must specify a valid 'case_name' when no block is provided" unless case_name
|
51
61
|
raise "'case_name' expected to be a String. Given: #{case_name.class}" unless case_name.is_a?(String)
|
52
62
|
end
|
63
|
+
|
53
64
|
puts "Overriding CLI case '#{option_case}'" if @linked_cases.key?(option_case)
|
54
65
|
@linked_cases[option_case] = CaseConfig.new(self, option_case, type, desc, case_name, callback)
|
55
66
|
end
|
@@ -60,6 +71,7 @@ module Eco
|
|
60
71
|
io.session.usecases.each do |usecase|
|
61
72
|
next unless usecase.respond_to?(:classed_definition)
|
62
73
|
next unless (original_case = usecase.classed_definition)
|
74
|
+
|
63
75
|
original_case.cli_apply!
|
64
76
|
end
|
65
77
|
end
|
@@ -84,9 +96,12 @@ module Eco
|
|
84
96
|
|
85
97
|
def process(io:)
|
86
98
|
validate_io!(io)
|
99
|
+
|
87
100
|
processed = false
|
101
|
+
|
88
102
|
active(io: io).each do |usecase, data|
|
89
103
|
raise "Something went wrong when scoping active cases" unless data
|
104
|
+
|
90
105
|
processed = true
|
91
106
|
io = case_io(io: io, usecase: usecase)
|
92
107
|
# some usecases have a callback to collect the parameters
|
@@ -101,6 +116,7 @@ module Eco
|
|
101
116
|
# Gets a `UseCaseIO`
|
102
117
|
def case_io(io:, usecase:)
|
103
118
|
validate_io!(io)
|
119
|
+
|
104
120
|
case io
|
105
121
|
when Eco::API::UseCases::UseCaseIO
|
106
122
|
io.chain(usecase: usecase)
|
@@ -122,12 +138,12 @@ module Eco
|
|
122
138
|
params = io.params(keyed: true).merge(type: data.type)
|
123
139
|
io = io.new(**params, validate: false)
|
124
140
|
callback.call(*io.params).tap do |use|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
141
|
+
next if use.is_a?(Eco::API::UseCases::UseCase)
|
142
|
+
|
143
|
+
msg = "When adding a usecase, without specifying 'case_name:', "
|
144
|
+
msg << "the block that integrates usecase for cli option '#{data.option}'"
|
145
|
+
msg << "must return an Eco::API::UseCases::UseCase object. It returns #{use.class}"
|
146
|
+
raise msg
|
131
147
|
end
|
132
148
|
end
|
133
149
|
end
|
data/lib/eco/cli/config.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Eco
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
|
-
|
5
4
|
attr_reader :cli
|
6
5
|
|
7
6
|
def initialize(cli:)
|
@@ -16,6 +15,16 @@ module Eco
|
|
16
15
|
cli.args
|
17
16
|
end
|
18
17
|
|
18
|
+
# All the available known option args that can be used in the CLI
|
19
|
+
def available_option_args
|
20
|
+
[
|
21
|
+
usecases.available(keys: true),
|
22
|
+
options_set.available(keys: true),
|
23
|
+
people_filters.available(keys: true),
|
24
|
+
input_filters.available(keys: true)
|
25
|
+
].flatten.uniq
|
26
|
+
end
|
27
|
+
|
19
28
|
def options_set
|
20
29
|
@opions_set ||= Eco::CLI::Config::OptionsSet.new(core_config: self)
|
21
30
|
@opions_set.tap do |opts_set|
|
@@ -25,12 +34,10 @@ module Eco
|
|
25
34
|
|
26
35
|
def input(default_option: nil, &block)
|
27
36
|
@input ||= Eco::CLI::Config::Input.new(core_config: self, default_option: default_option)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@input
|
33
|
-
end
|
37
|
+
return @input unless block_given?
|
38
|
+
|
39
|
+
@input.define(&block)
|
40
|
+
self
|
34
41
|
end
|
35
42
|
|
36
43
|
def people(io: nil, &block)
|
@@ -38,10 +45,11 @@ module Eco
|
|
38
45
|
@people_load = block
|
39
46
|
self
|
40
47
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
48
|
+
msg = "There is no definition on how to load people"
|
49
|
+
raise msg unless instance_variable_defined?(:@people_load) && @people_load
|
50
|
+
|
51
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
52
|
+
raise msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
45
53
|
|
46
54
|
io = io.new(type: :import, input: io.input || [])
|
47
55
|
@people_load.call(*io.params)
|
@@ -7,7 +7,7 @@ module Eco
|
|
7
7
|
@argv || ARGV
|
8
8
|
end
|
9
9
|
|
10
|
-
def is_modifier?(value)
|
10
|
+
def is_modifier?(value) # rubocop:disable Naming/PredicateName
|
11
11
|
Argument.is_modifier?(value)
|
12
12
|
end
|
13
13
|
|
@@ -21,22 +21,34 @@ module Eco
|
|
21
21
|
arguments.add(key, with_param: with_param)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
24
|
# Validation to stop the `script` if among `argv` there's any **unknown** argument.
|
26
|
-
def stop_on_unknown!(exclude: [], only_options: false)
|
25
|
+
def stop_on_unknown!(exclude: [], only_options: false, all_available: nil)
|
27
26
|
# validate only those that are options
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
suggestions = {}
|
28
|
+
args = {
|
29
|
+
exclude: exclude,
|
30
|
+
all_available: all_available
|
31
|
+
}
|
32
|
+
unknown = arguments.unknown(**args) do |key, correct|
|
33
|
+
suggestions[key] = correct unless correct.empty?
|
31
34
|
end
|
35
|
+
unknown = unknown.select {|arg| is_modifier?(arg)} if only_options
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
return if unknown.empty?
|
38
|
+
|
39
|
+
suggestions_str = suggestions.slice(*unknown).map do |key, correct|
|
40
|
+
str = "Unknown option '#{key}'."
|
41
|
+
str_corr = correct.map {|key| "'#{key}'"}
|
42
|
+
str << " Did you mean: #{str_corr.join(', ')}" unless correct.empty?
|
43
|
+
str
|
44
|
+
end.join("\n * ")
|
45
|
+
|
46
|
+
msg = "\nThere are UNKNOWN OPTIONS in your command line arguments !!"
|
47
|
+
msg << "\n * #{suggestions_str}\n"
|
48
|
+
msg << "\nUse 'ruby main.rb -org [-usecase] --help -options' for more information"
|
49
|
+
msg << "\n - Please, remember that use case specific options "
|
50
|
+
msg << "should come after the use case in the command line.\n"
|
51
|
+
raise msg
|
40
52
|
end
|
41
53
|
|
42
54
|
# @return [Boolean] if `key` is in the command line.
|
@@ -46,28 +58,31 @@ module Eco
|
|
46
58
|
|
47
59
|
# @return [Integer, nil] the position of `key` in the command line.
|
48
60
|
def get_arg_index(key)
|
49
|
-
return
|
61
|
+
return unless arg?(key)
|
62
|
+
|
50
63
|
argv.index(key)
|
51
64
|
end
|
52
65
|
|
53
66
|
# @return [Boolean] if `key1` precedes `key2` in the command line.
|
54
|
-
def arg_order?(
|
55
|
-
|
56
|
-
|
67
|
+
def arg_order?(key_1, key_2)
|
68
|
+
k_1 = get_arg_index(key_1)
|
69
|
+
k_2 = get_arg_index(key_2)
|
70
|
+
return false unless k_1 && k_2
|
71
|
+
|
72
|
+
k_1 < k_2
|
57
73
|
end
|
58
74
|
|
59
75
|
# @return [String, Boolean] the argument value if `with_param` or a `Boolean` if not.
|
60
76
|
def get_arg(key, with_param: false, valid: true)
|
61
77
|
# track what a known option looks like
|
62
78
|
known_argument(key, with_param: with_param)
|
63
|
-
return nil
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
return value
|
79
|
+
return nil unless (index = get_arg_index(key))
|
80
|
+
return true unless with_param
|
81
|
+
|
82
|
+
value = argv[index + 1]
|
83
|
+
#puts "modifier argument: #{value}"
|
84
|
+
value = nil if valid && is_modifier?(value)
|
85
|
+
value
|
71
86
|
end
|
72
87
|
|
73
88
|
# @return [String] the filename.
|
@@ -13,6 +13,7 @@ module Eco
|
|
13
13
|
|
14
14
|
def each(&block)
|
15
15
|
return to_enum(:each) unless block
|
16
|
+
|
16
17
|
@known.values.each(&block)
|
17
18
|
end
|
18
19
|
|
@@ -22,7 +23,8 @@ module Eco
|
|
22
23
|
|
23
24
|
def <<(arg)
|
24
25
|
raise "Expected Argument. Given #{arg.class}" unless arg.is_a?(Argument)
|
25
|
-
|
26
|
+
|
27
|
+
if (karg = @known[arg.key])
|
26
28
|
#puts "Found already existent option #{arg.key} (with_param: arg.with_param?)"
|
27
29
|
karg.with_param! if arg.with_param?
|
28
30
|
else
|
@@ -40,14 +42,33 @@ module Eco
|
|
40
42
|
@known.keys
|
41
43
|
end
|
42
44
|
|
43
|
-
def unknown(exclude: [])
|
45
|
+
def unknown(exclude: [], all_available: nil)
|
44
46
|
reduce(args.dup - exclude) do |not_known, arg|
|
45
47
|
arg.args_slice(*not_known)
|
48
|
+
end.compact.tap do |list|
|
49
|
+
list.each do |key|
|
50
|
+
next unless block_given?
|
51
|
+
|
52
|
+
yield(key, unknown_suggestions(key, all_available: all_available))
|
53
|
+
end
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
57
|
+
def unknown_suggestions(str, all_available: nil)
|
58
|
+
return [] if str.nil?
|
59
|
+
|
60
|
+
str = str.to_s.strip
|
61
|
+
return [] if str.empty?
|
62
|
+
|
63
|
+
all_available ||= @known.keys
|
64
|
+
spell_checker = DidYouMean::SpellChecker.new(
|
65
|
+
dictionary: all_available
|
66
|
+
)
|
67
|
+
spell_checker.correct(str)
|
68
|
+
end
|
69
|
+
|
49
70
|
def any_unknown?(exclude: [])
|
50
|
-
unknown(exclude: exclude).length
|
71
|
+
unknown(exclude: exclude).length.positive?
|
51
72
|
end
|
52
73
|
|
53
74
|
private
|
data/lib/eco/cli.rb
CHANGED
@@ -74,8 +74,9 @@ ASSETS.cli do |cli| # rubocop:disable Metrics/BlockLength
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
wf.before(:launch_jobs) do
|
78
|
-
|
77
|
+
wf.before(:launch_jobs) do |_wf_jobs, io|
|
78
|
+
available_args = cli.config.available_option_args
|
79
|
+
SCR.stop_on_unknown!(all_available: available_args)
|
79
80
|
end
|
80
81
|
|
81
82
|
wf.on(:launch_jobs) do
|
data/lib/eco/csv/split.rb
CHANGED
@@ -5,10 +5,13 @@ module Eco
|
|
5
5
|
|
6
6
|
attr_reader :filename
|
7
7
|
|
8
|
-
def initialize(filename, max_rows:, **kargs)
|
9
|
-
|
8
|
+
def initialize(filename, max_rows:, start_at: nil, **kargs)
|
9
|
+
msg = "File '#{filename}' does not exist"
|
10
|
+
raise ArgumentError, msg unless ::File.exist?(filename)
|
11
|
+
|
10
12
|
@filename = filename
|
11
13
|
@max_rows = max_rows
|
14
|
+
@start_at = start_at
|
12
15
|
@params = kargs
|
13
16
|
init
|
14
17
|
end
|
@@ -20,7 +23,7 @@ module Eco
|
|
20
23
|
# - If `nil` it will create its own filename convention
|
21
24
|
# @return [Array<String>] names of the generated files
|
22
25
|
def call(&block)
|
23
|
-
stream.for_each do |row, ridx|
|
26
|
+
stream.for_each(start_at_idx: start_at) do |row, ridx|
|
24
27
|
copy_row(row, ridx, &block)
|
25
28
|
end
|
26
29
|
out_files
|
@@ -32,7 +35,7 @@ module Eco
|
|
32
35
|
private
|
33
36
|
|
34
37
|
attr_reader :params
|
35
|
-
attr_reader :idx, :max_rows
|
38
|
+
attr_reader :idx, :max_rows, :start_at
|
36
39
|
attr_reader :headers, :row_idx
|
37
40
|
|
38
41
|
attr_accessor :exception
|
@@ -40,19 +43,29 @@ module Eco
|
|
40
43
|
def copy_row(row, ridx, &block)
|
41
44
|
@headers ||= row.headers
|
42
45
|
@row_idx = ridx
|
43
|
-
|
46
|
+
|
47
|
+
current_csv(ridx) do |csv, fidx, file_out|
|
48
|
+
included = true
|
49
|
+
included &&= !block || yield(row, ridx, fidx, file_out)
|
50
|
+
next unless included
|
51
|
+
|
52
|
+
csv << row.fields
|
53
|
+
end
|
44
54
|
end
|
45
55
|
|
46
56
|
def current_csv(ridx)
|
47
57
|
if split?(ridx) || @csv.nil?
|
48
58
|
puts "Split at row #{row_idx}"
|
49
59
|
@csv&.close
|
50
|
-
|
51
|
-
out_filename =
|
52
|
-
@csv
|
53
|
-
@csv
|
54
|
-
out_files
|
60
|
+
|
61
|
+
out_filename = generate_name(next_idx)
|
62
|
+
@csv = ::CSV.open(out_filename, "w")
|
63
|
+
@csv << headers
|
64
|
+
out_files << out_filename
|
55
65
|
end
|
66
|
+
|
67
|
+
yield(@csv, idx, out_files.last) if block_given?
|
68
|
+
|
56
69
|
@csv
|
57
70
|
end
|
58
71
|
|
data/lib/eco/csv/stream.rb
CHANGED
data/lib/eco/csv.rb
CHANGED
@@ -18,16 +18,38 @@ module Eco
|
|
18
18
|
parse(get_file_content(file, **params), **kargs)
|
19
19
|
end
|
20
20
|
|
21
|
-
#
|
22
|
-
# @
|
21
|
+
# Splits the csv `filename` into `max_rows`
|
22
|
+
# @yield [row, ridx, fidx, file]
|
23
|
+
# @yieldparam row [Integer] the row
|
24
|
+
# @yieldparam ridx [Integer] the index of the row in the source file
|
25
|
+
# @yieldparam fidx [Integer] the number of the file
|
23
26
|
# @yieldparam file [String] the default name of the file
|
24
|
-
# @yieldreturn [
|
25
|
-
# - If `nil` it will create its own filename convention
|
27
|
+
# @yieldreturn [Bollean] whether the row should be included
|
26
28
|
# @param filename [String] the orignal file
|
27
29
|
# @param max_rows [Integer] number of rows per file
|
30
|
+
# @param start_at [Integer] row that sets the starting point.
|
31
|
+
# Leave empty for the full set of rows.
|
28
32
|
# @see Eco::CSV::Split#call
|
29
|
-
def split(filename, max_rows:, **kargs, &block)
|
30
|
-
Eco::CSV::Split.new(
|
33
|
+
def split(filename, max_rows:, start_at: nil, **kargs, &block)
|
34
|
+
Eco::CSV::Split.new(
|
35
|
+
filename,
|
36
|
+
max_rows: max_rows,
|
37
|
+
start_at: start_at,
|
38
|
+
**kargs
|
39
|
+
).call(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @note it excludes headers by default
|
43
|
+
# @return [Integer] the number of rows of the file
|
44
|
+
def count(filename, start_at: nil, **kargs)
|
45
|
+
count = 0
|
46
|
+
streamer = Eco::CSV::Stream.new(filename, **kargs)
|
47
|
+
streamer.for_each(start_at_idx: start_at) do |row|
|
48
|
+
included = true
|
49
|
+
included = yield(row) if block_given?
|
50
|
+
count += 1 if included
|
51
|
+
end
|
52
|
+
count
|
31
53
|
end
|
32
54
|
end
|
33
55
|
end
|
data/lib/eco/version.rb
CHANGED