eco-helpers 2.1.4 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca676558c0e1a885b1f9e15d68e3c4f3688b90713c88182854b426a8ce1bf447
4
- data.tar.gz: 41e8693e90664d7789040dd2dab66521e330693a9ae62f7bb5c5bc2dbd940911
3
+ metadata.gz: a60227ce2638442d0a7b4cb8a292506ef2b4f13b59535c53e7397afb0ac48eb0
4
+ data.tar.gz: 794d2c7e3c85e1a9e97c22677efd483e94fb47687ff6dd2c238613099022a7e1
5
5
  SHA512:
6
- metadata.gz: 65b5157cc78ff3016f932283e2f0504e46681e15dc2acd902bb0e6188af98f5fe5271ad0db49a48fe969c642df3146daffeffcb0008dda3012433450dbbc1e87
7
- data.tar.gz: 3a23f58986631f819b18bd8395ec92c321e9cb21bfde172a3e565df309cdad3b759d1d60f8c4bf52cff29332b49607f30afad19c90a80c5f9fb974637c3b218e
6
+ metadata.gz: 4186a76a325a920057c6a71a0dd1f774a9342a4f1e280c392e639ceb008f845a104b7615fa8e03d11f9dab73fd995e15f25551c3b989577fa2595a64505550b9
7
+ data.tar.gz: 2ab664150646c1f9af560aca8c2a1dff9ddd651f867e28bddd5d560ec89c4ae9bbc8884fadd07346ab7ec6544288adcc2ce9be866adadbb63db04f0e521ee3e9
data/CHANGELOG.md CHANGED
@@ -1,12 +1,32 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [2.1.5] - 2022-10-xx
4
+ ## [2.1.7] - 2022-11-xx
5
5
 
6
6
  ### Added
7
7
  ### Changed
8
8
  ### Fixed
9
9
 
10
+ ## [2.1.6] - 2022-11-17
11
+
12
+ ### Fixed
13
+ - `Eco::API::UseCases::OozeSamples::Helpers::Shortcuts#to_i` (missing param on method)
14
+ - `Eco::API::UseCases::OozeSamples::module HelpersMigration::TypedFieldsPairing#multi_delete` typo
15
+
16
+ ## [2.1.5] - 2022-11-17
17
+
18
+ ### Added
19
+ - `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` added creation count
20
+ - Requires use of `#create_ooze(draft, template_id)`
21
+ - `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` added method
22
+ - `with_rescue` that prompts user to continue
23
+ - this is for usage in child classes
24
+ - new use case and helpers thereof `Eco::API::UseCases::OozeSamples::RegisterMigrationCase`
25
+
26
+ ### Changed
27
+ - upgraded `ecoportal-api-v2` **gem**
28
+ - **refactored** `Eco::API::UseCases::OozeSamples::Helpers::Shortcuts#same_string?`
29
+
10
30
  ## [2.1.4] - 2022-10-20
11
31
 
12
32
  ### Added
data/eco-helpers.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "redcarpet", ">= 3.5.1", "< 3.6"
32
32
 
33
33
  spec.add_dependency 'ecoportal-api', '>= 0.8.5', '< 0.9'
34
- spec.add_dependency 'ecoportal-api-v2', '>= 0.9.3', '< 0.10'
34
+ spec.add_dependency 'ecoportal-api-v2', '>= 0.9.5', '< 0.10'
35
35
  spec.add_dependency 'ecoportal-api-graphql', '>= 0.1.10', '< 0.2'
36
36
  spec.add_dependency 'aws-sdk-s3', '>= 1.83.0', '< 2'
37
37
  spec.add_dependency 'aws-sdk-ses', '>= 1.36.0', '< 2'
@@ -4,29 +4,71 @@ module Eco
4
4
  class OozeSamples
5
5
  module Helpers
6
6
  module Shortcuts
7
+ # Basic simplification pattern (matches all but a-z and blank space)
8
+ def non_letters_regex
9
+ @non_letters_regex ||= /[^a-z ]+/
10
+ end
7
11
 
8
- # Offers multiple ways to compare two strings
9
- def same_string?(value1, value2, exact: false, mild: false)
10
- case
11
- when value1.is_a?(String) && value2.is_a?(String)
12
- if exact
13
- value1 == value2
14
- else
15
- v1 = value1.to_s.strip.downcase
16
- v2 = value2.to_s.strip.downcase
12
+ # Matches anything between two consecutive (), inclusive
13
+ def bracked_regex
14
+ @bracked_regex ||= /(?<bracked>\([^\)]+?\))/
15
+ end
16
+
17
+ # It always downcase, trim and remove double spaces.
18
+ # @param ignore Boolean, Regexp] ingored when `exact` is `true`
19
+ # * when `false`: it does not do anything additional
20
+ # * when `Regex`: it removes all characters that match the expression.
21
+ # * when `String`: each character listed is removed.
22
+ # * when `Array`: reduces `str` processing `ignore` in order.
23
+ # * when `true` (or otherwise): it removes all non a-zA-Z characters but blank spaces.
24
+ def simplify_string(str, ignore: false)
25
+ str = str.to_s.strip.downcase.gsub(/\s+/, ' ')
26
+ return str unless ignore
27
+ sub = non_letters_regex
28
+ case ignore
29
+ when Regexp; sub = ignore
30
+ when String; sub = /[#{ignore}]+/
31
+ when Array
32
+ return ignore.reduce(str) do |out, sub|
33
+ simplify_string(out, ignore: sub)
34
+ end
35
+ end
36
+ str.gsub(sub, '').gsub(/\s+/, ' ').strip
37
+ end
17
38
 
18
- if mild
19
- v1 = v1.gsub(/[^a-z ]+/, ' ').gsub(/\s+/, ' ').strip
20
- v2 = v2.gsub(/[^a-z ]+/, ' ').gsub(/\s+/, ' ').strip
21
- end
22
- v1 == v2
39
+ # Offers multiple simplification methods to compare two strings
40
+ # @note only one of the values can be a Regexp
41
+ # @param value1 [String, Regexp, Nil]
42
+ # @param value2 [String, Regexp, Nil]
43
+ # @param exact [Boolean]
44
+ # * when `true`: requiring the values to be exactly the same
45
+ # * otherwise (`false`): compares in downcase, with no extra spaces
46
+ # @param mild [Boolean] only a-z comparison
47
+ # @param ignore [see @simplify_string]
48
+ def same_string?(value1, value2, exact: false, mild: false, ignore: false)
49
+ return true if value1.to_s.strip.empty? && value2.to_s.strip.empty?
50
+ return false if value1.to_s.strip.empty? || value2.to_s.strip.empty?
51
+ val1, val2 = value1, value2
52
+ unless exact
53
+ if val1.is_a?(String)
54
+ val1 = simplify_string(val1, ignore: ignore) if ignore
55
+ val1 = simplify_string(val1, ignore: non_letters_regex) if mild
56
+ end
57
+ if val2.is_a?(String)
58
+ val2 = simplify_string(val2, ignore: ignore) if ignore
59
+ val2 = simplify_string(val2, ignore: non_letters_regex) if mild
23
60
  end
24
- when value1.is_a?(Regexp) && value2.is_a?(String)
25
- value2 =~ value1
26
- when value1.is_a?(String) && value2.is_a?(Regexp)
27
- value1 =~ value2
61
+ end
62
+ case
63
+ when val1.is_a?(String) && val2.is_a?(String)
64
+ val1 == val2
65
+ when val1.is_a?(Regexp) && val2.is_a?(String)
66
+ val2 =~ val1
67
+ when val1.is_a?(String) && val2.is_a?(Regexp)
68
+ val1 =~ val2
28
69
  else
29
- value1 == value2
70
+ #val1 == val2
71
+ raise "Expected at least one String, and either a String or Regex. Given: (1: #{val1.class}) and (2: #{val2.class})"
30
72
  end
31
73
  end
32
74
 
@@ -84,7 +126,7 @@ module Eco
84
126
  end
85
127
  end
86
128
 
87
- def to_i
129
+ def to_i(value)
88
130
  Float(value).to_i
89
131
  end
90
132
 
@@ -0,0 +1,125 @@
1
+ class Eco::API::UseCases::OozeSamples
2
+ module HelpersMigration
3
+ module Copying
4
+ def session
5
+ defined?(super)? super : @session
6
+ end
7
+
8
+ def logger
9
+ session.logger
10
+ end
11
+
12
+ def copy_generic_paired_fields(src, dst, exclude_ximport: false)
13
+ HelpersMigration::TypedFieldsPairing.new(src, dst, exclude_ximport: false).tap do |pairing|
14
+ pairing.each_pair do |src_fld, dst_fld|
15
+ copy_field_content(src_fld, dst_fld)
16
+ yield(src_fld, dst_fld, pairing) if block_given?
17
+ end
18
+ end
19
+ end
20
+
21
+ def copy_hooked_fields(src, dst, hook:, type:, fields_tracker: nil)
22
+ case hook
23
+ when String
24
+ with_src_dest_field_pair(src, dst, label: to_regex(hook), type: type) do |src_fld, dst_fld|
25
+ copy_field_content(src_fld, dst_fld, type).tap do |result|
26
+ fields_tracker.resolve(src_fld, dst_fld) if fields_tracker
27
+ yield(src_fld, dst_fld, fields_tracker) if block_given?
28
+ end
29
+ end
30
+ when Hash
31
+ hook.each do |hook_src, hook_dst|
32
+ src_lab, dst_lab = to_regex(hook_src), to_regex(hook_dst)
33
+ with_src_dest_field_pair(src, dst, label: src_lab, label_dst: dst_lab, type: type) do |src_fld, dst_fld|
34
+ copy_field_content(src_fld, dst_fld, type).tap do |result|
35
+ fields_tracker.resolve(src_fld, dst_fld) if fields_tracker
36
+ yield(src_fld, dst_fld, fields_tracker) if block_given?
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def to_regex(str)
44
+ /^#{str}$/i
45
+ end
46
+
47
+ def with_src_dest_field_pair(src, dst, label: nil, label_dst: label, type: nil)
48
+ unless respond_to?(:with_fields, true)
49
+ raise "These helpers are to be included in class cases inheriting from Eco::API::UseCases::OozeSamples::RegisterUpdateCase"
50
+ end
51
+ src_flds = with_fields(src, label: label, type: type)
52
+ dst_flds = with_fields(dst, label: label_dst, type: type)
53
+
54
+ if dst_flds.count > 1
55
+ logger.warn("There are #{dst_flds.count} destination '#{type}' fields named '#{label_dst}' (source: '#{src.id}'). Using first...")
56
+ elsif dst_flds.empty?
57
+ logger.warn("There isn't a destination '#{type}' field named '#{label_dst}' (source: '#{src.id}')")
58
+ return nil, nil
59
+ end
60
+
61
+ if src_flds.count > 1
62
+ logger.warn("There are #{src_flds.count} source '#{type}' fields named '#{label}' (source: '#{src.id}'). Using first...")
63
+ elsif src_flds.empty?
64
+ logger.warn("There isn't a source '#{type}' field named '#{label}' (source: '#{src.id}')")
65
+ return nil, dst
66
+ end
67
+
68
+ [src_flds.first, dst_flds.first].tap do |(src_fld, dst_fld)|
69
+ yield(src_fld, dst_fld) if block_given?
70
+ end
71
+ end
72
+
73
+ def copy_field_content(src, dst, type = src.type)
74
+ case type
75
+ when "select"
76
+ src.values.each {|val| dst.select(val)}
77
+ dst.other_desc = src.other_desc if src.other && src.other_desc && dst.other
78
+ when "plain_text", "date", "number", "gauge"
79
+ dst.value = src.value
80
+ when "rich_text"
81
+ dst.content = src.content
82
+ when "cross_reference"
83
+ src.reference_ids.each {|id| dst.add(id)}
84
+ when "people"
85
+ dst.people_ids << src.people_ids.to_a
86
+ when "geo"
87
+ src_coo, dst_coo = src.coordinates, dst.coordinates
88
+ dst_coo.lat, dst_coo.lon = src_coo.lat, src_coo.lon
89
+ when "file"
90
+ src.items.each do |src_item|
91
+ dst.add_file(src_item.file_container_id) do |dst_item|
92
+ dst_item.label = src_item.label
93
+ end
94
+ end
95
+ when "image_gallery"
96
+ src.images.each do |src_image|
97
+ dst.add_image(src_image.upload_id) do |dst_image|
98
+ dst_image.caption = src_image.caption
99
+ end
100
+ end
101
+ when "checklist"
102
+ src.items.each do |src_item|
103
+ dst.add_item(label: src_item.label) do |dst_item|
104
+ dst_item.checked = src_item.checked
105
+ end
106
+ end
107
+ when "law"
108
+ "won't copy"
109
+ when "page_action"
110
+ "won't copy"
111
+ when "actions_list"
112
+ "won't copy"
113
+ when "signature"
114
+ "can't copy"
115
+ when "tag_field"
116
+ "can't copy"
117
+ when "chart"
118
+ "won't copy"
119
+ when "frequency_rate_chart"
120
+ "won't copy"
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,235 @@
1
+ # It pairs fields of same type with same label name.
2
+ # It does it in two screenings:
3
+ # => (1) where the label name is exactly the same.
4
+ # => (2) with the rest, case insensitive letters (a-z), removed content between brackets
5
+ # Other features:
6
+ # => It keeps track on source and destination fields that were not paired
7
+ # => It allows to remove from the unpaired classification fields that are paired by other means
8
+ # => It tracks what source fields have multiple candidate destination fields
9
+ # In other words: it is one of the World Wonders of 2022 !!
10
+ class Eco::API::UseCases::OozeSamples
11
+ module HelpersMigration
12
+ class TypedFieldsPairing
13
+ include Eco::API::UseCases::OozeSamples::Helpers::Shortcuts
14
+
15
+ EXCLUDED_TYPES = ["tag_field", "chart", "frequency_rate_chart"]
16
+
17
+ attr_reader :src, :dst
18
+ attr_reader :src_unpaired, :dst_unpaired
19
+ attr_reader :src_dst_pairs, :src_dst_multi
20
+
21
+ def initialize(source, destination, src_excluded: nil, dst_excluded: nil,
22
+ exclude_ximport: false)
23
+ @src = source
24
+ @dst = destination
25
+ @src_excluded = src_excluded || []
26
+ @dst_excluded = dst_excluded || []
27
+ @exclude_ximport = !!exclude_ximport
28
+ reset!
29
+ process(exact: true)
30
+ process
31
+ end
32
+
33
+ def each_pair
34
+ src_dst_pairs.each do |(src_field, dst_field)|
35
+ yield(src_field, dst_field) if block_given?
36
+ end
37
+ end
38
+
39
+ def report_src_unpaired(only_present: true, only_indexed: false)
40
+ return nil if src_unpaired.empty?
41
+ msg = "Source entry with unpaired fields (#{object_reference(src)}):\n"
42
+ src_filtered = src_unpaired.values.flatten.reject do |src_fld|
43
+ only_present && src_fld.empty?
44
+ end.reject do |src_fld|
45
+ only_indexed && src_fld.deindex
46
+ end
47
+ return nil if src_filtered.empty?
48
+ msg << src_filtered.map do |src_fld|
49
+ " • #{object_reference(src_fld)}"
50
+ end.join("\n")
51
+ end
52
+
53
+ def report_dst_unpaired(only_required: false, exclude_ximport: true)
54
+ return nil if dst_unpaired.empty?
55
+ msg = "Destination entry with unpaired fields (#{object_reference(src)}):\n"
56
+ dst_filtered = dst_unpaired.values.flatten.reject do |dst_fld|
57
+ only_required && !dst_fld.required
58
+ end.reject do |dst_fld|
59
+ exclude_ximport && ximport?(dst_fld)
60
+ end
61
+ return nil if dst_filtered.empty?
62
+ msg << dst_filtered.map do |dst_fld|
63
+ " • #{object_reference(dst_fld)}"
64
+ end.join("\n")
65
+ end
66
+
67
+ def report_multi_pairs(only_present: true, only_indexed: false, dst_exclude_ximport: true)
68
+ return nil if src_dst_multi.empty?
69
+ "".tap do |msg|
70
+ msg << "Source fields in (#{object_reference(src)}) paired with multiple destination fields:\n"
71
+ msg_flds = ""
72
+ src_dst_multi.each do |type, pairs|
73
+ pairs.each do |(src_fld, dst_flds)|
74
+ next if (only_present && src_fld.empty?) || (only_indexed && src_fld.deindex)
75
+ dst_filtered = dst_flds.reject do |dst_fld|
76
+ dst_exclude_ximport && ximport?(dst_fld)
77
+ end
78
+ next if dst_filtered.empty?
79
+ msg_flds << " • #{object_reference(src_fld)}\n"
80
+ msg_flds << dst_filtered.map do |dst_fld|
81
+ " • #{object_reference(dst_fld)}"
82
+ end.compact.join("\n")
83
+ end
84
+ end
85
+ return nil if msg_flds.empty?
86
+ msg << msg_flds
87
+ end
88
+ end
89
+
90
+ # Remove from unpaired tracking those `flds`.
91
+ def resolve(*flds)
92
+ flds.each do |fld|
93
+ next if unpaired_delete(fld, src_unpaired)
94
+ unpaired_delete(fld, dst_unpaired)
95
+ end
96
+ multi_delete(*flds)
97
+ end
98
+
99
+ def some_multi?
100
+ !src_dst_multi.empty?
101
+ end
102
+
103
+ private
104
+
105
+ # When comparing, use only a-z letters.
106
+ def only_letters?
107
+ true
108
+ end
109
+
110
+ # When comparign, remove anything between brackets from the field label name.
111
+ def non_bracked?
112
+ true
113
+ end
114
+
115
+ # If `false`, it compares with all lower case.
116
+ def exact?
117
+ !(only_letters? || non_bracked?)
118
+ end
119
+
120
+ def process(exact: false)
121
+ src_unpaired.each do |type, src_pend|
122
+ next unless (dst_pend = dst_unpaired[type]) && !dst_pend.empty?
123
+ src_pend.dup.each do |src_fld|
124
+ dst_candidates = dst_pend.select do |dst_fld|
125
+ str_eq?(src_fld.label, dst_fld.label, exact: exact)
126
+ end
127
+ next if dst_candidates.empty?
128
+ if dst_candidates.count == 1
129
+ pair_add(src_fld, dst_candidates.first)
130
+ else
131
+ multi_add(type, src_fld, *dst_candidates)
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ def str_eq?(str1, str2, exact: false)
138
+ same_string?(str1, str2, **str_eq_params.merge(exact: exact))
139
+ end
140
+
141
+ def str_eq_params
142
+ {
143
+ exact: exact?,
144
+ mild: only_letters?,
145
+ ignore: false
146
+ }.tap do |params|
147
+ params.merge!(ignore: bracked_regex) if non_bracked?
148
+ end
149
+ end
150
+
151
+ def pair_add(src_fld, dst_fld)
152
+ resolve(src_fld, dst_fld)
153
+ src_dst_pairs << [src_fld, dst_fld]
154
+ end
155
+
156
+ # Removes a field from a (src or dst) unpaired fields
157
+ def unpaired_delete(fld, unpaired)
158
+ type = fld.type
159
+ return false unless unpaired.key?(type)
160
+ return false unless (flds = unpaired[type]).delete(fld)
161
+ unpaired.delete(type) if flds.empty?
162
+ true
163
+ end
164
+
165
+ def multi_add(type, src_fld, *dst_flds)
166
+ (src_dst_multi[type] ||= []) << (pair = [src_fld, dst_flds])
167
+ @src_multi[src_fld] = pair
168
+ dst_flds.each do |dst_fld|
169
+ (@dst_multi[dst_fld] ||= []) << pair
170
+ end
171
+ end
172
+
173
+ def multi_delete(*flds)
174
+ flds.each do |fld|
175
+ next unless type_pairs = src_dst_multi[fld.type]
176
+ if pairs = @dst_multi.delete(fld)
177
+ pairs.each do |pair|
178
+ src_fld = pair.first
179
+ dst_flds = pair.last
180
+ dst_flds.delete(fld)
181
+ if dst_flds.empty?
182
+ @src_multi.delete(src_fld)
183
+ type_pairs.delete(pair)
184
+ end
185
+ end
186
+ elsif @src_multi.key?(fld)
187
+ pair = @src_multi.delete(fld)
188
+ type_pairs.delete(pair)
189
+ pair.last.each do |dst_fld|
190
+ pairs = @dst_multi[dst_fld]
191
+ pairs.delete(pair)
192
+ @dst_multi.delete(dst_fld) if pairs.empty?
193
+ end
194
+ end
195
+ src_dst_multi.delete(fld.type) if type_pairs.empty?
196
+ end
197
+ end
198
+
199
+ def by_type(components)
200
+ components.to_a.group_by(&:type).tap do |out|
201
+ EXCLUDED_TYPES.each {|type| out.delete(type)}
202
+ end
203
+ end
204
+
205
+ def src_components
206
+ flds = src.components.to_a
207
+ flds = flds.reject {|fld| ximport?(fld)} if @exclude_ximport
208
+ flds - @src_excluded
209
+ end
210
+
211
+ def dst_components
212
+ flds = dst.components.to_a
213
+ flds = flds.reject {|fld| ximport?(fld)} if @exclude_ximport
214
+ flds - @dst_excluded
215
+ end
216
+
217
+ def ximport?(fld)
218
+ fld && fld.tooltip.to_s.include?("ximport")
219
+ end
220
+
221
+ def reset!
222
+ @src_unpaired = by_type(src_components)
223
+ @dst_unpaired = by_type(dst_components)
224
+ @src_dst_pairs = []
225
+ reset_multi!
226
+ end
227
+
228
+ def reset_multi!
229
+ @src_dst_multi = {}
230
+ @src_multi = {}
231
+ @dst_multi = {}
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'helpers_migration/typed_fields_pairing'
2
+ require_relative 'helpers_migration/copying'
3
+ module Eco
4
+ module API
5
+ class UseCases
6
+ class OozeSamples
7
+ module HelpersMigration
8
+ include OozeSamples::Helpers
9
+ include Copying
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,193 @@
1
+ # Use case to offer the basics to migrate a register by creating new entries
2
+ # @note
3
+ # - You can define methods `filters` and `search` to change the target entries of the register
4
+ # - You can define a json file named after your use case file (i.e. new_training.json) to pair source-destination fields:
5
+ # - This file format expects an object {} where keys are field types (i.e. "select", "plain_text")
6
+ # and the value is an array with either:
7
+ # 1. field label name (String or RegExp body/source),
8
+ # 2. or an object with key-value being source-destination label names (also supports RegExp body/source)
9
+ # - Example: {"select": ["severity of .*:", {"type of event:" => "type of incident:"}]}
10
+ # - You may either
11
+ # 1. define the `process_ooze` method calling super with a block: super.do ||
12
+ # 2. of define the function `custom_processing(draft, source, fields_tracker: nil)`
13
+ # - You can define a callback function, `paired_fields_post_callback(src_fld, dst_fld, pairing_tracking = nil)`
14
+ # - It will be called after copying by using any of the pairing methods, and allows to introduce modifications
15
+ # such as selection option mappings
16
+ # This case expects `options[:source][:register_id]`
17
+ class Eco::API::UseCases::OozeSamples::RegisterMigrationCase < Eco::API::UseCases::OozeSamples::RegisterUpdateCase
18
+ name "register-migration-case"
19
+ type :other
20
+
21
+ #batch_size 2
22
+ #DRY_COUNT = 4
23
+
24
+ #SOURCE_STAGE = 'name_of_source_stage'
25
+ #TEMPLATE_ID = 'id_of_the_template'
26
+
27
+ # whether or not it should try to pair src <-> dst ximport fields:
28
+ EXCLUDE_XIMPORT = false
29
+ # whether or not it should warn on unpaired destination ximport fields:
30
+ REPORT_DST_XIMPORT = true
31
+ # whether or not it should warn on unpaired source fields that are empty (no data loss):
32
+ REPORT_SRC_EMTPY = false
33
+ # whether or not it should warn on unpaired source fields that are deindexed (low potential of data loss):
34
+ REPORT_SRC_DEINDEX = false
35
+
36
+ include Eco::API::UseCases::OozeSamples::HelpersMigration
37
+
38
+ attr_reader :session, :options
39
+ attr_reader :csv
40
+
41
+ def main(session, options, usecase, &block)
42
+ @session = session; @options = options
43
+ if options[:dry_run]
44
+ @csv = []
45
+ super(session, options, usecase, &block)
46
+ else
47
+ CSV.open(out_csv_filename, "w") do |csv|
48
+ @csv = csv
49
+ csv << ["src_page_id", "dst_page_id"]
50
+ super(session, options, usecase, &block)
51
+ end
52
+ logger.info("File '#{out_csv_filename}' has been created.")
53
+ end
54
+ end
55
+
56
+ # This method is expected to finalize the copying/adjustment of the draft entry.
57
+ # @note At this stage, the RegisterMigration case has already populated the draft where
58
+ # source and destination fields(in the draft) could be paired.
59
+ # def custom_processing(draft, source, fields_tracker: nil)
60
+ # draft.tags << source.tags.to_a
61
+ # # More custom logics
62
+ # end
63
+
64
+ def process_ooze
65
+ with_target_stage(self.class::SOURCE_STAGE) do |source|
66
+ drafting_entry(self.class::TEMPLATE_ID) do |draft|
67
+ draft_reference = object_reference(source)
68
+ draft.base_tags << ["IMPORTED", "MIGRATED"]
69
+
70
+ pairing_tracking = copy_pairings(source, draft)
71
+
72
+ if respond_to?(:custom_processing, true)
73
+ custom_processing(draft, source, fields_tracker: pairing_tracking)
74
+ elsif block_given?
75
+ yield(draft, source, fields_tracker: pairing_tracking)
76
+ end
77
+
78
+ if msg = pairing_tracking.report_src_unpaired(only_present: !self.class::REPORT_SRC_EMTPY, only_indexed: !self.class::REPORT_SRC_DEINDEX)
79
+ logger.warn(msg)
80
+ end
81
+
82
+ if msg = pairing_tracking.report_multi_pairs(only_present: !self.class::REPORT_SRC_EMTPY, only_indexed: !self.class::REPORT_SRC_DEINDEX, dst_exclude_ximport: !self.class::REPORT_DST_XIMPORT)
83
+ logger.warn(msg)
84
+ end
85
+
86
+ if msg = pairing_tracking.report_dst_unpaired(only_required: false, exclude_ximport: !self.class::REPORT_DST_XIMPORT)
87
+ logger.warn(msg)
88
+ end
89
+
90
+ if page_id = create_entry(draft, reference: draft_reference)
91
+ csv << [source.id, page_id]
92
+ logger.info("Page '#{page_id}' created successfully.")
93
+ elsif options.dig(:dry_run)
94
+ logger.info("Simulated launch for #{draft_reference}")
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def copy_pairings(src, dst)
103
+ after_copy = callback(:paired_fields_post)
104
+ copy_generic_paired_fields(src, dst, exclude_ximport: self.class::EXCLUDE_XIMPORT, &after_copy).tap do |pairing_tracking|
105
+ copy_mapped_fields(src, dst, fields_tracker: pairing_tracking, &after_copy)
106
+ end
107
+ end
108
+
109
+ def script_fullname
110
+ msg = "You should define this method in the child class with this content:\n"
111
+ msg << " @script_fullname||= File.expand_path(__FILE__)"
112
+ raise msg
113
+ end
114
+
115
+ def copy_mapped_fields(src, dst, fields_tracker: nil, &after_copy)
116
+ return [] unless field_maps?
117
+ field_maps.each do |type, field_hooks|
118
+ field_hooks.each do |hook_name|
119
+ copy_hooked_fields(src, dst, hook: hook_name, type: type, fields_tracker: fields_tracker, &after_copy)
120
+ end
121
+ end
122
+ end
123
+
124
+ def callback?(sym)
125
+ respond_to?(callback_name(sym), true)
126
+ end
127
+
128
+ def callback(sym)
129
+ if callback?(sym)
130
+ method(callback_name(sym))
131
+ end
132
+ end
133
+
134
+ def callback_name(sym)
135
+ "#{sym}_callback".to_sym
136
+ end
137
+
138
+ def create_entry(draft, reference:, from: self.class::TEMPLATE_ID)
139
+ with_rescue(reference) do
140
+ if !options[:dry_run]
141
+ create_ooze(draft, template_id: from).page_id
142
+ else
143
+ dry_run_feedback(draft)
144
+ false
145
+ end
146
+ end
147
+ end
148
+
149
+ def drafting_entry(template_id = selfTEMPLATE_ID)
150
+ draft = apiv2.pages.get_new(template_id)
151
+ yield(draft)
152
+ end
153
+
154
+ def with_target_stage(name)
155
+ if stg = target_stage(name)
156
+ yield(stg)
157
+ else
158
+ logger.warn("Entry '#{target.id}' does not have stage '#{name}'")
159
+ end
160
+ end
161
+
162
+ def target_stage(name)
163
+ stage(name)
164
+ end
165
+
166
+ def out_csv_filename
167
+ @out_csv_filename ||= File.expand_path("./#{session.config.active_enviro}_#{script_basename}_#{name_timestamp}.csv")
168
+ end
169
+
170
+ def name_timestamp
171
+ @name_timestamp ||= Time.now.strftime("%Y%m%d%H%M%S")
172
+ end
173
+
174
+ def field_maps
175
+ @field_maps ||= field_maps? && JSON.parse(File.read(field_maps_file))
176
+ end
177
+
178
+ def field_maps?
179
+ File.exist?(field_maps_file)
180
+ end
181
+
182
+ def field_maps_file
183
+ @field_maps_file ||= "#{File.join(script_dirname, script_basename)}.json"
184
+ end
185
+
186
+ def script_basename
187
+ @script_basename ||= File.basename(script_fullname, "_case.rb")
188
+ end
189
+
190
+ def script_dirname
191
+ @script_dirname ||= File.dirname(script_fullname)
192
+ end
193
+ end
@@ -19,7 +19,7 @@ class Eco::API::UseCases::OozeSamples::RegisterUpdateCase < Eco::API::UseCases::
19
19
 
20
20
  attr_reader :retrieved_oozes, :non_retrieved_oozes
21
21
  attr_reader :total_search_oozes, :dupped_search_oozes
22
- attr_reader :ooze_result_ids, :updated_oozes, :failed_update_oozes
22
+ attr_reader :ooze_result_ids, :updated_oozes, :failed_update_oozes, :created_oozes
23
23
 
24
24
  def main(session, options, usecase, &block)
25
25
  @retrieved_oozes = 0
@@ -28,6 +28,7 @@ class Eco::API::UseCases::OozeSamples::RegisterUpdateCase < Eco::API::UseCases::
28
28
  @ooze_result_ids = {}
29
29
  @updated_oozes = 0
30
30
  @failed_update_oozes = 0
31
+ @created_oozes = 0
31
32
 
32
33
  super(session, options, usecase) do
33
34
  with_each_entry do |ooze|
@@ -41,6 +42,7 @@ class Eco::API::UseCases::OozeSamples::RegisterUpdateCase < Eco::API::UseCases::
41
42
  msg += " • Could not get #{non_retrieved_oozes} oozes.\n"
42
43
  msg += " • Updated #{updated_oozes} oozes.\n"
43
44
  msg += " - Failed update on #{failed_update_oozes} oozes.\n"
45
+ msg += " • Created #{created_oozes} oozes.\n"
44
46
  logger.info(msg)
45
47
  end
46
48
 
@@ -50,6 +52,29 @@ class Eco::API::UseCases::OozeSamples::RegisterUpdateCase < Eco::API::UseCases::
50
52
 
51
53
  private
52
54
 
55
+ def with_rescue(reference)
56
+ begin
57
+ yield
58
+ rescue StandardError => e
59
+ logger.error([reference, e.message].join(' => \n'))
60
+ lines = []
61
+ lines << '\nThere was an error. Choose one option:\n'
62
+ lines << ' (C) - Continue/resume. Just ignore this one.'
63
+ lines << ' (A) - Abort. Just break this run.'
64
+
65
+ session.prompt_user('Type one option (C/a):', explanation: lines.join('\n'), default: 'C') do |res|
66
+ res = res.upcase
67
+ case
68
+ when res.start_with?("A")
69
+ raise
70
+ else res.start_with?("C")
71
+ logger.warn "Script resumed after error..."
72
+ nil
73
+ end
74
+ end
75
+ end
76
+ end
77
+
53
78
  def before_loading_new_target(ooze_id)
54
79
  if pending = queue_shift(ooze_id)
55
80
  update_ooze(pending).tap do |result|
@@ -148,6 +173,12 @@ class Eco::API::UseCases::OozeSamples::RegisterUpdateCase < Eco::API::UseCases::
148
173
  yield(results) unless results.empty?
149
174
  end
150
175
 
176
+ def create_ooze(draft, template_id:)
177
+ apiv2.pages.create(draft, from: template_id).tap do |result|
178
+ @created_oozes += 1
179
+ end
180
+ end
181
+
151
182
  def update_oozes(batched_oozes = batch_queue)
152
183
  batched_oozes.each do |ooze|
153
184
  update_ooze(ooze).tap do |result|
@@ -9,6 +9,7 @@ module Eco
9
9
  end
10
10
 
11
11
  require_relative 'ooze_samples/helpers'
12
+ require_relative 'ooze_samples/helpers_migration'
12
13
  require_relative 'ooze_samples/ooze_base_case'
13
14
  require_relative 'ooze_samples/register_export_case'
14
15
  require_relative 'ooze_samples/ooze_run_base_case'
@@ -16,3 +17,4 @@ require_relative 'ooze_samples/ooze_update_case'
16
17
  require_relative 'ooze_samples/ooze_from_doc_case'
17
18
  require_relative 'ooze_samples/register_update_case'
18
19
  require_relative 'ooze_samples/target_oozes_update_case'
20
+ require_relative 'ooze_samples/register_migration_case'
data/lib/eco/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = "2.1.4"
2
+ VERSION = "2.1.6"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eco-helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura
@@ -136,7 +136,7 @@ dependencies:
136
136
  requirements:
137
137
  - - ">="
138
138
  - !ruby/object:Gem::Version
139
- version: 0.9.3
139
+ version: 0.9.5
140
140
  - - "<"
141
141
  - !ruby/object:Gem::Version
142
142
  version: '0.10'
@@ -146,7 +146,7 @@ dependencies:
146
146
  requirements:
147
147
  - - ">="
148
148
  - !ruby/object:Gem::Version
149
- version: 0.9.3
149
+ version: 0.9.5
150
150
  - - "<"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0.10'
@@ -597,11 +597,15 @@ files:
597
597
  - lib/eco/api/usecases/ooze_samples/helpers/filters.rb
598
598
  - lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb
599
599
  - lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb
600
+ - lib/eco/api/usecases/ooze_samples/helpers_migration.rb
601
+ - lib/eco/api/usecases/ooze_samples/helpers_migration/copying.rb
602
+ - lib/eco/api/usecases/ooze_samples/helpers_migration/typed_fields_pairing.rb
600
603
  - lib/eco/api/usecases/ooze_samples/ooze_base_case.rb
601
604
  - lib/eco/api/usecases/ooze_samples/ooze_from_doc_case.rb
602
605
  - lib/eco/api/usecases/ooze_samples/ooze_run_base_case.rb
603
606
  - lib/eco/api/usecases/ooze_samples/ooze_update_case.rb
604
607
  - lib/eco/api/usecases/ooze_samples/register_export_case.rb
608
+ - lib/eco/api/usecases/ooze_samples/register_migration_case.rb
605
609
  - lib/eco/api/usecases/ooze_samples/register_update_case.rb
606
610
  - lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb
607
611
  - lib/eco/api/usecases/use_case.rb