eco-helpers 2.0.16 → 2.0.17

Sign up to get free protection for your applications and to get access to all the features.
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"] ||= {}