eco-helpers 2.1.4 → 2.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca676558c0e1a885b1f9e15d68e3c4f3688b90713c88182854b426a8ce1bf447
4
- data.tar.gz: 41e8693e90664d7789040dd2dab66521e330693a9ae62f7bb5c5bc2dbd940911
3
+ metadata.gz: eaeec07a9cf6c0b584a546d6626f661d73f608332a72ef938bcfe7513adc6f2c
4
+ data.tar.gz: 285234b6c42a281c900ae3c05dc5a71f9e6a55d4aa52a884457a6956a02c456e
5
5
  SHA512:
6
- metadata.gz: 65b5157cc78ff3016f932283e2f0504e46681e15dc2acd902bb0e6188af98f5fe5271ad0db49a48fe969c642df3146daffeffcb0008dda3012433450dbbc1e87
7
- data.tar.gz: 3a23f58986631f819b18bd8395ec92c321e9cb21bfde172a3e565df309cdad3b759d1d60f8c4bf52cff29332b49607f30afad19c90a80c5f9fb974637c3b218e
6
+ metadata.gz: bc26467b2cb2b102a03aff3e5c189c60d3d63290c107e69c609e5536e70631e50e1a2c21c5859260e048c132584288fc728fd185681df4540a3c67e822f581dc
7
+ data.tar.gz: 0a5a23a848dac8114d1945efac8f58508066de077cac286e4b6bd473965356ce2aa0b961b1b2e542183afb58f04b1ceeb441f14452e0477976b565c9ff414a51
data/CHANGELOG.md CHANGED
@@ -1,12 +1,26 @@
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.6] - 2022-10-xx
5
5
 
6
6
  ### Added
7
7
  ### Changed
8
8
  ### Fixed
9
9
 
10
+ ## [2.1.5] - 2022-10-xx
11
+
12
+ ### Added
13
+ - `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` added creation count
14
+ - Requires use of `#create_ooze(draft, template_id)`
15
+ - `Eco::API::UseCases::OozeSamples::RegisterUpdateCase` added method
16
+ - `with_rescue` that prompts user to continue
17
+ - this is for usage in child classes
18
+ - new use case and helpers thereof `Eco::API::UseCases::OozeSamples::RegisterMigrationCase`
19
+
20
+ ### Changed
21
+ - upgraded `ecoportal-api-v2` **gem**
22
+ - **refactored** `Eco::API::UseCases::OozeSamples::Helpers::Shortcuts#same_string?`
23
+
10
24
  ## [2.1.4] - 2022-10-20
11
25
 
12
26
  ### 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
 
@@ -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(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.5"
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.5
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