eco-helpers 3.0.16 → 3.0.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 +11 -2
- data/eco-helpers.gemspec +1 -1
- data/lib/eco/api/session/batch/launcher/benchmarking.rb +23 -0
- data/lib/eco/api/session/batch/launcher/mode.rb +23 -0
- data/lib/eco/api/session/batch/launcher/options.rb +21 -0
- data/lib/eco/api/session/batch/launcher/retry.rb +40 -0
- data/lib/eco/api/session/batch/launcher/size.rb +40 -0
- data/lib/eco/api/session/batch/launcher/status_handling.rb +23 -0
- data/lib/eco/api/session/batch/launcher/valid_methods.rb +34 -0
- data/lib/eco/api/session/batch/launcher.rb +141 -0
- data/lib/eco/api/session/batch/request_stats.rb +92 -74
- data/lib/eco/api/session/batch/searcher.rb +108 -0
- data/lib/eco/api/session/batch.rb +6 -263
- data/lib/eco/version.rb +1 -1
- metadata +13 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 884bc5e19544453088719de4fb1445b7fdb38de4b14b0a70d2dbe795d7082fb6
|
|
4
|
+
data.tar.gz: 0ba7fa5b2367d5fe51e7a51571c5e031961ddb94ec863fa71a6ab2869aa7a353
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 75eddb8557bf95b1174a2de8d18922aeac5bb3fb74fa8371c58d8c74be0413d7eef12a682549efe4a996c5413bc245e75dc2a6b06d9615aee520ab3be3247abd
|
|
7
|
+
data.tar.gz: 898c76f69fd73fd89c261ff4989798c2dec9d845ce8b4aadb168ac1c44c0b7c67cd0d6a1aabead337ede761ef37dcf7d850e895057d04af211c8f19a6708cf94
|
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
|
-
## [3.0.
|
|
5
|
+
## [3.0.18] - 2024-10-xx
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
@@ -10,10 +10,19 @@ All notable changes to this project will be documented in this file.
|
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
-
## [3.0.
|
|
13
|
+
## [3.0.17] - 2024-10-18
|
|
14
14
|
|
|
15
15
|
### Added
|
|
16
16
|
|
|
17
|
+
- Configurability around the **size** of batch and job modes
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `ecoportal-api` gem upgrade
|
|
22
|
+
- Internal refactor of `Eco::API::Session::Batch`
|
|
23
|
+
|
|
24
|
+
## [3.0.16] - 2024-10-15
|
|
25
|
+
|
|
17
26
|
### Changed
|
|
18
27
|
|
|
19
28
|
- `Eco::API::Session::Batch#launch_batch`
|
data/eco-helpers.gemspec
CHANGED
|
@@ -40,7 +40,7 @@ Gem::Specification.new do |spec|
|
|
|
40
40
|
spec.add_dependency 'bcrypt_pbkdf', '~> 1.0'
|
|
41
41
|
spec.add_dependency 'docx', '>= 0.8.0', '< 0.9'
|
|
42
42
|
spec.add_dependency 'dotenv', '~> 3'
|
|
43
|
-
spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.
|
|
43
|
+
spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.5'
|
|
44
44
|
spec.add_dependency 'ecoportal-api-graphql', '~> 0.4', '>= 0.4.2'
|
|
45
45
|
spec.add_dependency 'ecoportal-api-v2', '~> 2.0', '>= 2.0.10'
|
|
46
46
|
spec.add_dependency 'ed25519', '~> 1.2'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module Benchmarking
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def str_per_sec(start, count)
|
|
10
|
+
now = Time.now
|
|
11
|
+
secs = (now - start).round(2)
|
|
12
|
+
|
|
13
|
+
return ' -- ' unless secs > 0.0
|
|
14
|
+
|
|
15
|
+
per_sec = (count.to_f / secs).round(2)
|
|
16
|
+
"#{secs}s -> #{per_sec} people/s"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module Mode
|
|
7
|
+
include Eco::API::Session::Batch::Launcher::Options
|
|
8
|
+
|
|
9
|
+
# @return [Symbol] the batch mode to run
|
|
10
|
+
def batch_mode(opts = options)
|
|
11
|
+
opts.dig(:workflow, :batch, :mode) || :batch
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @return [Boolean] are we running in `:job` mode?
|
|
15
|
+
def job_mode?(opts = options)
|
|
16
|
+
batch_mode(opts) == :job
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module Options
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Default way to retrieve options (unless provided)
|
|
10
|
+
def options
|
|
11
|
+
return @options if instance_variable_defined?(:@options)
|
|
12
|
+
return super if defined?(super)
|
|
13
|
+
|
|
14
|
+
ASSETS.cli.options
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module Retry
|
|
7
|
+
def self.included(base)
|
|
8
|
+
unless base <= Eco::API::Session::Batch::Launcher
|
|
9
|
+
msg = "To be included only in Eco::API::Common::Session::BaseSession. "
|
|
10
|
+
msg << "Tried on '#{base}'"
|
|
11
|
+
raise msg
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def offer_retry_on(error_type, retries_left = 3, &block)
|
|
20
|
+
yield
|
|
21
|
+
rescue error_type => err
|
|
22
|
+
raise err.class, err.message, cause: nil unless retries_left.positive?
|
|
23
|
+
|
|
24
|
+
explanation = "#{err}\n"
|
|
25
|
+
explanation << "You have #{retries_left} retries left."
|
|
26
|
+
question = " Do you want to retry (y/N)?"
|
|
27
|
+
|
|
28
|
+
prompt_user(question, default: "Y", explanation: explanation, timeout: 10) do |response|
|
|
29
|
+
raise unless response.upcase.start_with?("Y")
|
|
30
|
+
|
|
31
|
+
puts "\nOkay... let's retry!"
|
|
32
|
+
offer_retry_on(error_type, retries_left - 1, &block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module Size
|
|
7
|
+
include Eco::API::Session::Batch::Launcher::Mode
|
|
8
|
+
|
|
9
|
+
DEFAULT_BATCH_SIZE = 50
|
|
10
|
+
DEFAULT_JOB_SIZE = 100
|
|
11
|
+
|
|
12
|
+
def batch_size(opts = options)
|
|
13
|
+
return job_mode_size if job_mode?(opts)
|
|
14
|
+
|
|
15
|
+
batch_mode_size
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def job_mode_size
|
|
21
|
+
options.dig(:workflow, :batch, :job, :size).then do |size|
|
|
22
|
+
next self.class::DEFAULT_JOB_SIZE unless size
|
|
23
|
+
|
|
24
|
+
size
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def batch_mode_size
|
|
29
|
+
options.dig(:workflow, :batch, :size).then do |size|
|
|
30
|
+
next self.class::DEFAULT_BATCH_SIZE unless size
|
|
31
|
+
|
|
32
|
+
[size, 100].min
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module StatusHandling
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def tap_status(enviro:, queue:, method:, status: nil, &block)
|
|
10
|
+
status ||= Eco::API::Session::Batch::Status.new(
|
|
11
|
+
enviro,
|
|
12
|
+
queue: queue,
|
|
13
|
+
method: method
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
status.tap(&block)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Eco
|
|
2
|
+
module API
|
|
3
|
+
class Session
|
|
4
|
+
class Batch
|
|
5
|
+
module Launcher
|
|
6
|
+
module ValidMethods
|
|
7
|
+
VALID_METHODS = %i[get create update upsert delete].freeze
|
|
8
|
+
|
|
9
|
+
def self.included(base)
|
|
10
|
+
super
|
|
11
|
+
base.extend(ClassMethods)
|
|
12
|
+
base.send(:include, InstanceMethods)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
# @return [Boolean] `true` if the method is supported, `false` otherwise.
|
|
17
|
+
def valid_method?(value)
|
|
18
|
+
VALID_METHODS.include?(value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module InstanceMethods
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def valid_method?(value)
|
|
26
|
+
self.class.valid_method?(value)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require_relative 'launcher/valid_methods'
|
|
2
|
+
require_relative 'launcher/options'
|
|
3
|
+
require_relative 'launcher/mode'
|
|
4
|
+
require_relative 'launcher/size'
|
|
5
|
+
require_relative 'launcher/benchmarking'
|
|
6
|
+
require_relative 'launcher/status_handling'
|
|
7
|
+
require_relative 'launcher/retry'
|
|
8
|
+
|
|
9
|
+
module Eco
|
|
10
|
+
module API
|
|
11
|
+
class Session
|
|
12
|
+
class Batch
|
|
13
|
+
module Launcher
|
|
14
|
+
def self.included(base)
|
|
15
|
+
unless base <= Eco::API::Common::Session::BaseSession
|
|
16
|
+
msg = "To be included only in Eco::API::Common::Session::BaseSession. "
|
|
17
|
+
msg << "Tried on '#{base}'"
|
|
18
|
+
raise msg
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
base.send(:include, ValidMethods)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
include Options
|
|
27
|
+
include Mode
|
|
28
|
+
include Size
|
|
29
|
+
include Benchmarking
|
|
30
|
+
include StatusHandling
|
|
31
|
+
include Retry
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def batch_from(
|
|
36
|
+
data,
|
|
37
|
+
method:,
|
|
38
|
+
params: {},
|
|
39
|
+
silent: false,
|
|
40
|
+
options: self.options
|
|
41
|
+
)
|
|
42
|
+
fatal "Invalid batch method: #{method}." unless valid_method?(method)
|
|
43
|
+
return unless data.is_a?(Enumerable)
|
|
44
|
+
|
|
45
|
+
msg = "cannot batch #{method} without api connnection, please provide a valid api connection!"
|
|
46
|
+
fatal msg unless (people_api = api&.people)
|
|
47
|
+
|
|
48
|
+
launch_batch(
|
|
49
|
+
data,
|
|
50
|
+
method: method,
|
|
51
|
+
per_page: params[:per_page] || batch_size(options),
|
|
52
|
+
people_api: people_api,
|
|
53
|
+
silent: silent,
|
|
54
|
+
options: options
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def launch_batch( # rubocop:disable Metrics/AbcSize
|
|
59
|
+
data,
|
|
60
|
+
method:,
|
|
61
|
+
status: nil,
|
|
62
|
+
job_mode: true, # rubocop:disable Lint/UnusedMethodArgument
|
|
63
|
+
options: self.options,
|
|
64
|
+
per_page: batch_size(options),
|
|
65
|
+
people_api: api&.people,
|
|
66
|
+
silent: false
|
|
67
|
+
)
|
|
68
|
+
iteration = 1
|
|
69
|
+
done = 0
|
|
70
|
+
iterations = (data.length.to_f / per_page).ceil
|
|
71
|
+
|
|
72
|
+
tap_status(status: status, enviro: enviro, queue: data, method: method) do |overall_status|
|
|
73
|
+
pending_for_server_error = data.to_a[0..]
|
|
74
|
+
|
|
75
|
+
start_time = Time.now
|
|
76
|
+
|
|
77
|
+
data.each_slice(per_page) do |slice|
|
|
78
|
+
msg = "starting batch '#{method}' iteration #{iteration}/#{iterations}, "
|
|
79
|
+
msg << "with #{slice.length} entries of #{data.length} -- #{done} done"
|
|
80
|
+
msg << (" " * 20)
|
|
81
|
+
log(:info) { msg } unless silent
|
|
82
|
+
|
|
83
|
+
start_slice = Time.now
|
|
84
|
+
|
|
85
|
+
offer_retry_on(Ecoportal::API::Errors::TimeOut) do
|
|
86
|
+
people_api.batch(job_mode: job_mode?(options)) do |batch|
|
|
87
|
+
slice.each do |person|
|
|
88
|
+
batch.public_send(method, person) do |response|
|
|
89
|
+
faltal("Request with no response") unless response
|
|
90
|
+
|
|
91
|
+
next if server_error?(response)
|
|
92
|
+
|
|
93
|
+
pending_for_server_error.delete(person)
|
|
94
|
+
overall_status[person] = response
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end # end batch
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
done += slice.length
|
|
101
|
+
|
|
102
|
+
msg = " ... iteration #{iteration}/#{iterations} done "
|
|
103
|
+
msg << "in #{str_per_sec(start_slice, slice.length)} "
|
|
104
|
+
msg << "(average: #{str_per_sec(start_time, done)})"
|
|
105
|
+
msg << (" " * 20)
|
|
106
|
+
log(:info) { msg } unless silent
|
|
107
|
+
|
|
108
|
+
iteration += 1
|
|
109
|
+
end # next slice
|
|
110
|
+
|
|
111
|
+
# temporary working around (due to back-end problems with batch/jobs)
|
|
112
|
+
unless pending_for_server_error.empty?
|
|
113
|
+
msg = "Going to re-try #{pending_for_server_error.count} due to server errors"
|
|
114
|
+
log(:info) { msg } unless silent
|
|
115
|
+
|
|
116
|
+
launch_batch(
|
|
117
|
+
pending_for_server_error,
|
|
118
|
+
status: overall_status,
|
|
119
|
+
method: method,
|
|
120
|
+
job_mode: false,
|
|
121
|
+
per_page: per_page,
|
|
122
|
+
people_api: people_api,
|
|
123
|
+
silent: silent,
|
|
124
|
+
options: options
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def server_error?(response)
|
|
131
|
+
res_status = response.status
|
|
132
|
+
server_error = !res_status || res_status.server_error?
|
|
133
|
+
other_error = !server_error && (!res_status.code || res_status.code < 100)
|
|
134
|
+
no_body = !server_error && !other_error && !response.body
|
|
135
|
+
server_error || other_error || no_body
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -6,48 +6,48 @@ module Eco
|
|
|
6
6
|
# @attr_reader stats [Hash] plain `Hash` with the number of requests that include an attribute
|
|
7
7
|
class RequestStats
|
|
8
8
|
CORE_ATTRS = Eco::API::Common::People::PersonParser::CORE_ATTRS
|
|
9
|
-
ACCOUNT_ATTRS = (Eco::API::Common::People::PersonParser::ACCOUNT_ATTRS + [
|
|
10
|
-
DETAILS_ATTRS = [
|
|
11
|
-
BLANKED_PREFIX =
|
|
12
|
-
DETAILS_FIELDS =
|
|
9
|
+
ACCOUNT_ATTRS = (Eco::API::Common::People::PersonParser::ACCOUNT_ATTRS + ['permissions_custom']).uniq
|
|
10
|
+
DETAILS_ATTRS = ['fields'].freeze
|
|
11
|
+
BLANKED_PREFIX = 'blanked_'.freeze
|
|
12
|
+
DETAILS_FIELDS = 'details_fields'.freeze
|
|
13
13
|
|
|
14
14
|
class << self
|
|
15
|
-
|
|
16
15
|
def valid_type?(type)
|
|
17
16
|
Eco::API::Session::Batch::Job.valid_type?(type.to_sym)
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
def core_attrs(stats: false, all: false)
|
|
21
20
|
CORE_ATTRS.dup.tap do |attrs|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
next unless stats || all
|
|
22
|
+
|
|
23
|
+
attrs.unshift('core')
|
|
24
|
+
attrs.concat(blank_attrs(CORE_ATTRS))
|
|
26
25
|
end
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
def account_attrs(stats: false, all: false)
|
|
30
29
|
ACCOUNT_ATTRS.dup.tap do |attrs|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
next unless stats || all
|
|
31
|
+
|
|
32
|
+
attrs.unshift('account_remove')
|
|
33
|
+
attrs.unshift('account') if all
|
|
34
|
+
attrs.concat(blank_attrs(ACCOUNT_ATTRS))
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
def details_attrs(stats: false, all: false)
|
|
40
39
|
DETAILS_ATTRS.dup.tap do |attrs|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
next unless stats || all
|
|
41
|
+
|
|
42
|
+
attrs.unshift('details_remove')
|
|
43
|
+
attrs.unshift('details') if all
|
|
45
44
|
end
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
def blanked_prefix(attr = nil)
|
|
49
48
|
@blanked_prefix ||= BLANKED_PREFIX
|
|
50
49
|
return @blanked_prefix unless attr
|
|
50
|
+
|
|
51
51
|
"#{blanked_prefix}#{attr}"
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -58,15 +58,16 @@ module Eco
|
|
|
58
58
|
def blank_attrs(attrs)
|
|
59
59
|
attrs.map {|attr| "#{blanked_prefix}#{attr}"}
|
|
60
60
|
end
|
|
61
|
-
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
attr_reader :type, :count
|
|
65
64
|
|
|
66
65
|
def initialize(type:, requests: [])
|
|
67
|
-
|
|
66
|
+
msg = "Type should be one of #{Eco::API::Session::Batch::Job.types}. Given: #{type}"
|
|
67
|
+
raise msg unless self.class.valid_type?(type.to_sym)
|
|
68
|
+
|
|
68
69
|
@type = type.to_sym
|
|
69
|
-
@count = requests
|
|
70
|
+
@count = requests&.length
|
|
70
71
|
@stats = build(requests)
|
|
71
72
|
end
|
|
72
73
|
|
|
@@ -86,21 +87,22 @@ module Eco
|
|
|
86
87
|
|
|
87
88
|
def attr_value(attr, percent: false, total: count, details: false)
|
|
88
89
|
target = details ? (@stats[DETAILS_FIELDS] || {}) : @stats
|
|
89
|
-
i
|
|
90
|
+
i = target[attr.to_s]
|
|
90
91
|
return i unless percent
|
|
92
|
+
|
|
91
93
|
percentage(i, total: total)
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
def core(percent: false)
|
|
95
|
-
attr_value(
|
|
97
|
+
attr_value('core', percent: percent)
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
def account(percent: false)
|
|
99
|
-
attr_value(
|
|
101
|
+
attr_value('account', percent: percent)
|
|
100
102
|
end
|
|
101
103
|
|
|
102
104
|
def details(percent: false)
|
|
103
|
-
attr_value(
|
|
105
|
+
attr_value('details', percent: percent)
|
|
104
106
|
end
|
|
105
107
|
|
|
106
108
|
def stats
|
|
@@ -110,13 +112,20 @@ module Eco
|
|
|
110
112
|
def build(requests)
|
|
111
113
|
stats.tap do |stats|
|
|
112
114
|
stats[type] = count
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
requests
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
|
|
116
|
+
no_requests =
|
|
117
|
+
!requests ||
|
|
118
|
+
!requests.is_a?(Enumerable) ||
|
|
119
|
+
requests.empty?
|
|
120
|
+
|
|
121
|
+
next if no_requests
|
|
122
|
+
|
|
123
|
+
stats[DETAILS_FIELDS] = Hash.new(0)
|
|
124
|
+
|
|
125
|
+
requests.each do |request|
|
|
126
|
+
add_core_stats(stats, request || {})
|
|
127
|
+
add_account_stats(stats, request || {})
|
|
128
|
+
add_details_stats(stats, request || {})
|
|
120
129
|
end
|
|
121
130
|
end
|
|
122
131
|
end
|
|
@@ -124,74 +133,84 @@ module Eco
|
|
|
124
133
|
def attrs_to_stat(stats, hash, attrs)
|
|
125
134
|
stats.tap do |st|
|
|
126
135
|
attrs.each do |attr|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
next unless hash.key?(attr)
|
|
137
|
+
|
|
138
|
+
st[attr] += 1
|
|
139
|
+
next unless blanked_value?(hash[attr])
|
|
140
|
+
|
|
141
|
+
st[blanked_prefix(attr)]+= 1
|
|
131
142
|
end
|
|
132
143
|
end
|
|
133
144
|
end
|
|
134
145
|
|
|
135
146
|
def add_core_stats(stats, request)
|
|
136
|
-
|
|
147
|
+
any_core = !(request.keys & core_attrs).empty? # rubocop:disable Style/ArrayIntersect
|
|
148
|
+
stats['core'] += 1 if any_core
|
|
149
|
+
|
|
137
150
|
attrs_to_stat(stats, request, core_attrs)
|
|
138
151
|
end
|
|
139
152
|
|
|
140
153
|
def add_account_stats(stats, request)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
return unless request.key?('account')
|
|
155
|
+
|
|
156
|
+
stats['account'] += 1
|
|
157
|
+
stats['account_remove'] += 1 unless request['account']
|
|
158
|
+
|
|
159
|
+
attrs_to_stat(stats, request['account'] || {}, account_attrs)
|
|
146
160
|
end
|
|
147
161
|
|
|
148
162
|
def add_details_stats(stats, request)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
end
|
|
163
|
+
return unless request.key?('details')
|
|
164
|
+
|
|
165
|
+
stats['details'] += 1
|
|
166
|
+
stats['details_remove'] += 1 unless request['details']
|
|
167
|
+
|
|
168
|
+
det_attrs = {}
|
|
169
|
+
if (fields = request.dig('details', 'fields'))
|
|
170
|
+
stats['fields'] += fields.length
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
det_attrs = fields.each_with_object(det_attrs) do |fld, hash|
|
|
173
|
+
hash[fld['alt_id']] = fld['value']
|
|
174
|
+
end
|
|
160
175
|
end
|
|
176
|
+
|
|
177
|
+
attrs_to_stat(stats[DETAILS_FIELDS], det_attrs, det_attrs.keys)
|
|
161
178
|
end
|
|
162
179
|
|
|
163
180
|
def core_pairs(percent: false)
|
|
164
|
-
cattrs
|
|
165
|
-
[[
|
|
181
|
+
cattrs = core_attrs + blank_attrs(core_attrs)
|
|
182
|
+
[['core', core(percent: percent)]] + pairs(cattrs, percent: percent, total: core)
|
|
166
183
|
end
|
|
167
184
|
|
|
168
185
|
def account_pairs(percent: false)
|
|
169
|
-
aattrs
|
|
170
|
-
[[
|
|
186
|
+
aattrs = ['account_remove'] + account_attrs + blank_attrs(account_attrs)
|
|
187
|
+
[['account', account(percent: percent)]] + pairs(aattrs, percent: percent, total: account)
|
|
171
188
|
end
|
|
172
189
|
|
|
173
190
|
def details_pairs(percent: false)
|
|
174
|
-
det_pairs = [[
|
|
175
|
-
det_pairs += [[
|
|
176
|
-
det_pairs += pairs([
|
|
191
|
+
det_pairs = [['details', details(percent: percent)]]
|
|
192
|
+
det_pairs += [['fields', fields_average]] if attr_value('fields') && fields_average
|
|
193
|
+
det_pairs += pairs(['details_remove'], percent: percent, total: details)
|
|
177
194
|
det_pairs += pairs(details_field_attrs, percent: percent, total: details, details: true)
|
|
195
|
+
det_pairs
|
|
178
196
|
end
|
|
179
197
|
|
|
180
198
|
def pairs(attrs, percent: false, total: count, details: false)
|
|
181
|
-
|
|
182
|
-
|
|
199
|
+
attrs.map do |a|
|
|
200
|
+
value = attr_value(a, percent: percent, total: total, details: details)
|
|
201
|
+
value.positive? ? [a, value] : nil
|
|
183
202
|
end.compact
|
|
184
203
|
end
|
|
185
204
|
|
|
186
205
|
def pairs_to_line(pairs, percent: false)
|
|
187
|
-
key_val_delimiter =
|
|
206
|
+
key_val_delimiter = ': '; attr_delimiter = ' ++ '
|
|
188
207
|
pairs.map do |p|
|
|
189
|
-
[p.first.to_s, "#{p.last
|
|
208
|
+
[p.first.to_s, "#{p.last}#{percent ? '%' : ''}"].join(key_val_delimiter)
|
|
190
209
|
end.join(attr_delimiter)
|
|
191
210
|
end
|
|
192
211
|
|
|
193
212
|
def core_attrs
|
|
194
|
-
@core_attrs
|
|
213
|
+
@core_attrs ||= self.class.core_attrs
|
|
195
214
|
end
|
|
196
215
|
|
|
197
216
|
def account_attrs
|
|
@@ -216,12 +235,10 @@ module Eco
|
|
|
216
235
|
|
|
217
236
|
def blanked_value?(value)
|
|
218
237
|
case value
|
|
219
|
-
when nil
|
|
220
|
-
true
|
|
221
|
-
when false
|
|
238
|
+
when nil, false
|
|
222
239
|
true
|
|
223
240
|
when Numeric
|
|
224
|
-
value
|
|
241
|
+
value.zero?
|
|
225
242
|
when Array
|
|
226
243
|
value.compact.empty?
|
|
227
244
|
when String
|
|
@@ -230,16 +247,17 @@ module Eco
|
|
|
230
247
|
end
|
|
231
248
|
|
|
232
249
|
def fields_average
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
250
|
+
fields_num = attr_value('fields')
|
|
251
|
+
return unless fields_num && (total = details).positive?
|
|
252
|
+
|
|
253
|
+
(fields_num / total.to_f).round(2)
|
|
236
254
|
end
|
|
237
255
|
|
|
238
256
|
def percentage(num, total: count)
|
|
257
|
+
return unless num
|
|
258
|
+
|
|
239
259
|
total ||= count
|
|
240
|
-
|
|
241
|
-
(num.to_f / total * 100).round(2)
|
|
242
|
-
end
|
|
260
|
+
(num.to_f / total * 100).round(2)
|
|
243
261
|
end
|
|
244
262
|
end
|
|
245
263
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require_relative 'launcher/valid_methods'
|
|
2
|
+
require_relative 'launcher/options'
|
|
3
|
+
|
|
4
|
+
module Eco
|
|
5
|
+
module API
|
|
6
|
+
class Session
|
|
7
|
+
class Batch
|
|
8
|
+
module Searcher
|
|
9
|
+
def self.included(base)
|
|
10
|
+
unless base <= Eco::API::Session::Batch
|
|
11
|
+
msg = "To be included only in Eco::API::Common::Session::BaseSession. "
|
|
12
|
+
msg << "Tried on '#{base}'"
|
|
13
|
+
raise msg
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Gets the _people_ of the organization according `params`.
|
|
20
|
+
# If `people` is not `nil`, scopes to only the people specified.
|
|
21
|
+
# @note
|
|
22
|
+
# - If `people` is given keys `page:` and `q` of `params:`.
|
|
23
|
+
# @param people [Nil, People, Enumerable<Person>, Enumerable<Hash>] target _People_ to launch the batch against.
|
|
24
|
+
# @param params [Hash] api request options.
|
|
25
|
+
# @option params [String] :page the page number `page` based on `:per_page`.
|
|
26
|
+
# @option params [String] :per_page the number of people included per each batch api request.
|
|
27
|
+
# @option params [String] :q some text to search. Omit this parameter to target all the people.
|
|
28
|
+
# @return [Array<People>] all the people based on `params`
|
|
29
|
+
def get_people(people = nil, params: {}, silent: false, options: self.options)
|
|
30
|
+
return get(params: params, silent: silent, options: options) unless people.is_a?(Enumerable)
|
|
31
|
+
|
|
32
|
+
launch(
|
|
33
|
+
people,
|
|
34
|
+
method: :get,
|
|
35
|
+
params: params,
|
|
36
|
+
silent: silent,
|
|
37
|
+
options: options
|
|
38
|
+
).people
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def search(data, silent: false, params: {}, options: self.options) # rubocop:disable Metrics/AbcSize
|
|
42
|
+
params = {per_page: batch_size(options)}.merge(params)
|
|
43
|
+
|
|
44
|
+
launch(
|
|
45
|
+
data,
|
|
46
|
+
method: :get,
|
|
47
|
+
params: params,
|
|
48
|
+
silent: silent,
|
|
49
|
+
options: options
|
|
50
|
+
).tap do |status|
|
|
51
|
+
status.mode = :search
|
|
52
|
+
|
|
53
|
+
entries = status.queue
|
|
54
|
+
puts "\n"
|
|
55
|
+
|
|
56
|
+
entries.each_with_index do |entry, i|
|
|
57
|
+
if (i % 10).zero?
|
|
58
|
+
percent = i * 100 / entries.length
|
|
59
|
+
print "Searching: #{percent.round}% (#{i}/#{entries.length} entries)\r"
|
|
60
|
+
$stdout.flush
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
next if status.success?(entry)
|
|
64
|
+
|
|
65
|
+
email = nil
|
|
66
|
+
if entry.respond_to?(:email)
|
|
67
|
+
email = entry.email
|
|
68
|
+
elsif entry.respond_to?(:to_h)
|
|
69
|
+
email = entry.to_h["email"]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
people_matching = []
|
|
73
|
+
email = email.to_s.strip.downcase
|
|
74
|
+
|
|
75
|
+
unless email.empty?
|
|
76
|
+
people_matching = get(
|
|
77
|
+
params: params.merge(q: email),
|
|
78
|
+
silent: silent,
|
|
79
|
+
options: options
|
|
80
|
+
).select do |person|
|
|
81
|
+
person.email == email
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
case people_matching.length
|
|
86
|
+
when 1
|
|
87
|
+
status.set_person_match(entry, people_matching.first)
|
|
88
|
+
when 2..Float::INFINITY
|
|
89
|
+
status.set_people_match(entry, people_matching)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def get(params: {}, silent: false, options: self.options)
|
|
98
|
+
msg = "cannot batch get without api connnection, please provide a valid api connection!"
|
|
99
|
+
fatal msg unless (people_api = api&.people)
|
|
100
|
+
|
|
101
|
+
params = {per_page: batch_size(options)}.merge(params)
|
|
102
|
+
people_api.get_all(params: params, silent: silent)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -2,54 +2,11 @@ module Eco
|
|
|
2
2
|
module API
|
|
3
3
|
class Session
|
|
4
4
|
class Batch < Common::Session::BaseSession
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
VALID_METHODS = %i[get create update upsert delete].freeze
|
|
5
|
+
require_relative 'batch/launcher'
|
|
6
|
+
require_relative 'batch/searcher'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def valid_method?(value)
|
|
12
|
-
VALID_METHODS.include?(value)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def batch_size(opts = options)
|
|
17
|
-
return self.class::DEFAULT_JOB_SIZE if job_mode?(opts)
|
|
18
|
-
|
|
19
|
-
self.class::DEFAULT_BATCH_SIZE
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# @return [Symbol] the batch mode to run
|
|
23
|
-
def batch_mode(opts = options)
|
|
24
|
-
opts.dig(:workflow, :batch, :mode) || :batch
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# @return [Boolean] are we running in `:job` mode?
|
|
28
|
-
def job_mode?(opts = options)
|
|
29
|
-
batch_mode(opts) == :job
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Gets the _people_ of the organization according `params`.
|
|
33
|
-
# If `people` is not `nil`, scopes to only the people specified.
|
|
34
|
-
# @note
|
|
35
|
-
# - If `people` is given keys `page:` and `q` of `params:`.
|
|
36
|
-
# @param people [Nil, People, Enumerable<Person>, Enumerable<Hash>] target _People_ to launch the batch against.
|
|
37
|
-
# @param params [Hash] api request options.
|
|
38
|
-
# @option params [String] :page the page number `page` based on `:per_page`.
|
|
39
|
-
# @option params [String] :per_page the number of people included per each batch api request.
|
|
40
|
-
# @option params [String] :q some text to search. Omit this parameter to target all the people.
|
|
41
|
-
# @return [Array<People>] all the people based on `params`
|
|
42
|
-
def get_people(people = nil, params: {}, silent: false, options: self.options)
|
|
43
|
-
return get(params: params, silent: silent, options: options) unless people.is_a?(Enumerable)
|
|
44
|
-
|
|
45
|
-
launch(
|
|
46
|
-
people,
|
|
47
|
-
method: :get,
|
|
48
|
-
params: params,
|
|
49
|
-
silent: silent,
|
|
50
|
-
options: options
|
|
51
|
-
).people
|
|
52
|
-
end
|
|
8
|
+
include Launcher
|
|
9
|
+
include Searcher
|
|
53
10
|
|
|
54
11
|
# launches a batch of `method` type using `people` and the specified `params`
|
|
55
12
|
# @raise Exception
|
|
@@ -60,222 +17,8 @@ module Eco
|
|
|
60
17
|
# @param params [Hash] api request options.
|
|
61
18
|
# @option params [String] :per_page the number of people included per each batch api request.
|
|
62
19
|
# @return [Batch::Status] the `status` of this batch launch.
|
|
63
|
-
def launch(
|
|
64
|
-
batch_from(
|
|
65
|
-
people,
|
|
66
|
-
method: method,
|
|
67
|
-
params: params,
|
|
68
|
-
silent: silent,
|
|
69
|
-
options: options
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def search(data, silent: false, params: {}, options: self.options) # rubocop:disable Metrics/AbcSize
|
|
74
|
-
params = {per_page: batch_size(options)}.merge(params)
|
|
75
|
-
|
|
76
|
-
launch(
|
|
77
|
-
data,
|
|
78
|
-
method: :get,
|
|
79
|
-
params: params,
|
|
80
|
-
silent: silent,
|
|
81
|
-
options: options
|
|
82
|
-
).tap do |status|
|
|
83
|
-
status.mode = :search
|
|
84
|
-
|
|
85
|
-
entries = status.queue
|
|
86
|
-
puts "\n"
|
|
87
|
-
entries.each_with_index do |entry, i|
|
|
88
|
-
if (i % 10).zero?
|
|
89
|
-
percent = i * 100 / entries.length
|
|
90
|
-
print "Searching: #{percent.round}% (#{i}/#{entries.length} entries)\r"
|
|
91
|
-
$stdout.flush
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
next if status.success?(entry)
|
|
95
|
-
|
|
96
|
-
email = nil
|
|
97
|
-
if entry.respond_to?(:email)
|
|
98
|
-
email = entry.email
|
|
99
|
-
elsif entry.respond_to?(:to_h)
|
|
100
|
-
email = entry.to_h["email"]
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
people_matching = []
|
|
104
|
-
email = email.to_s.strip.downcase
|
|
105
|
-
unless email.empty?
|
|
106
|
-
people_matching = get(
|
|
107
|
-
params: params.merge(q: email),
|
|
108
|
-
silent: silent,
|
|
109
|
-
options: options
|
|
110
|
-
).select do |person|
|
|
111
|
-
person.email == email
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
case people_matching.length
|
|
116
|
-
when 1
|
|
117
|
-
status.set_person_match(entry, people_matching.first)
|
|
118
|
-
when 2..Float::INFINITY
|
|
119
|
-
status.set_people_match(entry, people_matching)
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
private
|
|
126
|
-
|
|
127
|
-
def get(params: {}, silent: false, options: self.options)
|
|
128
|
-
msg = "cannot batch get without api connnection, please provide a valid api connection!"
|
|
129
|
-
fatal msg unless (people_api = api&.people)
|
|
130
|
-
|
|
131
|
-
params = {per_page: batch_size(options)}.merge(params)
|
|
132
|
-
people_api.get_all(params: params, silent: silent)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def batch_from(
|
|
136
|
-
data,
|
|
137
|
-
method:,
|
|
138
|
-
params: {},
|
|
139
|
-
silent: false,
|
|
140
|
-
options: self.options
|
|
141
|
-
)
|
|
142
|
-
fatal "Invalid batch method: #{method}." unless self.class.valid_method?(method)
|
|
143
|
-
return nil if !data || !data.is_a?(Enumerable)
|
|
144
|
-
|
|
145
|
-
msg = "cannot batch #{method} without api connnection, please provide a valid api connection!"
|
|
146
|
-
fatal msg unless (people_api = api&.people)
|
|
147
|
-
|
|
148
|
-
# param q does not make sense here, even for GET method
|
|
149
|
-
params = {per_page: batch_size(options)}.merge(params)
|
|
150
|
-
per_page = params[:per_page] || batch_size(options)
|
|
151
|
-
|
|
152
|
-
launch_batch(
|
|
153
|
-
data,
|
|
154
|
-
method: method,
|
|
155
|
-
per_page: per_page,
|
|
156
|
-
people_api: people_api,
|
|
157
|
-
silent: silent,
|
|
158
|
-
options: options
|
|
159
|
-
)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# Default way to retrieve options (unless provided)
|
|
163
|
-
def options
|
|
164
|
-
ASSETS.cli.options
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def launch_batch( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
168
|
-
data,
|
|
169
|
-
method:,
|
|
170
|
-
status: nil,
|
|
171
|
-
job_mode: true, # rubocop:disable Lint/UnusedMethodArgument
|
|
172
|
-
options: self.options,
|
|
173
|
-
per_page: batch_size(options),
|
|
174
|
-
people_api: api&.people,
|
|
175
|
-
silent: false
|
|
176
|
-
)
|
|
177
|
-
iteration = 1
|
|
178
|
-
done = 0
|
|
179
|
-
iterations = (data.length.to_f / per_page).ceil
|
|
180
|
-
|
|
181
|
-
status ||= Eco::API::Session::Batch::Status.new(
|
|
182
|
-
enviro,
|
|
183
|
-
queue: data,
|
|
184
|
-
method: method
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
status.tap do
|
|
188
|
-
pending_for_server_error = data.to_a[0..]
|
|
189
|
-
|
|
190
|
-
start_time = Time.now
|
|
191
|
-
|
|
192
|
-
data.each_slice(per_page) do |slice|
|
|
193
|
-
msg = "starting batch '#{method}' iteration #{iteration}/#{iterations}, "
|
|
194
|
-
msg << "with #{slice.length} entries of #{data.length} -- #{done} done"
|
|
195
|
-
msg << (" " * 20)
|
|
196
|
-
log(:info) { msg } unless silent
|
|
197
|
-
|
|
198
|
-
start_slice = Time.now
|
|
199
|
-
|
|
200
|
-
offer_retry_on(Ecoportal::API::Errors::TimeOut) do
|
|
201
|
-
people_api.batch(job_mode: job_mode?(options)) do |batch|
|
|
202
|
-
slice.each do |person|
|
|
203
|
-
batch.public_send(method, person) do |response|
|
|
204
|
-
faltal("Request with no response") unless response
|
|
205
|
-
|
|
206
|
-
next if server_error?(response)
|
|
207
|
-
|
|
208
|
-
pending_for_server_error.delete(person)
|
|
209
|
-
status[person] = response
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end # end batch
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
done += slice.length
|
|
216
|
-
|
|
217
|
-
msg = " ... iteration #{iteration}/#{iterations} done "
|
|
218
|
-
msg << "in #{str_stats(start_slice, slice.length)} "
|
|
219
|
-
msg << "(average: #{str_stats(start_time, done)})"
|
|
220
|
-
msg << (" " * 20)
|
|
221
|
-
log(:info) { msg } unless silent
|
|
222
|
-
|
|
223
|
-
iteration += 1
|
|
224
|
-
end # next slice
|
|
225
|
-
|
|
226
|
-
# temporary working around (due to back-end problems with batch/jobs)
|
|
227
|
-
unless pending_for_server_error.empty?
|
|
228
|
-
msg = "Going to re-try #{pending_for_server_error.count} due to server errors"
|
|
229
|
-
log(:info) { msg } unless silent
|
|
230
|
-
|
|
231
|
-
launch_batch(
|
|
232
|
-
pending_for_server_error,
|
|
233
|
-
status: status,
|
|
234
|
-
method: method,
|
|
235
|
-
job_mode: false,
|
|
236
|
-
per_page: per_page,
|
|
237
|
-
people_api: people_api,
|
|
238
|
-
silent: silent,
|
|
239
|
-
options: options
|
|
240
|
-
)
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def server_error?(response)
|
|
246
|
-
res_status = response.status
|
|
247
|
-
server_error = !res_status || res_status.server_error?
|
|
248
|
-
other_error = !server_error && (!res_status.code || res_status.code < 100)
|
|
249
|
-
no_body = !server_error && !other_error && !response.body
|
|
250
|
-
server_error || other_error || no_body
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def offer_retry_on(error_type, retries_left = 3, &block)
|
|
254
|
-
yield
|
|
255
|
-
rescue error_type => err
|
|
256
|
-
raise err.class, err.message, cause: nil unless retries_left.positive?
|
|
257
|
-
|
|
258
|
-
explanation = "#{err}\n"
|
|
259
|
-
explanation << "You have #{retries_left} retries left."
|
|
260
|
-
question = " Do you want to retry (y/N)?"
|
|
261
|
-
|
|
262
|
-
prompt_user(question, default: "Y", explanation: explanation, timeout: 10) do |response|
|
|
263
|
-
raise unless response.upcase.start_with?("Y")
|
|
264
|
-
|
|
265
|
-
puts "\nOkay... let's retry!"
|
|
266
|
-
offer_retry_on(error_type, retries_left - 1, &block)
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
def str_stats(start, count)
|
|
271
|
-
now = Time.now
|
|
272
|
-
secs = (now - start).round(2)
|
|
273
|
-
if secs > 0.0
|
|
274
|
-
per_sec = (count.to_f / secs).round(2)
|
|
275
|
-
"#{secs}s -> #{per_sec} people/s"
|
|
276
|
-
else
|
|
277
|
-
" -- "
|
|
278
|
-
end
|
|
20
|
+
def launch(...)
|
|
21
|
+
batch_from(...)
|
|
279
22
|
end
|
|
280
23
|
end
|
|
281
24
|
end
|
data/lib/eco/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eco-helpers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oscar Segura
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-10-
|
|
11
|
+
date: 2024-10-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -229,7 +229,7 @@ dependencies:
|
|
|
229
229
|
version: '0.10'
|
|
230
230
|
- - ">="
|
|
231
231
|
- !ruby/object:Gem::Version
|
|
232
|
-
version: 0.10.
|
|
232
|
+
version: 0.10.5
|
|
233
233
|
type: :runtime
|
|
234
234
|
prerelease: false
|
|
235
235
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -239,7 +239,7 @@ dependencies:
|
|
|
239
239
|
version: '0.10'
|
|
240
240
|
- - ">="
|
|
241
241
|
- !ruby/object:Gem::Version
|
|
242
|
-
version: 0.10.
|
|
242
|
+
version: 0.10.5
|
|
243
243
|
- !ruby/object:Gem::Dependency
|
|
244
244
|
name: ecoportal-api-graphql
|
|
245
245
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -656,8 +656,17 @@ files:
|
|
|
656
656
|
- lib/eco/api/session/batch/job.rb
|
|
657
657
|
- lib/eco/api/session/batch/jobs.rb
|
|
658
658
|
- lib/eco/api/session/batch/jobs_groups.rb
|
|
659
|
+
- lib/eco/api/session/batch/launcher.rb
|
|
660
|
+
- lib/eco/api/session/batch/launcher/benchmarking.rb
|
|
661
|
+
- lib/eco/api/session/batch/launcher/mode.rb
|
|
662
|
+
- lib/eco/api/session/batch/launcher/options.rb
|
|
663
|
+
- lib/eco/api/session/batch/launcher/retry.rb
|
|
664
|
+
- lib/eco/api/session/batch/launcher/size.rb
|
|
665
|
+
- lib/eco/api/session/batch/launcher/status_handling.rb
|
|
666
|
+
- lib/eco/api/session/batch/launcher/valid_methods.rb
|
|
659
667
|
- lib/eco/api/session/batch/policies.rb
|
|
660
668
|
- lib/eco/api/session/batch/request_stats.rb
|
|
669
|
+
- lib/eco/api/session/batch/searcher.rb
|
|
661
670
|
- lib/eco/api/session/batch/status.rb
|
|
662
671
|
- lib/eco/api/session/config.rb
|
|
663
672
|
- lib/eco/api/session/config/api.rb
|