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 +4 -4
- data/CHANGELOG.md +15 -1
- data/eco-helpers.gemspec +1 -1
- data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +61 -19
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/copying.rb +125 -0
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/typed_fields_pairing.rb +235 -0
- data/lib/eco/api/usecases/ooze_samples/helpers_migration.rb +14 -0
- data/lib/eco/api/usecases/ooze_samples/register_migration_case.rb +193 -0
- data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +32 -1
- data/lib/eco/api/usecases/ooze_samples.rb +2 -0
- data/lib/eco/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eaeec07a9cf6c0b584a546d6626f661d73f608332a72ef938bcfe7513adc6f2c
|
4
|
+
data.tar.gz: 285234b6c42a281c900ae3c05dc5a71f9e6a55d4aa52a884457a6956a02c456e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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
|
-
#
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
when
|
27
|
-
|
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
|
-
|
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
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
|
+
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.
|
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.
|
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
|