eco-helpers 2.6.4 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +95 -0
  4. data/CHANGELOG.md +139 -2
  5. data/Rakefile +13 -7
  6. data/eco-helpers.gemspec +2 -2
  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/exception.rb +11 -13
  40. data/lib/eco/api/error.rb +32 -20
  41. data/lib/eco/api/organization/node_classifications.rb +82 -0
  42. data/lib/eco/api/organization/policy_groups.rb +4 -6
  43. data/lib/eco/api/organization/tag_tree.rb +169 -93
  44. data/lib/eco/api/organization.rb +1 -0
  45. data/lib/eco/api/session/batch/job.rb +1 -1
  46. data/lib/eco/api/session/config/tagtree.rb +41 -23
  47. data/lib/eco/api/session/config/workflow.rb +113 -88
  48. data/lib/eco/api/session/config.rb +6 -0
  49. data/lib/eco/api/session.rb +51 -29
  50. data/lib/eco/api/usecases/base_io.rb +28 -25
  51. data/lib/eco/api/usecases/default/locations/cli/tagtree_extract_cli.rb +7 -2
  52. data/lib/eco/api/usecases/default/locations/cli/tagtree_upload_cli.rb +21 -0
  53. data/lib/eco/api/usecases/default/locations/csv_to_tree_case.rb +3 -3
  54. data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +54 -23
  55. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +87 -0
  56. data/lib/eco/api/usecases/default/locations.rb +1 -0
  57. data/lib/eco/api/usecases/default/people/analyse_people_case.rb +60 -56
  58. data/lib/eco/api/usecases/default/people/change_email_case.rb +8 -9
  59. data/lib/eco/api/usecases/default/people/clean_unknown_tags_case.rb +13 -11
  60. data/lib/eco/api/usecases/default/people/clear_abilities_case.rb +2 -2
  61. data/lib/eco/api/usecases/default/people/org_data_convert_case.rb +25 -27
  62. data/lib/eco/api/usecases/default/people/refresh_case.rb +2 -2
  63. data/lib/eco/api/usecases/default/people/reinvite_trans_case.rb +1 -1
  64. data/lib/eco/api/usecases/default/people/reinvite_trans_cli.rb +0 -1
  65. data/lib/eco/api/usecases/default/people/restore_db_case.rb +39 -34
  66. data/lib/eco/api/usecases/default/people/set_default_tag_case.rb +19 -15
  67. data/lib/eco/api/usecases/default/people/supers_cyclic_identify_case.rb +16 -12
  68. data/lib/eco/api/usecases/default_cases/hris_case.rb +17 -15
  69. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +30 -16
  70. data/lib/eco/api/usecases/graphql/base.rb +5 -3
  71. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +4 -1
  72. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +14 -0
  73. data/lib/eco/api/usecases/graphql/helpers/base.rb +5 -4
  74. data/lib/eco/api/usecases/graphql/helpers/location/base/classifications_parser.rb +60 -0
  75. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +72 -0
  76. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +25 -59
  77. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +59 -0
  78. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/compare.rb +49 -0
  79. data/lib/eco/api/usecases/graphql/helpers/location/command/diff.rb +11 -0
  80. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +46 -0
  81. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +23 -0
  82. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_unarchive.rb +65 -0
  83. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable.rb +49 -0
  84. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable/relation_safe_sort.rb +119 -0
  85. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable.rb +59 -0
  86. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages.rb +82 -0
  87. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs.rb +20 -0
  88. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +84 -0
  89. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +4 -4
  90. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +24 -12
  91. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +21 -24
  92. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_map.rb +1 -1
  93. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_set.rb +10 -11
  94. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap.rb +8 -9
  95. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +41 -12
  96. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +11 -80
  97. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +89 -0
  98. data/lib/eco/api/usecases/graphql/samples/location/command/service.rb +6 -0
  99. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +89 -0
  100. data/lib/eco/api/usecases/graphql/samples/location/command.rb +3 -0
  101. data/lib/eco/api/usecases/graphql/samples/location/service/base.rb +9 -0
  102. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/heading.rb +18 -0
  103. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +53 -0
  104. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/classifications.rb +34 -0
  105. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/helpers.rb +28 -0
  106. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +46 -0
  107. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible.rb +38 -0
  108. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +105 -0
  109. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/discarded.rb +16 -0
  110. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +15 -0
  111. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/node_attr_maps.rb +22 -0
  112. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +45 -0
  113. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +36 -0
  114. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/output.rb +56 -0
  115. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list.rb +41 -0
  116. data/lib/eco/api/usecases/graphql/samples/location/service.rb +8 -0
  117. data/lib/eco/api/usecases/graphql/samples/location.rb +1 -0
  118. data/lib/eco/api/usecases/graphql/utils/sftp.rb +96 -36
  119. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +8 -6
  120. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +4 -3
  121. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +39 -25
  122. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +13 -15
  123. data/lib/eco/api/usecases/ooze_samples/helpers/filters.rb +50 -21
  124. data/lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb +21 -11
  125. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +2 -0
  126. data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +49 -43
  127. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +17 -19
  128. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +48 -43
  129. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +33 -34
  130. data/lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb +8 -10
  131. data/lib/eco/api/usecases.rb +0 -1
  132. data/lib/eco/cli/config/use_cases.rb +31 -29
  133. data/lib/eco/cli_default/workflow.rb +13 -14
  134. data/lib/eco/csv/table.rb +34 -25
  135. data/lib/eco/data/hashes/array_diff.rb +24 -35
  136. data/lib/eco/data/hashes/diff_result/meta.rb +131 -0
  137. data/lib/eco/data/hashes/diff_result.rb +65 -57
  138. data/lib/eco/data/hashes/sanke_camel_indifferent_access.rb +278 -0
  139. data/lib/eco/data/hashes.rb +1 -1
  140. data/lib/eco/data/locations/convert.rb +1 -1
  141. data/lib/eco/data/locations/node_base/csv_convert.rb +19 -9
  142. data/lib/eco/data/locations/node_base/parsing.rb +4 -2
  143. data/lib/eco/data/locations/node_base/treeify.rb +149 -132
  144. data/lib/eco/data/locations/node_base.rb +15 -4
  145. data/lib/eco/data/locations/node_diff/accessors.rb +13 -5
  146. data/lib/eco/data/locations/node_diff/nodes_diff/clustered_treeify.rb +101 -0
  147. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +99 -0
  148. data/lib/eco/data/locations/node_diff/{selectors.rb → nodes_diff/selectors.rb} +1 -1
  149. data/lib/eco/data/locations/node_diff/nodes_diff.rb +50 -35
  150. data/lib/eco/data/locations/node_diff.rb +45 -17
  151. data/lib/eco/data/locations/node_level/parsing.rb +15 -21
  152. data/lib/eco/data/locations/node_level.rb +66 -22
  153. data/lib/eco/data/locations/node_plain/parsing.rb +1 -1
  154. data/lib/eco/data/locations/node_plain.rb +60 -7
  155. data/lib/eco/data/strings/camel_case.rb +35 -0
  156. data/lib/eco/data/strings/snake_case.rb +18 -0
  157. data/lib/eco/data/strings.rb +8 -0
  158. data/lib/eco/data.rb +1 -0
  159. data/lib/eco/language/auxiliar_logger.rb +7 -5
  160. data/lib/eco/language/methods/call_detector.rb +11 -0
  161. data/lib/eco/language/methods/dsl_able.rb +7 -1
  162. data/lib/eco/language/methods.rb +2 -1
  163. data/lib/eco/language/models/collection.rb +23 -25
  164. data/lib/eco/language/models/parser_serializer.rb +24 -5
  165. data/lib/eco/version.rb +1 -1
  166. data/lib/eco-helpers.rb +0 -1
  167. metadata +52 -7
  168. data/lib/eco/data/hashes/diff_meta.rb +0 -52
@@ -11,7 +11,7 @@ module Eco
11
11
  class MissingParameter < StandardError
12
12
  attr_reader :type, :required, :given
13
13
 
14
- def initialize(msg = nil, type: nil, required:, given:)
14
+ def initialize(msg = nil, required:, given:, type: nil)
15
15
  @type = type
16
16
  @required = required
17
17
  @given = given
@@ -23,11 +23,11 @@ module Eco
23
23
 
24
24
  class << self
25
25
  def input_required?(type)
26
- !valid_type?(type) || [:import, :sync].include?(type)
26
+ !valid_type?(type) || %i[import sync].include?(type)
27
27
  end
28
28
 
29
29
  def people_required?(type)
30
- !valid_type?(type) || [:filter, :transform, :sync, :error_handler, :export].include?(type)
30
+ !valid_type?(type) || %i[filter transform sync error_handler export].include?(type)
31
31
  end
32
32
  end
33
33
 
@@ -40,10 +40,14 @@ module Eco
40
40
  # @param people [Eco::API::Organization::People] people object.
41
41
  # @param session [Eco::API:Session]
42
42
  # @param options [Hash] hash with symbol keys (i.e. behaviour modifiers, cli trackers, filters, etc.)
43
- def initialize(type: nil, input: nil, people: nil, session:, options: {}, output: nil, validate: true)
44
- self.type = type if type
45
-
46
- if self.type && validate
43
+ def initialize( # rubocop:disable Lint/MissingSuper
44
+ session:,
45
+ type: nil, input: nil, people: nil,
46
+ options: {}, output: nil, validate: true
47
+ )
48
+ self.type = type if type
49
+
50
+ if self.type && validate # rubocop:disable Style/IfUnlessModifier
47
51
  validate_args(input: input, people: people, session: session, options: options)
48
52
  end
49
53
 
@@ -64,8 +68,8 @@ module Eco
64
68
  # @return [Eco::API::UseCases::BaseIO]
65
69
  def base
66
70
  kargs = params(keyed: true).merge({
67
- type: self.type,
68
- output: self.output,
71
+ type: type,
72
+ output: output
69
73
  }).slice(:type, :input, :people, :session, :options, :output) # :validate <- ?
70
74
  Eco::API::UseCases::BaseIO.new(**kargs)
71
75
  end
@@ -74,12 +78,12 @@ module Eco
74
78
  # @return [Eco::API::UseCases::BaseIO]
75
79
  def new(**kargs)
76
80
  default = {
77
- type: self.type,
78
- input: self.input,
79
- people: self.people,
80
- session: self.session,
81
- options: self.options,
82
- output: self.output,
81
+ type: type,
82
+ input: input,
83
+ people: people,
84
+ session: session,
85
+ options: options,
86
+ output: output,
83
87
  validate: true
84
88
  }
85
89
  self.class.new(**default.merge(kargs))
@@ -88,7 +92,7 @@ module Eco
88
92
  # Helper to build a `Hash` of symbol keys or `Array` with params to do callbacks.
89
93
  def params(keyed: false, all: false)
90
94
  kargs = {}
91
- kargs.merge!(input: input) if input_required? || all
95
+ kargs.merge!(input: input) if input_required? || all
92
96
  kargs.merge!(people: people) if people_required? || all
93
97
  kargs.merge!(session: session, options: options)
94
98
  keyed ? kargs : kargs.values
@@ -108,7 +112,7 @@ module Eco
108
112
  # provided that it preserves the target parameter
109
113
  # (otherise it would blank it)
110
114
  # @return [Eco::API::UseCases::BaseIO]
111
- def chained(as: self.type)
115
+ def chained(as: type) # rubocop:disable Naming/MethodParameterName
112
116
  base.tap do |io_base|
113
117
  next unless io_base.output
114
118
  case as
@@ -116,7 +120,7 @@ module Eco
116
120
  io_base.output_be_input!
117
121
  when :filter
118
122
  io_base.output_be_people!
119
- when :transform, :sync, :export, :error_handler, :other
123
+ # when :transform, :sync, :export, :error_handler, :other
120
124
  end
121
125
  end
122
126
  end
@@ -124,26 +128,25 @@ module Eco
124
128
  protected
125
129
 
126
130
  def output_be_input!
127
- @input, @output = output, nil
131
+ @input, @output = output, nil # rubocop:disable Style/ParallelAssignment
128
132
  input
129
133
  end
130
134
 
131
135
  def output_be_people!
132
- @people, @output = output, nil
136
+ @people, @output = output, nil # rubocop:disable Style/ParallelAssignment
133
137
  people
134
138
  end
135
139
 
136
140
  private
137
141
 
138
142
  def validate_args(input:, people:, session:, options:)
139
- case
140
- when !session.is_a?(Eco::API::Session)
143
+ if !session.is_a?(Eco::API::Session)
141
144
  raise MissingParameter.new("UseCase", required: :session, given: session.class)
142
- when input_required? && !input
145
+ elsif input_required? && !input
143
146
  raise MissingParameter.new("UseCase", type: type, required: :input, given: input.class)
144
- when people_required? && !people.is_a?(Eco::API::Organization::People)
147
+ elsif people_required? && !people.is_a?(Eco::API::Organization::People)
145
148
  raise MissingParameter.new("UseCase", type: type, required: :people, given: people.class)
146
- when !options || (options && !options.is_a?(Hash))
149
+ elsif !options || (options && !options.is_a?(Hash))
147
150
  raise MissingParameter.new("Use Case options", required: :Hash, given: options.class)
148
151
  end
149
152
  true
@@ -1,7 +1,7 @@
1
1
  class Eco::API::UseCases::Default::Locations::TagtreeExtract
2
2
  # Class to define the CLI integration of a usecase anywhere it suits.
3
3
  class Cli < Eco::API::UseCases::Cli
4
- desc "Exports the tagtree in a specific format"
4
+ desc "Converts a tagtree into a specific format"
5
5
 
6
6
  add_option("-include-archived", "Whether the archived nodes should be included") do |options|
7
7
  options.deep_merge!(output: {include: {archived: true}})
@@ -17,7 +17,7 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract
17
17
  options.deep_merge!(output: {attrs: attrs.split('|').map(&:to_sym)})
18
18
  end
19
19
 
20
- add_option("-with-ancestors", "Include ancestors in tree csv/excel_tree") do |options|
20
+ add_option("-with-ancestors", "Include full path of ancestors in tree csv/excel_tree") do |options|
21
21
  options.deep_merge!(output: {include: {ancestors: true}})
22
22
  end
23
23
 
@@ -25,5 +25,10 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract
25
25
  indent = SCR.get_arg("-indent", with_param: true) || ""
26
26
  options.deep_merge!(output: {indent: indent})
27
27
  end
28
+
29
+ add_option("-file-as-source", "Use a file as locations input (rather than the live locations)") do |options|
30
+ file = SCR.get_file("-file-as-source", required: true, should_exist: true)
31
+ options.deep_merge!(source: {file: file})
32
+ end
28
33
  end
29
34
  end
@@ -0,0 +1,21 @@
1
+ class Eco::API::UseCases::Default::Locations::TagtreeUpload
2
+ # Class to define the CLI integration of a usecase anywhere it suits.
3
+ class Cli < Eco::API::UseCases::Cli
4
+ desc "Uploads the tagtree (assumes nodes don't exist!)"
5
+
6
+ callback do |_sess, options, _case|
7
+ file = SCR.get_file(cli_name, required: true, should_exist: true)
8
+ options.deep_merge!(source: {file: file})
9
+ end
10
+
11
+ add_option("-top-id", "To nest the upload under some existing node (i.e. top node)") do |options|
12
+ format = SCR.get_arg("-top-id", with_param: true)
13
+ options.deep_merge!(input: {top_id: format})
14
+ end
15
+
16
+ add_option("-structure-id", "Target structure id") do |options|
17
+ id = SCR.get_arg("-structure-id", with_param: true)
18
+ options.deep_merge!(source: {structure_id: id})
19
+ end
20
+ end
21
+ end
@@ -4,9 +4,9 @@ class Eco::API::UseCases::Default::Locations::CsvToTree < Eco::API::Common::Load
4
4
 
5
5
  include Eco::Data::Locations::DSL
6
6
 
7
- TIME_FORMAT = '%Y%m%dT%H%M%S'
7
+ TIME_FORMAT = '%Y%m%dT%H%M%S'.freeze
8
8
 
9
- def main(session, options, usecase)
9
+ def main(_session, options, _usecase)
10
10
  options[:end_get] = false
11
11
  tree_struct = org_tree(input_csv)
12
12
 
@@ -27,7 +27,7 @@ class Eco::API::UseCases::Default::Locations::CsvToTree < Eco::API::Common::Load
27
27
  end
28
28
 
29
29
  def input_file
30
- @input_file ||= options.dig(:source, :file)
30
+ @input_file ||= options.dig(:source, :file)
31
31
  end
32
32
 
33
33
  def input_encoding
@@ -3,14 +3,14 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
3
3
  require_relative 'cli/tagtree_extract_cli'
4
4
  #cli self::Cli
5
5
 
6
- name "export-tree"
6
+ name "tagtree-extract"
7
7
  type :other
8
8
 
9
9
  include Eco::Data::Files
10
10
 
11
- OUT_FOLDER = "sftp"
12
- OUT_TIME_FORMAT = '%Y%m%dT%H%M%S'
13
- OUT_FILENAME = "live_tree"
11
+ OUT_FOLDER = "sftp".freeze
12
+ OUT_TIME_FORMAT = '%Y%m%dT%H%M%S'.freeze
13
+ OUT_FILENAME = "live_tree".freeze
14
14
 
15
15
  def process
16
16
  case format
@@ -18,7 +18,7 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
18
18
  excel(output_filename) do |workbook|
19
19
  tag_trees.each do |tree|
20
20
  excel_sheet(workbook, compact_name(tree.name), header: format == :excel_nodes) do |sheet|
21
- tree_extract(tree).each_with_index do |row, idx|
21
+ tree_extract(tree).each_with_index do |row, _idx|
22
22
  sheet.append_row(row)
23
23
  end
24
24
  end
@@ -39,7 +39,7 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
39
39
  private
40
40
 
41
41
  def compact_name(str)
42
- str.gsub(/\s+/, "_")[0..30]
42
+ str.gsub(/\s+/, "_").gsub(/\W/, '')[0..30]
43
43
  end
44
44
 
45
45
  def str_node_attrs
@@ -85,25 +85,58 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
85
85
 
86
86
  def output_file_format
87
87
  case format
88
- when :list; 'txt'
89
- when :nodes; 'csv'
90
- when :csv_tree; 'csv'
91
- when :json; 'json'
92
- when :excel_tree; 'xlsx'
93
- when :excel_nodes; 'xlsx'
94
- else
88
+ when :nodes, :csv_tree
89
+ 'csv'
90
+ when :json then 'json'
91
+ when :excel_tree, :excel_nodes
92
+ 'xlsx'
93
+ else # :list
95
94
  'txt'
96
95
  end
97
96
  end
98
97
 
99
98
  def tag_trees
100
- @tag_trees ||= session.live_trees(include_archived: include_archived?).reject do |tree|
99
+ @tag_trees ||=
100
+ if file_as_source?
101
+ [org_tree(input_csv).tap do |tree|
102
+ tree.name = file_as_source
103
+ end]
104
+ else
105
+ retrieve_live_trees
106
+ end
107
+ end
108
+
109
+ def retrieve_live_trees
110
+ session.live_trees(include_archived: include_archived?).reject do |tree|
101
111
  tree.empty?.tap do |rejected|
102
112
  log(:warn) { "Tree '#{tree.name}' does NOT have location nodes. Skipping..." } if rejected
103
113
  end
104
114
  end
105
115
  end
106
116
 
117
+ def input_csv
118
+ return unless file_as_source?
119
+ @input_csv ||= Eco::CSV.read(file_as_source, encoding: input_encoding)
120
+ end
121
+
122
+ def input_encoding
123
+ options.dig(:input, :file, :encoding) || 'utf-8'
124
+ end
125
+
126
+ def file_as_source?
127
+ return true unless file_as_source.nil?
128
+ false
129
+ end
130
+
131
+ def file_as_source
132
+ @file_as_source ||= options.dig(:source, :file).tap do |file|
133
+ next if file.nil?
134
+ next if File.exist?(file)
135
+ log(:error) { "File '#{file}' does not exist" }
136
+ exit 1
137
+ end
138
+ end
139
+
107
140
  def include_ancestors?
108
141
  options.dig(:output, :include, :ancestors)
109
142
  end
@@ -150,10 +183,8 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
150
183
  log(:info) { "Created file: #{filename}" }
151
184
  end
152
185
 
153
- def file(filename)
154
- File.open(filename, "w") do |fd|
155
- yield(fd)
156
- end
186
+ def file(filename, &block)
187
+ File.open(filename, "w", &block)
157
188
  ensure
158
189
  log(:info) { "Created file: #{filename}" }
159
190
  end
@@ -169,13 +200,13 @@ class Eco::API::UseCases::Default::Locations::TagtreeExtract < Eco::API::UseCase
169
200
 
170
201
  def excel_sheet(workbook, name, header: true)
171
202
  #unless sheet = workbook.get_worksheet_by_name(name)
172
- sheet = workbook.add_worksheet(name)
203
+ sheet = workbook.add_worksheet(name)
173
204
  #end
174
205
  sheet.auto_width = true
175
206
  yield(sheet)
176
- if header
177
- sheet.set_row(0, 30, workbook.bold_format)
178
- sheet.freeze_panes(1, 0)
179
- end
207
+
208
+ return unless header
209
+ sheet.set_row(0, 30, workbook.bold_format)
210
+ sheet.freeze_panes(1, 0)
180
211
  end
181
212
  end
@@ -0,0 +1,87 @@
1
+ class Eco::API::UseCases::Default::Locations::TagtreeUpload < Eco::API::UseCases::GraphQL::Samples::Location::Command
2
+ require_relative 'cli/tagtree_upload_cli'
3
+
4
+ name "tagtree-upload"
5
+ type :other
6
+
7
+ COMMANDS_PER_PAGE = 45
8
+ TOP_ID = nil
9
+
10
+ def track_current_tree?
11
+ false
12
+ end
13
+
14
+ # We only define the `:insert` stage
15
+ def inputs(force_continue: force_continue?)
16
+ {}.tap do |stages|
17
+ stages[:insert] = input(insert_commands, force_continue: force_continue)
18
+ end.each do |stage, input|
19
+ yield(input, stage) if block_given?
20
+ end
21
+ end
22
+
23
+ def insert_commands(tree = input_tree, pid: nil)
24
+ [].tap do |comms|
25
+ unless tree.top?
26
+ pid = top_id if top_id? && pid.nil?
27
+ comms << insert_command(tree, pid: pid) unless top_id?(tree.id)
28
+ pid = tree.id
29
+ end
30
+ tree.nodes.map do |node|
31
+ insert_commands(node, pid: pid)
32
+ end.flatten(1).tap do |subs|
33
+ comms.concat(subs)
34
+ end
35
+ end
36
+ end
37
+
38
+ def insert_command(node, pid: nil)
39
+ {
40
+ insert: {
41
+ nodeId: node.id,
42
+ name: node.name,
43
+ parentId: pid
44
+ }.tap do |node_hash|
45
+ classification_ids = to_classification_ids(node.classifications)
46
+ next unless classification_ids.any?
47
+
48
+ node_hash.merge!({
49
+ classificationIds: node.classifications.dup
50
+ })
51
+ end
52
+ }
53
+ end
54
+
55
+ def top_id?(node_id = nil)
56
+ return top_id.is_a?(String) if node_id.nil?
57
+ node_id == top_id
58
+ end
59
+
60
+ def top_id
61
+ options.dig(:input, :top_id) || self.class::TOP_ID
62
+ end
63
+
64
+ def input_tree
65
+ @input_tree ||= Eco::API::Organization::TagTree.new(tree_json).tap do |tree|
66
+ log(:info) {
67
+ "Loaded input locations structure (#{tree.count} nodes)"
68
+ }
69
+ end
70
+ end
71
+
72
+ def tree_json
73
+ hash_tree(input_csv)
74
+ end
75
+
76
+ def input_csv
77
+ @input_csv ||= csv_from(input_file).tap do |table|
78
+ log(:info) {
79
+ "#{table.count} data rows loaded from #{input_file}"
80
+ }
81
+ end
82
+ end
83
+
84
+ def input_file
85
+ @input_file ||= options.dig(:source, :file)
86
+ end
87
+ end
@@ -13,3 +13,4 @@ require_relative 'locations/codes_to_tags_case'
13
13
  require_relative 'locations/create_tag_paths_case'
14
14
  require_relative 'locations/csv_to_tree_case'
15
15
  require_relative 'locations/tagtree_extract_case'
16
+ require_relative 'locations/tagtree_upload_case'
@@ -4,22 +4,20 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
4
4
 
5
5
  attr_reader :people
6
6
 
7
- def main(people, session, options, usecase)
7
+ def main(_people, _session, options, _usecase)
8
8
  options[:end_get] = false
9
- @people = people
10
9
 
11
- case
12
- when case_options[:identify_duplicates]
10
+ if case_options[:identify_duplicates]
13
11
  identify_duplicates
14
- when case_options[:identify_unnamed]
12
+ elsif case_options[:identify_unnamed]
15
13
  identify_unnamed
16
14
  else
17
- session.logger.info("No analysis operation was specified")
15
+ log(:info) { "No analysis operation was specified" }
18
16
  end.tap do |people_involved|
19
- if people_involved
20
- to_csv(people_involved) if to_csv?
21
- create_people_backup(people_involved) if results_people_backup?
22
- end
17
+ next unless people_involved
18
+
19
+ to_csv(people_involved) if to_csv?
20
+ create_people_backup(people_involved) if results_people_backup?
23
21
  end
24
22
  end
25
23
 
@@ -27,22 +25,21 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
27
25
 
28
26
  def identify_unnamed
29
27
  similarity_analytics.unnamed.tap do |unnamed|
30
- if unnamed.empty?
31
- session.logger.info("There were no people with no name!!")
32
- end
28
+ next unless unnamed.empty?
29
+ log(:info) { "There were no people with no name!!" }
33
30
  end
34
31
  end
35
32
 
36
33
  def identify_duplicates
37
34
  analysed = similarity_screening
38
35
  if case_options[:ignore_matching_words]
39
- puts "Fine tune results by ignoring matching words..."
36
+ log(:info) { "Fine tune results by ignoring matching words..." }
40
37
  analysed = strict_similarity(analysed)
41
38
  end
42
39
 
43
40
  similarity_analytics.newSimilarity(analysed).tap do |related_people|
44
41
  if related_people.empty?
45
- session.logger.info("There were no possible duplicates identified!!")
42
+ log(:info) { "There were no possible duplicates identified!!" }
46
43
  else
47
44
  report = similarity_analytics.report(analysed, format: :txt)
48
45
  save!(report)
@@ -51,34 +48,38 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
51
48
  end
52
49
 
53
50
  def strict_similarity(analysed)
54
- similarity_analytics.ignore_matching_words(analysed, **{
51
+ similarity_analytics.ignore_matching_words(
52
+ analysed,
55
53
  threshold: 0.5,
56
- order: [:ngrams]
57
- })
54
+ order: [:ngrams]
55
+ )
58
56
  end
59
57
 
60
58
  def similarity_screening
61
59
  similarity_analytics.attribute = field_similarity
62
60
  options = {
63
61
  threshold: 0.4,
64
- order: [:average, :dice]
62
+ order: %i[average dice]
65
63
  }.tap do |opts|
66
64
  opts.merge!(needle_read: facet_field_proc) if facet_field?
67
- opts.merge!(unique_words: true) if unique_words?
65
+ opts.merge!(unique_words: true) if unique_words?
68
66
  end
69
67
  analysed = similarity_analytics.analyse(**options)
70
- puts "Got #{analysed.count} results after basic screening with #{options}"
68
+ log(:info) {
69
+ "Got #{analysed.count} results after basic screening with #{options}"
70
+ }
71
71
 
72
72
  return analysed if case_options[:only_screening]
73
73
  options = {threshold: 0.5, order: [:average]}
74
- puts "Going to rearrange results... with #{options}"
75
- similarity_analytics.rearrange(analysed, **options).tap do |analysed|
76
- puts "... got #{analysed.count} results after rearranging"
74
+ log(:info) { "Going to rearrange results... with #{options}" }
75
+
76
+ similarity_analytics.rearrange(analysed, **options).tap do |results|
77
+ log(:info) { "... got #{results.count} results after rearranging" }
77
78
  end
78
79
  end
79
80
 
80
81
  def similarity_analytics
81
- @analytics ||= people.similarity
82
+ @similarity_analytics ||= people.similarity
82
83
  end
83
84
 
84
85
  def create_people_backup(cut = people, file = results_people_backup)
@@ -90,7 +91,8 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
90
91
  opts.deep_merge!(export: {file: {name: file, format: :csv}})
91
92
  opts.deep_merge!(export: {options: {nice_header: true}})
92
93
  opts.deep_merge!(export: {options: {internal_names: true}})
93
- #opts.deep_merge!(export: {options: {split_schemas: true}})
94
+ # opts.deep_merge!(export: {options: {split_schemas: true}})
95
+
94
96
  session.process_case("to-csv", type: :export, people: data, options: opts.merge(options.slice(:export)))
95
97
  end
96
98
 
@@ -112,7 +114,7 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
112
114
  end
113
115
 
114
116
  def use_field
115
- case_options.dig(:use_field)
117
+ case_options[:use_field]
116
118
  end
117
119
 
118
120
  def use_field?
@@ -120,7 +122,7 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
120
122
  end
121
123
 
122
124
  def facet_field
123
- case_options.dig(:facet_field)
125
+ case_options[:facet_field]
124
126
  end
125
127
 
126
128
  def facet_field?
@@ -128,7 +130,7 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
128
130
  end
129
131
 
130
132
  def csv_file
131
- case_options.dig(:csv_file)
133
+ case_options[:csv_file]
132
134
  end
133
135
 
134
136
  def to_csv?
@@ -136,7 +138,7 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
136
138
  end
137
139
 
138
140
  def results_people_backup
139
- case_options.dig(:backup_people)
141
+ case_options[:backup_people]
140
142
  end
141
143
 
142
144
  def results_people_backup?
@@ -152,15 +154,16 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
152
154
  end
153
155
 
154
156
  def save!(data)
155
- ext = File.extname(output_file).downcase.delete(".")
157
+ ext = File.extname(output_file).downcase.delete(".")
156
158
  session.logger.info("Generating file '#{output_file}'")
157
159
  File.open(output_file, "w") do |fd|
158
- if ext == "txt"
160
+ case ext
161
+ when "txt"
159
162
  fd << data
160
- elsif ext == "html"
163
+ when "html"
161
164
  puts "html is still not supported"
162
165
  exit(1)
163
- elsif ext == "json"
166
+ when "json"
164
167
  puts "json is still not supported"
165
168
  exit(1)
166
169
  end
@@ -172,7 +175,7 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
172
175
  def proc_value_access(expression)
173
176
  #return expression.to_sym if expression.start_with?(":")
174
177
  subexpressions = expression.split(" AND ")
175
- Proc.new do |person|
178
+ proc do |person|
176
179
  values = subexpressions.map {|exp| attribute_access(person, exp)}
177
180
  values.compact.join(" ")
178
181
  end
@@ -196,27 +199,28 @@ class Eco::API::UseCases::Default::People::Analyse < Eco::API::Common::Loaders::
196
199
  end
197
200
 
198
201
  def get_attr(obj, part)
199
- case
200
- when !obj
201
- nil
202
- when part.is_a?(Symbol) || obj.respond_to?(part.to_sym)
203
- obj.send(part.to_sym)
204
- when part.start_with?(":")
205
- get_attr(obj, part[1..-1])
206
- when part.start_with?("details[")
207
- if (obj.respond_to?(:details)) && details = obj.details
208
- if match = part.match(/details\[(?<field>.*)\]/)
209
- details[match[:field]]
210
- else
211
- raise "Review your -use-field expression. It should read: person.details[target-alt_id]"
212
- end
213
- end
214
- when part.start_with?("account")
215
- obj.account if obj.respond_to?(:account)
216
- when part.start_with?("person")
217
- obj
218
- else
219
- raise "Review your expression. Cannot recognize '#{part}' as part of '#{obj.class}'"
202
+ return unless obj
203
+
204
+ is_method = part.is_a?(Symbol) || obj.respond_to?(part.to_sym)
205
+ return obj.send(part.to_sym) if is_method
206
+ return get_attr(obj, part[1..]) if part.start_with?(":")
207
+
208
+ if part.start_with?("details[")
209
+ via_details = obj.respond_to?(:details) && (details = obj.details)
210
+ return unless via_details
211
+
212
+ match = part.match(/details\[(?<field>.*)\]/)
213
+ return details[match[:field]] if match
214
+
215
+ raise "Review your -use-field expression. It should read: person.details[target-alt_id]"
220
216
  end
217
+
218
+ if part.start_with?("account")
219
+ return obj.respond_to?(:account) ? obj.account : nil
220
+ end
221
+
222
+ return obj if part.start_with?("person")
223
+
224
+ raise "Review your expression. Cannot recognize '#{part}' as part of '#{obj.class}'"
221
225
  end
222
226
  end