eco-helpers 2.6.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +95 -0
  4. data/CHANGELOG.md +135 -2
  5. data/Rakefile +13 -7
  6. data/eco-helpers.gemspec +3 -3
  7. data/lib/eco/api/common/loaders/base.rb +2 -2
  8. data/lib/eco/api/common/loaders/case_base.rb +1 -1
  9. data/lib/eco/api/common/loaders/config/workflow/mailer.rb +5 -5
  10. data/lib/eco/api/common/loaders/error_handler.rb +8 -5
  11. data/lib/eco/api/common/loaders/parser.rb +44 -22
  12. data/lib/eco/api/common/loaders/policy.rb +6 -4
  13. data/lib/eco/api/common/loaders/use_case.rb +13 -7
  14. data/lib/eco/api/common/people/base_parser.rb +0 -2
  15. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +0 -1
  16. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +1 -1
  17. data/lib/eco/api/common/people/default_parsers/date_parser.rb +64 -12
  18. data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +0 -1
  19. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +13 -5
  20. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +0 -1
  21. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +18 -5
  22. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +8 -8
  23. data/lib/eco/api/common/people/default_parsers/select_parser.rb +50 -26
  24. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -6
  25. data/lib/eco/api/common/people/default_parsers/xls_parser.rb +9 -12
  26. data/lib/eco/api/common/people/default_parsers.rb +1 -12
  27. data/lib/eco/api/common/people/entries.rb +13 -13
  28. data/lib/eco/api/common/people/entry_factory.rb +76 -45
  29. data/lib/eco/api/common/people/person_attribute_parser.rb +8 -12
  30. data/lib/eco/api/common/people/person_entry.rb +86 -75
  31. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +60 -44
  32. data/lib/eco/api/common/people/person_factory.rb +30 -22
  33. data/lib/eco/api/common/people/person_modifier.rb +11 -13
  34. data/lib/eco/api/common/people/person_parser.rb +101 -39
  35. data/lib/eco/api/common/people/supervisor_helpers.rb +25 -26
  36. data/lib/eco/api/common/session/base_session.rb +9 -9
  37. data/lib/eco/api/common/session/environment.rb +7 -5
  38. data/lib/eco/api/common/session/sftp.rb +59 -32
  39. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +10 -6
  40. data/lib/eco/api/common/version_patches/exception.rb +11 -13
  41. data/lib/eco/api/error.rb +32 -20
  42. data/lib/eco/api/microcases/set_supervisor.rb +0 -3
  43. data/lib/eco/api/organization/node_classifications.rb +82 -0
  44. data/lib/eco/api/organization/policy_groups.rb +4 -6
  45. data/lib/eco/api/organization/tag_tree.rb +169 -93
  46. data/lib/eco/api/organization.rb +1 -0
  47. data/lib/eco/api/session/batch/job.rb +1 -1
  48. data/lib/eco/api/session/config/tagtree.rb +41 -23
  49. data/lib/eco/api/session/config/workflow.rb +113 -88
  50. data/lib/eco/api/session/config.rb +6 -0
  51. data/lib/eco/api/session.rb +51 -29
  52. data/lib/eco/api/usecases/base_io.rb +28 -25
  53. data/lib/eco/api/usecases/default/locations/cli/tagtree_extract_cli.rb +7 -2
  54. data/lib/eco/api/usecases/default/locations/cli/tagtree_upload_cli.rb +21 -0
  55. data/lib/eco/api/usecases/default/locations/csv_to_tree_case.rb +3 -3
  56. data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +54 -23
  57. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +87 -0
  58. data/lib/eco/api/usecases/default/locations.rb +1 -0
  59. data/lib/eco/api/usecases/default/people/analyse_people_case.rb +60 -56
  60. data/lib/eco/api/usecases/default/people/change_email_case.rb +8 -9
  61. data/lib/eco/api/usecases/default/people/clean_unknown_tags_case.rb +13 -11
  62. data/lib/eco/api/usecases/default/people/clear_abilities_case.rb +2 -2
  63. data/lib/eco/api/usecases/default/people/org_data_convert_case.rb +25 -27
  64. data/lib/eco/api/usecases/default/people/refresh_case.rb +2 -2
  65. data/lib/eco/api/usecases/default/people/reinvite_trans_case.rb +1 -1
  66. data/lib/eco/api/usecases/default/people/reinvite_trans_cli.rb +0 -1
  67. data/lib/eco/api/usecases/default/people/restore_db_case.rb +39 -34
  68. data/lib/eco/api/usecases/default/people/set_default_tag_case.rb +19 -15
  69. data/lib/eco/api/usecases/default/people/supers_cyclic_identify_case.rb +16 -12
  70. data/lib/eco/api/usecases/default_cases/hris_case.rb +17 -15
  71. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +30 -16
  72. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +0 -2
  73. data/lib/eco/api/usecases/graphql/base.rb +5 -3
  74. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +4 -1
  75. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +14 -0
  76. data/lib/eco/api/usecases/graphql/helpers/base.rb +5 -4
  77. data/lib/eco/api/usecases/graphql/helpers/location/base/classifications_parser.rb +60 -0
  78. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +72 -0
  79. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +25 -59
  80. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +59 -0
  81. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/compare.rb +49 -0
  82. data/lib/eco/api/usecases/graphql/helpers/location/command/diff.rb +11 -0
  83. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +46 -0
  84. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +23 -0
  85. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_unarchive.rb +65 -0
  86. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable.rb +49 -0
  87. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable/relation_safe_sort.rb +119 -0
  88. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable.rb +59 -0
  89. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages.rb +82 -0
  90. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs.rb +20 -0
  91. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +84 -0
  92. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +4 -4
  93. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +24 -12
  94. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +21 -24
  95. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_map.rb +1 -1
  96. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_set.rb +10 -11
  97. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap.rb +8 -9
  98. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +41 -12
  99. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +11 -80
  100. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +89 -0
  101. data/lib/eco/api/usecases/graphql/samples/location/command/service.rb +6 -0
  102. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +89 -0
  103. data/lib/eco/api/usecases/graphql/samples/location/command.rb +3 -0
  104. data/lib/eco/api/usecases/graphql/samples/location/service/base.rb +9 -0
  105. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/heading.rb +18 -0
  106. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +53 -0
  107. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/classifications.rb +34 -0
  108. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/helpers.rb +28 -0
  109. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +46 -0
  110. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible.rb +38 -0
  111. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +105 -0
  112. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/discarded.rb +16 -0
  113. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +15 -0
  114. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/node_attr_maps.rb +22 -0
  115. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +45 -0
  116. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +36 -0
  117. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/output.rb +56 -0
  118. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list.rb +41 -0
  119. data/lib/eco/api/usecases/graphql/samples/location/service.rb +8 -0
  120. data/lib/eco/api/usecases/graphql/samples/location.rb +1 -0
  121. data/lib/eco/api/usecases/graphql/utils/sftp.rb +96 -36
  122. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +8 -6
  123. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +4 -3
  124. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +39 -25
  125. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +13 -15
  126. data/lib/eco/api/usecases/ooze_samples/helpers/filters.rb +50 -21
  127. data/lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb +21 -11
  128. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +2 -0
  129. data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +49 -43
  130. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +17 -19
  131. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +48 -43
  132. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +34 -34
  133. data/lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb +8 -10
  134. data/lib/eco/api/usecases.rb +0 -1
  135. data/lib/eco/cli/config/use_cases.rb +31 -29
  136. data/lib/eco/cli_default/input_filters.rb +0 -5
  137. data/lib/eco/cli_default/people_filters.rb +4 -4
  138. data/lib/eco/cli_default/workflow.rb +13 -14
  139. data/lib/eco/csv/table.rb +34 -25
  140. data/lib/eco/data/hashes/array_diff.rb +24 -35
  141. data/lib/eco/data/hashes/diff_result/meta.rb +131 -0
  142. data/lib/eco/data/hashes/diff_result.rb +65 -57
  143. data/lib/eco/data/hashes/sanke_camel_indifferent_access.rb +278 -0
  144. data/lib/eco/data/hashes.rb +1 -1
  145. data/lib/eco/data/locations/convert.rb +1 -1
  146. data/lib/eco/data/locations/node_base/csv_convert.rb +19 -9
  147. data/lib/eco/data/locations/node_base/parsing.rb +4 -2
  148. data/lib/eco/data/locations/node_base/treeify.rb +149 -132
  149. data/lib/eco/data/locations/node_base.rb +15 -4
  150. data/lib/eco/data/locations/node_diff/accessors.rb +13 -5
  151. data/lib/eco/data/locations/node_diff/nodes_diff/clustered_treeify.rb +101 -0
  152. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +99 -0
  153. data/lib/eco/data/locations/node_diff/{selectors.rb → nodes_diff/selectors.rb} +1 -1
  154. data/lib/eco/data/locations/node_diff/nodes_diff.rb +50 -35
  155. data/lib/eco/data/locations/node_diff.rb +45 -17
  156. data/lib/eco/data/locations/node_level/parsing.rb +15 -21
  157. data/lib/eco/data/locations/node_level.rb +66 -22
  158. data/lib/eco/data/locations/node_plain/parsing.rb +1 -1
  159. data/lib/eco/data/locations/node_plain.rb +60 -7
  160. data/lib/eco/data/strings/camel_case.rb +35 -0
  161. data/lib/eco/data/strings/snake_case.rb +18 -0
  162. data/lib/eco/data/strings.rb +8 -0
  163. data/lib/eco/data.rb +1 -0
  164. data/lib/eco/language/methods/call_detector.rb +11 -0
  165. data/lib/eco/language/methods/dsl_able.rb +7 -1
  166. data/lib/eco/language/methods.rb +2 -1
  167. data/lib/eco/language/models/collection.rb +23 -25
  168. data/lib/eco/language/models/parser_serializer.rb +24 -5
  169. data/lib/eco/version.rb +1 -1
  170. data/lib/eco-helpers.rb +0 -1
  171. metadata +54 -9
  172. data/lib/eco/data/hashes/diff_meta.rb +0 -52
@@ -6,23 +6,27 @@ module Eco
6
6
  # This does not allow to capture the `name` and `id` of the locations
7
7
  # structure itself into the json storing model.
8
8
  class TagTree
9
- HEADER = [
10
- 'id', 'name', 'weight', 'parent_id',
11
- 'archived', 'archived_token'
9
+ HEADER = %w[
10
+ id name weight parent_id
11
+ archived archived_token
12
+ classifications classification_names
13
+ level
12
14
  ].freeze
13
15
 
16
+ include Enumerable
17
+
18
+ attr_reader :source
19
+
14
20
  attr_accessor :id
15
21
  alias_method :tag, :id
22
+
16
23
  attr_accessor :name, :weight
17
24
  attr_accessor :archived, :archived_token
25
+ attr_accessor :classifications, :classification_names
18
26
 
19
27
  attr_reader :parent
20
- attr_reader :nodes, :children_count
21
- attr_reader :depth, :path
22
-
23
- attr_reader :source
24
-
25
- include Enumerable
28
+ attr_reader :nodes
29
+ attr_reader :depth
26
30
 
27
31
  # @example Node format:
28
32
  # {"tag": "NODE NAME", "nodes": subtree}
@@ -34,33 +38,32 @@ module Eco
34
38
  # ]}]
35
39
  # tree = TagTree.new(tree.to_json)
36
40
  # @param tagtree [String] representation of the tagtree in json.
37
- def initialize(tagtree = [], name: nil, id: nil, depth: -1, path: [], parent: nil, _weight: nil)
38
- @depth = depth
39
- @parent = parent
41
+ def initialize(
42
+ tagtree = [],
43
+ name: nil, id: nil,
44
+ depth: -1, path: [],
45
+ parent: nil, _weight: nil # rubocop:disable Lint/UnderscorePrefixedVariableName
46
+ )
40
47
 
41
- case tagtree
42
- when String
43
- @source = JSON.parse(tagtree)
44
- else
45
- @source = tagtree
46
- end
47
- raise ArgumentError, "You are trying to initialize a TagTree with a null tagtree" if !@source
48
+ @source = parse_source_input(tagtree)
49
+
50
+ msg = "You are trying to initialize a TagTree with a null tagtree"
51
+ raise ArgumentError, msg unless source
52
+
53
+ @parent = parent
54
+ @depth = depth
55
+ @path = path || []
48
56
 
49
- if @source.is_a?(Array)
57
+ if source.is_a?(Array)
50
58
  @id = id
51
59
  @name = name
52
- @raw_nodes = @source
60
+ @raw_nodes = source
53
61
  else
54
- @id = @source.values_at('tag', 'id').compact.first&.upcase
55
- @name = @source['name']
56
- @archived = @source['archived'] || false
57
- @archived_token = @source['archived_token']
58
- @source['weight'] = @weight = @source['weight'] || _weight
59
- @raw_nodes = @source['nodes'] || []
62
+ source['weight'] ||= _weight
63
+ init_node
60
64
  end
61
65
 
62
- @path = path || []
63
- @path.push(@id) unless top?
66
+ self.path.push(self.id) unless top?
64
67
 
65
68
  @nodes = @raw_nodes.map.with_index do |cnode, idx|
66
69
  self.class.new(cnode, depth: depth + 1, path: @path.dup, parent: self, _weight: idx)
@@ -84,18 +87,19 @@ module Eco
84
87
  end
85
88
 
86
89
  # @return [Array] with the differences
87
- def diff(tagtree, differences: {}, level: 0, **options)
90
+ def diff(tagtree, _differences: {}, _level: 0, **options)
88
91
  require 'hashdiff'
89
- Hashdiff.diff(self.as_json, tagtree.as_json, **options.slice(:array_path, :similarity, :use_lcs))
92
+ Hashdiff.diff(as_json, tagtree.as_json, **options.slice(:array_path, :similarity, :use_lcs))
90
93
  end
91
94
 
92
95
  # It generates a merged tagtree out of two sources
93
96
  # @note it merges the first level nodes (and their children) as it comes
94
97
  # @return [Eco::API::Organization::TagTree] result of merging both trees
95
98
  def merge(other)
96
- raise ArgumentError, "Expecting Eco::API::Organization::TagTree. Given: #{other.class}" unless other.is_a?(Eco::API::Organization::TagTree)
97
- mid = [self.id, other.id].join('|')
98
- mname = [self.name, other.name].join('|')
99
+ msg = "Expecting Eco::API::Organization::TagTree. Given: #{other.class}"
100
+ raise ArgumentError, msg unless other.is_a?(Eco::API::Organization::TagTree)
101
+ mid = [id, other.id].join('|')
102
+ mname = [name, other.name].join('|')
99
103
  self.class.new(as_json | other.as_json, id: mid, name: mname)
100
104
  end
101
105
 
@@ -187,7 +191,11 @@ module Eco
187
191
  # @param include_archived [Boolean] whether it should include archived nodes.
188
192
  # @param max_depth [Boolean] up to what level `depth` nodes should be included.
189
193
  # @return [Array[Hash]] where `Hash` is a `node` (i.e. `{"tag" => TAG, "nodes": Array[Hash]}`)
190
- def as_json(include_children: true, include_archived: true, max_depth: total_depth, &block)
194
+ def as_json( # rubocop:disable Metrics/AbcSize
195
+ include_children: true, include_archived: true,
196
+ max_depth: total_depth,
197
+ &block
198
+ )
191
199
  max_depth ||= total_depth
192
200
  return nil if max_depth < depth
193
201
  return [] if top? && !include_children
@@ -202,17 +210,22 @@ module Eco
202
210
  max_depth: max_depth
203
211
  }
204
212
  children_json = child_nodes.map {|nd| nd.as_json(**kargs, &block)}.compact
205
- end
206
213
 
207
- if top?
208
- children_json
209
- else
210
- values = [id, name, weight, parent_id, archived, archived_token]
211
- node_json = self.class::HEADER.zip(values).to_h
212
- node_json["nodes"] = children_json if include_children
213
- node_json = yield(node_json, self) if block_given?
214
- node_json
214
+ return children_json if top?
215
215
  end
216
+
217
+ values = [
218
+ id, name,
219
+ weight, parent_id,
220
+ archived, archived_token,
221
+ classifications.dup,
222
+ classification_names.dup,
223
+ depth + 1
224
+ ]
225
+ node_json = self.class::HEADER.zip(values).to_h
226
+ node_json["nodes"] = children_json if include_children
227
+ node_json = yield(node_json, self) if block_given?
228
+ node_json
216
229
  end
217
230
 
218
231
  # Returns a plain list form of hash nodes.
@@ -233,11 +246,9 @@ module Eco
233
246
 
234
247
  # @return [Integer] the highest `depth` of all the children.
235
248
  def total_depth
236
- @total_depth ||= if has_children?
237
- deepest_node = nodes.max_by do |nd|
238
- nd.total_depth
239
- end
240
- deepest_node.total_depth
249
+ @total_depth ||=
250
+ if children?
251
+ nodes.max_by(&:total_depth)&.total_depth
241
252
  else
242
253
  depth
243
254
  end
@@ -245,20 +256,21 @@ module Eco
245
256
 
246
257
  # @return [Integer] if there's only top level.
247
258
  def flat?
248
- self.total_depth <= 0
259
+ total_depth <= 0
249
260
  end
250
261
 
251
262
  # Gets all the tags of the current node tree.
252
263
  # @note
253
264
  # - this will include the upper level tag(s) as well
254
265
  # - to get all but the upper level tag(s) use `subtags` method instead
255
- # @param depth [Integer] if empty, returns the list of tag nodes of that level. Otherwise the list of tag nodes of the entire subtree.
266
+ # @param depth [Integer] if empty, returns the list of tag nodes of that level.
267
+ # Otherwise the list of tag nodes of the entire subtree.
256
268
  # @return [Array<String>]
257
269
  def tags(depth: nil)
258
- if !depth || depth < 0
270
+ if !depth || depth&.negative?
259
271
  @hash_tags.keys
260
272
  else
261
- @hash_tags.select do |t, n|
273
+ @hash_tags.select do |_t, n|
262
274
  n.depth == depth
263
275
  end.keys
264
276
  end
@@ -280,8 +292,8 @@ module Eco
280
292
  # Returns all the tags with no children
281
293
  # @return [Array<String>]
282
294
  def leafs
283
- tags.select do |tag|
284
- !node(tag).has_children?
295
+ tags.reject do |tag|
296
+ node(tag).children?
285
297
  end
286
298
  end
287
299
 
@@ -291,8 +303,8 @@ module Eco
291
303
  end
292
304
 
293
305
  # @return [Boolean] it has subnodes
294
- def has_children?
295
- children_count > 0
306
+ def children?
307
+ children_count&.positive?
296
308
  end
297
309
 
298
310
  # Verifies if a tag exists in the tree.
@@ -314,7 +326,7 @@ module Eco
314
326
  # @param list [Array<String>] source tags.
315
327
  # @return [Array<String>]
316
328
  def filter_tags(list)
317
- return [] unless list && list.is_a?(Array)
329
+ return [] unless list.is_a?(Array)
318
330
  list.select {|str| tag?(str)}
319
331
  end
320
332
 
@@ -324,7 +336,7 @@ module Eco
324
336
  # @param key [String] tag to find the path to.
325
337
  # @return [Array<String>]
326
338
  def path(key = nil)
327
- return @path.dup if !key
339
+ return @path.dup unless key
328
340
  @hash_paths[key.upcase].dup
329
341
  end
330
342
 
@@ -341,24 +353,32 @@ module Eco
341
353
  # original = ["SYDNEY", "RISK"]
342
354
  # final = ["MELBOURNE", "EVENT"]
343
355
  #
344
- # tree.user_tags(initial: original, final: final) # out: ["MELBOURNE", "RISK"]
345
- # tree.user_tags(initial: original, final: final, preserve_custom: false) # out: ["MELBOURNE"]
346
- # tree.user_tags(initial: original, final: final, add_custom: true) # out: ["MELBOURNE", "RISK", "EVENT"]
347
- # tree.user_tags(initial: original, final: final, preserve_custom: false, add_custom: true) # out: ["MELBOURNE", "EVENT"]
356
+ # tree.user_tags(initial: original, final: final)
357
+ # # out: ["MELBOURNE", "RISK"]
358
+ # tree.user_tags(initial: original, final: final, preserve_custom: false)
359
+ # # out: ["MELBOURNE"]
360
+ # tree.user_tags(initial: original, final: final, add_custom: true)
361
+ # # out: ["MELBOURNE", "RISK", "EVENT"]
362
+ # tree.user_tags(initial: original, final: final, preserve_custom: false, add_custom: true)
363
+ # # out: ["MELBOURNE", "EVENT"]
348
364
  #
349
365
  # @param initial [Array<String>] original tags a person has in their account.
350
- # @param final [Array<String>] target tags the person should have in their account afterwards.
351
- # @param preserve_custom [Boolean] indicates if original tags that are not in the tree should be added/preserved.
352
- # @param add_custom [Boolean] indicates if target tags that are not in the tree should be really added.
366
+ # @param final [Array<String>] target tags the person should have in
367
+ # their account afterwards.
368
+ # @param preserve_custom [Boolean] indicates if original tags
369
+ # that are not in the tree should be added/preserved.
370
+ # @param add_custom [Boolean] indicates if target tags that are
371
+ # not in the tree should be really added.
353
372
  # @return [Array<String>] with the treated final tags.
354
373
  def user_tags(initial: [], final: [], preserve_custom: true, add_custom: false)
355
374
  initial = [initial].flatten.compact
356
375
  final = [final].flatten.compact
357
376
  raise "Expected Array for initial: and final:" unless initial.is_a?(Array) && final.is_a?(Array)
358
- final = filter_tags(final) unless add_custom
359
- custom = initial - filter_tags(initial)
360
- final = final + custom if preserve_custom
361
- new_tags = final - initial
377
+
378
+ final = filter_tags(final) unless add_custom
379
+ custom = initial - filter_tags(initial)
380
+ final |= custom if preserve_custom
381
+ new_tags = final - initial
362
382
  # keep same order as they where
363
383
  (initial & final) + new_tags
364
384
  end
@@ -372,52 +392,108 @@ module Eco
372
392
  # @param [Array<String>] values list of tags.
373
393
  # @return [String] default tag.
374
394
  def default_tag(*values)
375
- values = filter_tags(values)
376
- nodes = []; ddepth = -1
377
- values.each do |tag|
378
- raise("Couldn't find the node of #{tag} in the tag-tree definition") unless cnode = node(tag)
379
-
380
- if cnode.depth > ddepth
381
- nodes = [cnode]
382
- ddepth = cnode.depth
383
- elsif cnode.depth == ddepth
384
- nodes.push(cnode)
395
+ default_tag = nil
396
+ values = filter_tags(values)
397
+ tnodes, ddepth = get_deepest_nodes_among_tags(*values)
398
+
399
+ unless tnodes.empty?
400
+ common = tnodes.reduce(tags.reverse) do |com, cnode|
401
+ com & cnode.path.reverse
385
402
  end
403
+ default_tag = common.first if common.any? && ddepth&.positive?
386
404
  end
387
405
 
388
- default_tag = nil
389
- if nodes.length > 1
390
- common = nodes.reduce(self.tags.reverse) {|com, cnode| com & cnode.path.reverse}
391
- default_tag = common.first if common.length > 0 && ddepth > 0
392
- end
393
- default_tag ||= nodes.first&.tag if (ddepth > 0) || flat?
406
+ default_tag ||= tnodes.first&.tag if ddepth&.positive? || flat?
394
407
  default_tag
395
408
  end
396
409
 
397
410
  protected
398
411
 
412
+ attr_reader :hash_paths
413
+
399
414
  def hash
400
415
  @hash_tags
401
416
  end
402
417
 
403
- def hash_paths
404
- @hash_paths
418
+ private
419
+
420
+ def parse_source_input(value)
421
+ return value unless value.is_a?(String)
422
+ JSON.parse(value)
405
423
  end
406
424
 
407
- private
425
+ def init_node
426
+ return if source.is_a?(Array)
427
+ @id = source.values_at('tag', 'id').compact.first&.upcase
428
+ @name = source['name']
429
+ @weight = source['weight']
430
+ @archived = as_boolean(source['archived'])
431
+ @archived_token = source['archived_token']
432
+ @classifications = into_a(source['classifications']).map do |value|
433
+ treat_classication(value)
434
+ end
435
+ @classification_names = into_a(source['classification_names'])
436
+ @raw_nodes = source['nodes'] || []
437
+ end
408
438
 
409
439
  def init_hashes
410
- @hash_tags = {}
440
+ @hash_tags = {}
411
441
  @hash_tags[@id] = self unless top?
412
- @hash_tags = nodes.reduce(@hash_tags) do |h,n|
442
+ @hash_tags = nodes.reduce(@hash_tags) do |h, n|
413
443
  h.merge(n.hash)
414
444
  end
415
- @hash_paths = {}
445
+ @hash_paths = {}
416
446
  @hash_paths[@id] = @path unless top?
417
- @hash_paths = nodes.reduce(@hash_paths) do |h,n|
447
+ @hash_paths = nodes.reduce(@hash_paths) do |h, n|
418
448
  h.merge(n.hash_paths)
419
449
  end
420
450
  end
451
+
452
+ ## Helpers
453
+ # Gathers the deepest nodes among `values`
454
+ # @note it will only return multiple nodes if they
455
+ # they are all at the same level
456
+ # @return [Array<Nodes, Integer>]
457
+ def get_deepest_nodes_among_tags(*values)
458
+ ddepth = -1
459
+ tnodes = []
460
+
461
+ values.each do |ttag|
462
+ msg = "Couldn't find the node of #{ttag} in the tag-tree definition"
463
+ raise msg unless (cnode = node(ttag))
464
+
465
+ next unless cnode.depth >= ddepth
466
+ next tnodes.push(cnode) if cnode.depth == ddepth
467
+
468
+ # cnode.depth > ddepth
469
+ tnodes = [cnode]
470
+ ddepth = cnode.depth
471
+ end
472
+
473
+ [tnodes, ddepth]
474
+ end
475
+
476
+ def treat_classication(value)
477
+ return value unless value.is_a?(String)
478
+ value.strip.gsub(/\W+/, '').downcase
479
+ end
480
+
481
+ # Helper to convert to array
482
+ def into_a(value)
483
+ if value.is_a?(String)
484
+ value.split('|')
485
+ else
486
+ [value].flatten
487
+ end.compact
488
+ end
489
+
490
+ def as_boolean(value)
491
+ return false if value.nil? || value == false
492
+ return true if value == true
493
+ return false if value.to_s.strip.empty?
494
+ return true if %w[yes x true].include?(value.downcase)
495
+ false
496
+ end
421
497
  end
422
498
  end
423
499
  end
@@ -5,6 +5,7 @@ module Eco
5
5
  end
6
6
  end
7
7
 
8
+ require_relative 'organization/node_classifications'
8
9
  require_relative 'organization/tag_tree'
9
10
  require_relative 'organization/presets_factory'
10
11
  require_relative 'organization/preferences'
@@ -93,7 +93,7 @@ module Eco
93
93
  end
94
94
 
95
95
  # Adds an entry(ies) to the job queue.
96
- # @param entry [Ecoportal::API::V1::Person, Enumberable<Person>] the person(s) we want to update, carrying the changes to be done.
96
+ # @param entry [Ecoportal::API::V1::Person, Enumerable<Person>] the person(s) we want to update, carrying the changes to be done.
97
97
  # @param unique [Boolean] specifies if repeated entries should be avoided in the queue.
98
98
  # @yield [person] callback before launching the batch job request against the server.
99
99
  # @yieldparam person [Person] current person object that that should be treated by the callback before launching the batch.
@@ -8,12 +8,21 @@ module Eco
8
8
 
9
9
  attr_key :file, :structure_id
10
10
 
11
+ # The location node classifications of the organization
12
+ # @return [Eco::API::Organization::NodeClassifications]
13
+ def node_classifications(active: true)
14
+ return [] unless (graphql = graphql_api)
15
+
16
+ node_types = graphql.locationStructure.nodeClassifications(active: active)
17
+ Eco::API::Organization::NodeClassifications.new(node_types)
18
+ end
19
+
11
20
  # @note it retrieves the tree this way:
12
21
  # 1. If there's a file tagtree.json file, it uses it
13
22
  # 2. If no file, retrieves `structure_id` (config)
14
23
  # @param include_archived [Boolean] whether or not it should include archived nodes.
15
24
  # @return [Eco::API::Organization::TagTree]
16
- def scope_tree(enviro: nil, include_archived: true, raise_on_missing: true)
25
+ def scope_tree(enviro: nil, include_archived: true, raise_on_missing: true) # rubocop:disable Lint/UnusedMethodArgument
17
26
  return @tagtree if instance_variable_defined?(:@tagtree) && @tagtree.enviro == enviro
18
27
 
19
28
  kargs = {
@@ -21,34 +30,34 @@ module Eco
21
30
  includeArchivedNodes: include_archived
22
31
  }
23
32
 
24
- if tree_file = self.file
33
+ if (tree_file = file)
25
34
  if (tree = file_manager.load_json(tree_file)) && !tree.empty?
26
35
  @tagtree = Eco::API::Organization::TagTree.new(tree)
27
36
  end
28
- elsif self.structure_id
29
- kargs.merge(id: self.structure_id)
37
+ elsif structure_id
38
+ kargs.merge(id: structure_id)
30
39
  end
31
40
 
32
- @tagtree ||= live_tree(**kargs).tap do |tree|
33
- unless tree && !tree.empty?
34
- msg = "Could not find a local or live locations structure."
35
- raise MissingTagtree, msg
36
- end
41
+ @tagtree ||= live_tree(**kargs).tap do |tr| # rubocop:disable Naming/MemoizedInstanceVariableName
42
+ next if tr && !tr.empty?
43
+
44
+ msg = "Could not find a local or live locations structure."
45
+ raise MissingTagtree, msg
37
46
  end
38
47
  end
39
48
 
40
49
  # Among all the locations structures it selects the one with more location nodes
41
50
  # If `id` is provided, it only retrieves this locations structure.
42
51
  # @param [enviro] used for re-caching
43
- def live_tree(id: nil, enviro: nil, include_archived: false, **kargs, &block)
44
- existing_cache = !!@live_tree
52
+ def live_tree(id: nil, enviro: nil, include_archived: false, **kargs, &block) # rubocop:disable Metrics/AbcSize
53
+ existing_cache = !@live_tree.nil?
45
54
  first_load = !existing_cache
46
55
 
47
56
  target_change = existing_cache && id && @live_tree.id != id
48
57
  enviro_change = existing_cache && enviro && @live_tree.enviro != enviro
49
58
 
50
59
  switching_target = existing_cache && (target_change || enviro_change)
51
- refresh_cache = existing_cache && !switching_target
60
+ _refresh_cache = existing_cache && !switching_target
52
61
 
53
62
  kargs = {
54
63
  includeArchivedNodes: include_archived
@@ -59,10 +68,12 @@ module Eco
59
68
  @live_tree = live_tree_get(**args, &block)
60
69
  else
61
70
  trees = live_trees(**kargs, &block)
62
- @live_tree = trees.reject do |tree|
63
- tree.empty?
64
- end.max do |a,b|
65
- a.count <=> b.count
71
+ if trees.count > 1
72
+ @live_tree = trees.reject(&:empty?).max do |a, b|
73
+ a.count <=> b.count
74
+ end
75
+ else
76
+ @live_tree = trees.first
66
77
  end
67
78
  end.tap do |tree|
68
79
  if tree
@@ -85,8 +96,7 @@ module Eco
85
96
  # @param include_archived [Boolean] whether or not to include archived **nodes**
86
97
  # @return [Eco::API::Organization::TagTree, NilClass]
87
98
  def live_tree_get(id: nil, include_archived: false, **kargs, &block)
88
- return nil unless apis.active_api.version_available?(:graphql)
89
- return nil unless graphql = apis.api(version: :graphql)
99
+ return nil unless (graphql = graphql_api)
90
100
 
91
101
  kargs = {
92
102
  id: id,
@@ -94,9 +104,11 @@ module Eco
94
104
  }.merge(kargs)
95
105
 
96
106
  start = Time.now
97
- return nil unless tree = graphql.currentOrganization.locationStructure(**kargs, &block)
107
+ tree = graphql.currentOrganization.locationStructure(**kargs, &block)
108
+ return nil unless tree
109
+
98
110
  end_time = Time.now
99
- secs = (end_time - start).round(3)
111
+ secs = (end_time - start).round(3)
100
112
 
101
113
  Eco::API::Organization::TagTree.new(tree.treeify, id: tree.id, name: tree.name).tap do |eco_tree|
102
114
  cnt = eco_tree.count
@@ -110,15 +122,16 @@ module Eco
110
122
  # @return [Array<Eco::API::Organization::TagTree>]
111
123
  def live_trees(include_archived: false, **kargs, &block)
112
124
  [].tap do |eco_trees|
113
- next unless apis.active_api.version_available?(:graphql)
114
- next unless graphql = apis.api(version: :graphql)
125
+ next unless (graphql = graphql_api)
115
126
 
116
127
  kargs = {
117
128
  includeArchivedNodes: include_archived
118
129
  }.merge(kargs)
119
130
 
120
131
  start = Time.now
121
- next unless trees = graphql.currentOrganization.locationStructures(**kargs, &block)
132
+ trees = graphql.currentOrganization.locationStructures(**kargs, &block)
133
+ next unless trees
134
+
122
135
  end_time = Time.now
123
136
  secs = (end_time - start).round(3)
124
137
  cnt = 0
@@ -134,6 +147,11 @@ module Eco
134
147
 
135
148
  private
136
149
 
150
+ def graphql_api
151
+ return nil unless apis.active_api.version_available?(:graphql)
152
+ apis.api(version: :graphql)
153
+ end
154
+
137
155
  def session
138
156
  ASSETS.session
139
157
  end