eco-helpers 2.0.16 → 2.0.17

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -6
  3. data/eco-helpers.gemspec +6 -4
  4. data/lib/eco-helpers.rb +1 -0
  5. data/lib/eco/api/common/base_loader.rb +14 -0
  6. data/lib/eco/api/common/people/default_parsers/date_parser.rb +11 -1
  7. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +1 -1
  8. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +11 -11
  9. data/lib/eco/api/common/people/person_entry.rb +4 -2
  10. data/lib/eco/api/common/session/mailer.rb +0 -1
  11. data/lib/eco/api/common/session/s3_uploader.rb +0 -1
  12. data/lib/eco/api/common/session/sftp.rb +0 -1
  13. data/lib/eco/api/microcases.rb +3 -1
  14. data/lib/eco/api/microcases/append_usergroups.rb +0 -1
  15. data/lib/eco/api/microcases/people_cache.rb +2 -2
  16. data/lib/eco/api/microcases/people_load.rb +2 -2
  17. data/lib/eco/api/microcases/people_refresh.rb +2 -2
  18. data/lib/eco/api/microcases/people_search.rb +6 -6
  19. data/lib/eco/api/microcases/preserve_default_tag.rb +23 -0
  20. data/lib/eco/api/microcases/preserve_filter_tags.rb +28 -0
  21. data/lib/eco/api/microcases/preserve_policy_groups.rb +30 -0
  22. data/lib/eco/api/microcases/set_account.rb +0 -1
  23. data/lib/eco/api/organization.rb +1 -0
  24. data/lib/eco/api/organization/people.rb +7 -0
  25. data/lib/eco/api/organization/people_analytics.rb +60 -0
  26. data/lib/eco/api/organization/presets_factory.rb +22 -83
  27. data/lib/eco/api/organization/presets_integrity.json +6 -0
  28. data/lib/eco/api/organization/presets_values.json +5 -4
  29. data/lib/eco/api/policies/default_policies/99_user_access_policy.rb +0 -30
  30. data/lib/eco/api/session.rb +1 -20
  31. data/lib/eco/api/session/batch.rb +23 -7
  32. data/lib/eco/api/session/config.rb +0 -10
  33. data/lib/eco/api/session/config/people.rb +1 -17
  34. data/lib/eco/api/usecases/default_cases.rb +1 -1
  35. data/lib/eco/api/usecases/default_cases/abstract_policygroup_abilities_case.rb +1 -1
  36. data/lib/eco/api/usecases/default_cases/analyse_people_case.rb +76 -0
  37. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +2 -3
  38. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +11 -1
  39. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +1 -2
  40. data/lib/eco/api/usecases/default_cases/supers_cyclic_identify_case.rb +1 -1
  41. data/lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb +1 -1
  42. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +85 -27
  43. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +62 -36
  44. data/lib/eco/cli/config/default/options.rb +19 -17
  45. data/lib/eco/cli/config/default/people_filters.rb +3 -3
  46. data/lib/eco/cli/config/default/usecases.rb +66 -32
  47. data/lib/eco/cli/config/default/workflow.rb +1 -1
  48. data/lib/eco/cli/config/help.rb +1 -0
  49. data/lib/eco/cli/config/options_set.rb +106 -13
  50. data/lib/eco/cli/config/use_cases.rb +33 -33
  51. data/lib/eco/cli/scripting/args_helpers.rb +30 -3
  52. data/lib/eco/data.rb +1 -0
  53. data/lib/eco/data/crypto/encryption.rb +3 -3
  54. data/lib/eco/data/files/helpers.rb +6 -4
  55. data/lib/eco/data/fuzzy_match.rb +119 -0
  56. data/lib/eco/data/fuzzy_match/array_helpers.rb +75 -0
  57. data/lib/eco/data/fuzzy_match/chars_position_score.rb +37 -0
  58. data/lib/eco/data/fuzzy_match/ngrams_score.rb +73 -0
  59. data/lib/eco/data/fuzzy_match/pairing.rb +102 -0
  60. data/lib/eco/data/fuzzy_match/result.rb +67 -0
  61. data/lib/eco/data/fuzzy_match/results.rb +53 -0
  62. data/lib/eco/data/fuzzy_match/score.rb +44 -0
  63. data/lib/eco/data/fuzzy_match/stop_words.rb +35 -0
  64. data/lib/eco/data/fuzzy_match/string_helpers.rb +69 -0
  65. data/lib/eco/version.rb +1 -1
  66. metadata +82 -10
  67. data/lib/eco/api/microcases/refresh_abilities.rb +0 -19
  68. data/lib/eco/api/organization/presets_reference.json +0 -59
  69. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +0 -30
@@ -11,7 +11,6 @@ module Eco
11
11
  person.account.send_invites = options[:send_invites] if options.key?(:send_invites)
12
12
  micro.refresh_default_tag(entry, person, options)
13
13
  micro.fix_default_group(entry, person, options)
14
- micro.refresh_abilities(person, options)
15
14
  end
16
15
  end
17
16
 
@@ -9,6 +9,7 @@ require_relative 'organization/tag_tree'
9
9
  require_relative 'organization/presets_factory'
10
10
  require_relative 'organization/preferences'
11
11
  require_relative 'organization/people'
12
+ require_relative 'organization/people_analytics'
12
13
  require_relative 'organization/person_schemas'
13
14
  require_relative 'organization/policy_groups'
14
15
  require_relative 'organization/login_providers'
@@ -176,6 +176,12 @@ module Eco
176
176
  end
177
177
  # @!endgroup
178
178
 
179
+ # @!group Helper methods
180
+ def analytics
181
+ Eco::API::Organization::PeopleAnalytics.new(self.to_a)
182
+ end
183
+ # @!endgroup
184
+
179
185
  protected
180
186
 
181
187
  def on_change
@@ -190,6 +196,7 @@ module Eco
190
196
  @by_external_id = to_h('external_id')
191
197
  @by_users_email = users.to_h('email')
192
198
  @by_non_users_email = non_users.to_h('email')
199
+ @by_email = to_h('email')
193
200
  @caches_init = true
194
201
  end
195
202
 
@@ -0,0 +1,60 @@
1
+ module Eco
2
+ module API
3
+ module Organization
4
+ class PeopleAnalytics < Eco::API::Organization::People
5
+ include Eco::Data::FuzzyMatch
6
+
7
+ # @!group Helpers
8
+
9
+ # @!endgroup
10
+
11
+ # @!group Searchers
12
+
13
+ # It gathers those that have the same `email`
14
+ # @return [Hash] where `keys` are `email`s and `values` an `Array<Person>`
15
+ def repeated_emails
16
+ init_caches
17
+ @by_email.select do |email, people|
18
+ people.count > 1
19
+ end
20
+ end
21
+
22
+ # @!endgroup
23
+
24
+ # @!group Analysers
25
+
26
+ # TODO: Sort results by `results.first.methods`
27
+ def similarity(**options)
28
+ each_with_object({}) do |person, results|
29
+ results[person.id] = find_all_with_score(person, **options)
30
+ end
31
+ end
32
+
33
+
34
+ def print_analysis(threshold)
35
+ similarity.each do |id, results|
36
+ msg = results.results.select do |result|
37
+ result.threshold?(threshold)
38
+ end.map do |result|
39
+ result.print
40
+ end.join("\n ")
41
+
42
+ puts "'#{self[id].identify}':\n " + msg
43
+ end
44
+ end
45
+ # @!endgroup
46
+
47
+ protected
48
+
49
+ def on_change
50
+ remove_instance_variable(@fuzzy_match)
51
+ super
52
+ end
53
+
54
+ private
55
+
56
+
57
+ end
58
+ end
59
+ end
60
+ end
@@ -3,6 +3,8 @@ module Eco
3
3
  module Organization
4
4
 
5
5
  class PresetsFactory
6
+ ABILITIES = File.join(__dir__, 'presets_values.json')
7
+ INTEGRITY = File.join(__dir__, 'presets_integrity.json')
6
8
 
7
9
  class << self
8
10
 
@@ -24,44 +26,36 @@ module Eco
24
26
 
25
27
  end
26
28
 
27
- ABILITIES = File.join(__dir__, 'presets_values.json')
28
- INTEGRITY = File.join(__dir__, 'presets_integrity.json')
29
- DEFAULT_CUSTOM = 'presets_custom.json'
30
- DEFAULT_MAP = 'presets_map.json'
31
-
32
- def initialize(presets_custom: DEFAULT_CUSTOM, presets_map: DEFAULT_MAP, enviro: nil, policy_groups: nil)
29
+ def initialize(enviro: nil, policy_groups: nil)
33
30
  fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
34
31
  @enviro = enviro
35
-
36
- @policy_groups = policy_groups
37
- @presets_custom_file = presets_custom || DEFAULT_CUSTOM
38
- @presets_map_file = presets_map || DEFAULT_MAP
32
+ @policy_groups = policy_groups
39
33
  end
40
34
 
41
- def new(*policy_group_ids_or_names)
42
-
43
- names = policy_group_ids_or_names.map do |id_name|
44
- policy_groups.to_name(id_name)&.downcase
45
- end.compact
35
+ # @return [Array<String>] all the abilities
36
+ def keys
37
+ self.class.abilities
38
+ end
46
39
 
47
- if presets_map
48
- preset_names = names.map { |name| presets_map.fetch(name, nil) }
49
- else # option to do not use preset mapping (so just the policy group name)
50
- preset_names = names
51
- end
52
- compile(*preset_names)
40
+ def valid?(preset)
41
+ validate(perset).length == 0
53
42
  end
54
43
 
55
- # @return [Array<String>] all the abilities
56
- def keys
57
- abilities_model.keys
44
+ def validate(preset)
45
+ [].tap do |errors|
46
+ if err = preset_errors(preset)
47
+ errors << "{ '#{key}' preset -> #{err}}"
48
+ end
49
+ if err = preset_integrity(preset)
50
+ errors << "{ '#{key}' preset -> #{err}}"
51
+ end
52
+ end
58
53
  end
59
54
 
60
55
  private
61
56
 
62
- def compile(*preset_names)
63
- fatal("You need to specify an existing file for the custom presets.") if !@presets_custom
64
- @presets_custom.values_at(*preset_names).compact.reduce({}) do |p1, p2|
57
+ def compile(*presets)
58
+ presets.compact.reduce({}) do |p1, p2|
65
59
  merge(p1, p2)
66
60
  end
67
61
  end
@@ -79,11 +73,6 @@ module Eco
79
73
  end
80
74
  end
81
75
 
82
- # unsused: only play with the given abilities
83
- def empty_model
84
- JSON.parse(abilities_model.to_json).transform_values {|v| nil }
85
- end
86
-
87
76
  def preset_errors(preset)
88
77
  return "No preset given" if !preset
89
78
  errors = preset.map do |k, v|
@@ -146,7 +135,7 @@ module Eco
146
135
  end
147
136
 
148
137
  def ability_value_idx(ability, value)
149
- abilities_model[ability].index(value)
138
+ abilities_model[ability].index(value) || -1
150
139
  end
151
140
 
152
141
  def abilities_model
@@ -163,56 +152,6 @@ module Eco
163
152
  @policy_groups
164
153
  end
165
154
 
166
- def presets_custom
167
- return @presets_custom if instance_variable_defined?(:@presets_custom)
168
- @presets_custom = nil
169
- if @presets_custom_file
170
- if (file = File.expand_path(@presets_custom_file)) && File.exists?(file)
171
- @presets_custom = JSON.load(File.open(file)).tap do |custom_presets|
172
- errors = custom_presets.each_with_object([]) do |(key, preset), errors|
173
- if err = preset_errors(preset)
174
- errors << "{ '#{key}' preset -> #{err}}"
175
- end
176
- if err = preset_integrity(preset)
177
- errors << "{ '#{key}' preset -> #{err}}"
178
- end
179
- end
180
-
181
- fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0
182
- end
183
- end
184
- end
185
- end
186
-
187
- def presets_map
188
- return @presets_map if instance_variable_defined?(:@presets_map)
189
- @presets_map = nil
190
- if @presets_map_file
191
- if (file = File.expand_path(@presets_map_file)) && File.exists?(file)
192
- fatal("Maps file specified without 'presets_custom.json' file. Aborting!") if !presets_custom
193
- @presets_map = JSON.load(File.open(file)).tap do |map_presets|
194
-
195
- errors = []
196
- if policy_groups.length > 0
197
- errors = policy_groups.map do |pg|
198
- exists = map_presets[pg.name.downcase] || presets_custom[pg.name.downcase]
199
- exists ? nil : "'#{pg.name}'"
200
- end.compact
201
-
202
- warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0
203
- end
204
-
205
- errors = map_presets.map do |source, dest|
206
- presets_custom[dest] ? nil : "'#{dest}'"
207
- end.compact
208
-
209
- warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0
210
-
211
- end
212
- end
213
- end
214
- end
215
-
216
155
  def fatal(msg)
217
156
  raise msg if !@enviro
218
157
  @enviro.logger.fatal(msg)
@@ -48,5 +48,11 @@
48
48
  "at_least": {"person_core_edit": "edit"}
49
49
  }
50
50
  }
51
+ ],
52
+ "person_abilities": [
53
+ { "value": "view", "conditions": {
54
+ "at_least": {"person_core_edit": "edit"}
55
+ }
56
+ }
51
57
  ]
52
58
  }
@@ -2,14 +2,15 @@
2
2
  "files": [null, "download", "upload", "browse", "administrate"],
3
3
  "data": [null, "view", "update", "administrate", "implement"],
4
4
  "reports": [null, "view", "edit", "administrate"],
5
+ "pages": [null, "view", "update", "create", "administrate"],
6
+ "page_editor": [null, "basic", "intermediate", "advanced", "implement"],
7
+ "registers": [null, "view", "dashboard", "administrate", "implement"],
5
8
  "tasks": [null, "reassign_self", "reassign", "administrate"],
6
9
  "organization": [null, "view", "administrate", "implement"],
7
10
  "person_core": [null, "attach", "view_people_manager", "dashboard"],
8
11
  "person_core_create": [null, "create"],
9
12
  "person_core_edit": [null, "edit"],
10
- "person_account": [null, "view", "create", "edit"],
11
13
  "person_details": [null, "view", "edit_public", "view_private", "edit_private"],
12
- "pages": [null, "view", "update", "create", "administrate"],
13
- "page_editor": [null, "basic", "intermediate", "advanced", "implement"],
14
- "registers": [null, "view", "dashboard", "administrate", "implement"]
14
+ "person_account": [null, "view", "create", "edit"],
15
+ "person_abilities": [null, "view", "edit"]
15
16
  }
@@ -12,7 +12,6 @@ class Eco::API::Policies::DefaultPolicies::UserAccess < Eco::API::Common::Loader
12
12
  people.each do |person|
13
13
  remove_account_when_no_email!(person) if person.email.to_s.empty?
14
14
  person.account.policy_group_ids = defid if no_policy_group_ids?(person)
15
- refresh_abilities!(person)
16
15
  end
17
16
 
18
17
  warn_account_removal!
@@ -40,39 +39,10 @@ class Eco::API::Policies::DefaultPolicies::UserAccess < Eco::API::Common::Loader
40
39
  return !!person.original_doc["account"]
41
40
  end
42
41
 
43
- def refresh_abilities!(person)
44
- return nil if options.dig(:exclude, :abilities)
45
- return nil unless account = person.account
46
- account.permissions_custom = session.new_preset(person)
47
- account.permissions_custom = min_abilities if no_abilities?(person)
48
- end
49
-
50
42
  def no_policy_group_ids?(person)
51
43
  (account = person.account) && account.policy_group_ids.empty?
52
44
  end
53
45
 
54
- def no_abilities?(person)
55
- return true unless account = person.account
56
- account.permissions_custom && account.permissions_custom.values.all?(&:nil?)
57
- end
58
-
59
- def min_abilities
60
- {
61
- "files" => "upload",
62
- "data" => nil,
63
- "reports" => nil,
64
- "pages" => "create",
65
- "page_editor" => "basic",
66
- "registers" => "view",
67
- "organization" => nil,
68
- "person_core" => "attach",
69
- "person_core_edit" => nil,
70
- "person_core_create" => nil,
71
- "person_details" => "view",
72
- "person_account" => nil
73
- }
74
- end
75
-
76
46
  def defid
77
47
  @defid ||= policy_groups.to_id([default_group]).compact
78
48
  end
@@ -61,28 +61,9 @@ module Eco
61
61
  self
62
62
  end
63
63
 
64
- # Builds the presets using the usergroup ids of the input.
65
- # @note for each flag/ability it will take the highest among those mapped for the present usergroups.
66
- # @param input [Ecoportal::API::Internal::Person, Array<String>] the array should be of usegroup names or ids.
67
- # @return [Hash] with custom presets.
68
- def new_preset(input)
69
- case input
70
- when Ecoportal::API::Internal::Person
71
- presets_factory.new(*input&.account&.policy_group_ids)
72
- when Array
73
- presets_factory.new(*input)
74
- else
75
- presets_factory.new(input)
76
- end
77
- end
78
-
79
64
  # Helper to state the abilities that a person should have with given their usergroups
80
65
  def presets_factory
81
- @presets_factory ||= Eco::API::Organization::PresetsFactory.new({
82
- presets_custom: file_manager.dir.file(config.people.presets_custom, should_exist: true),
83
- presets_map: file_manager.dir.file(config.people.presets_map, should_exist: true),
84
- enviro: enviro
85
- })
66
+ @presets_factory ||= Eco::API::Organization::PresetsFactory.new(enviro: enviro)
86
67
  end
87
68
 
88
69
  # Helper to obtain a EntryFactory
@@ -114,14 +114,16 @@ module Eco
114
114
  logger.info(msg) unless silent
115
115
 
116
116
  start_slice = Time.now
117
- people_api.batch do |batch|
118
- slice.each do |person|
119
- batch.public_send(method, person) do |response|
120
- faltal("Request with no response") unless !!response
121
- status[person] = response
117
+ offer_retry_on(Ecoportal::API::Errors::TimeOut) do
118
+ people_api.batch do |batch|
119
+ slice.each do |person|
120
+ batch.public_send(method, person) do |response|
121
+ faltal("Request with no response") unless !!response
122
+ status[person] = response
123
+ end
122
124
  end
123
- end
124
- end # next batch
125
+ end # end batch
126
+ end
125
127
 
126
128
  iteration += 1
127
129
  done += slice.length
@@ -129,6 +131,20 @@ module Eco
129
131
  end
130
132
  end
131
133
 
134
+ def offer_retry_on(error_type, retries_left = 3, &block)
135
+ begin
136
+ block.call
137
+ rescue error_type => e
138
+ raise unless retries_left > 0
139
+ print "Batch TimeOut. You have #{retries_left} retries left. Do you want to retry (y/N)? "
140
+ if (res = STDIN.gets.chomp) && res[0].downcase == "y"
141
+ offer_retry_on(error_type, retries_left - 1, &block)
142
+ else
143
+ raise
144
+ end
145
+ end
146
+ end
147
+
132
148
  def str_stats(start, count)
133
149
  now = Time.now
134
150
  secs = (now - start).round(3)
@@ -297,16 +297,6 @@ module Eco
297
297
  people.default_schema = name
298
298
  end
299
299
 
300
- # Specify the file with the account custom abilities presets
301
- def presets_custom=(file)
302
- people.presets_custom = file
303
- end
304
-
305
- # Specify the file with the usergroup to custom presets mapping
306
- def presets_map=(file)
307
- people.presets_map = file
308
- end
309
-
310
300
  # @see Eco::API::Session::Config::People
311
301
  # @param (see Eco::API::Session::Config::People)
312
302
  # @return [Eco::API::Common::People::PersonParser] parser/serializer for the defined `format`.
@@ -86,23 +86,7 @@ module Eco
86
86
  def default_schema?
87
87
  !!self["default_schema"]
88
88
  end
89
-
90
- def presets_custom=(file)
91
- self["presets_custom"] = file
92
- end
93
-
94
- def presets_custom
95
- self["presets_custom"]
96
- end
97
-
98
- def presets_map=(file)
99
- self["presets_map"] = file
100
- end
101
-
102
- def presets_map
103
- self["presets_map"]
104
- end
105
-
89
+
106
90
  # @return [Hash] with defined pairs format `key` and Person parsers.
107
91
  def parsers
108
92
  self["parsers"] ||= {}