eco-helpers 2.0.14 → 2.0.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -2
  3. data/eco-helpers.gemspec +6 -4
  4. data/lib/eco-helpers.rb +2 -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/entry_factory.rb +26 -9
  10. data/lib/eco/api/common/people/person_entry.rb +5 -2
  11. data/lib/eco/api/common/people/supervisor_helpers.rb +27 -0
  12. data/lib/eco/api/common/session.rb +1 -0
  13. data/lib/eco/api/common/session/base_session.rb +2 -0
  14. data/lib/eco/api/common/session/file_manager.rb +2 -2
  15. data/lib/eco/api/common/session/helpers.rb +30 -0
  16. data/lib/eco/api/common/session/helpers/prompt_user.rb +34 -0
  17. data/lib/eco/api/common/session/mailer.rb +0 -1
  18. data/lib/eco/api/common/session/s3_uploader.rb +0 -1
  19. data/lib/eco/api/common/session/sftp.rb +0 -1
  20. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +1 -1
  21. data/lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb +7 -4
  22. data/lib/eco/api/common/version_patches/exception.rb +8 -4
  23. data/lib/eco/api/microcases.rb +3 -1
  24. data/lib/eco/api/microcases/append_usergroups.rb +0 -1
  25. data/lib/eco/api/microcases/people_cache.rb +2 -2
  26. data/lib/eco/api/microcases/people_load.rb +2 -2
  27. data/lib/eco/api/microcases/people_refresh.rb +2 -2
  28. data/lib/eco/api/microcases/people_search.rb +6 -6
  29. data/lib/eco/api/microcases/preserve_default_tag.rb +23 -0
  30. data/lib/eco/api/microcases/preserve_filter_tags.rb +28 -0
  31. data/lib/eco/api/microcases/preserve_policy_groups.rb +30 -0
  32. data/lib/eco/api/microcases/set_account.rb +0 -1
  33. data/lib/eco/api/microcases/with_each.rb +67 -6
  34. data/lib/eco/api/microcases/with_each_present.rb +4 -2
  35. data/lib/eco/api/microcases/with_each_starter.rb +4 -2
  36. data/lib/eco/api/organization.rb +1 -0
  37. data/lib/eco/api/organization/people.rb +98 -22
  38. data/lib/eco/api/organization/people_similarity.rb +112 -0
  39. data/lib/eco/api/organization/person_schemas.rb +5 -1
  40. data/lib/eco/api/organization/policy_groups.rb +5 -1
  41. data/lib/eco/api/organization/presets_factory.rb +40 -80
  42. data/lib/eco/api/organization/presets_integrity.json +6 -0
  43. data/lib/eco/api/organization/presets_values.json +5 -4
  44. data/lib/eco/api/policies/default_policies/99_user_access_policy.rb +0 -30
  45. data/lib/eco/api/session.rb +6 -22
  46. data/lib/eco/api/session/batch.rb +25 -7
  47. data/lib/eco/api/session/config.rb +16 -15
  48. data/lib/eco/api/session/config/api.rb +4 -0
  49. data/lib/eco/api/session/config/apis.rb +80 -0
  50. data/lib/eco/api/session/config/files.rb +7 -0
  51. data/lib/eco/api/session/config/people.rb +3 -19
  52. data/lib/eco/api/usecases/default_cases.rb +4 -1
  53. data/lib/eco/api/usecases/default_cases/abstract_policygroup_abilities_case.rb +161 -0
  54. data/lib/eco/api/usecases/default_cases/analyse_people_case.rb +53 -0
  55. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +2 -3
  56. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +11 -1
  57. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +1 -2
  58. data/lib/eco/api/usecases/default_cases/supers_cyclic_identify_case.rb +72 -0
  59. data/lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb +59 -0
  60. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +132 -29
  61. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +61 -36
  62. data/lib/eco/api/usecases/ooze_samples/ooze_update_case.rb +3 -2
  63. data/lib/eco/cli.rb +0 -10
  64. data/lib/eco/cli/config/default/options.rb +20 -17
  65. data/lib/eco/cli/config/default/people_filters.rb +3 -3
  66. data/lib/eco/cli/config/default/usecases.rb +80 -26
  67. data/lib/eco/cli/config/default/workflow.rb +16 -4
  68. data/lib/eco/cli/config/help.rb +1 -0
  69. data/lib/eco/cli/config/options_set.rb +106 -13
  70. data/lib/eco/cli/config/use_cases.rb +33 -33
  71. data/lib/eco/cli/scripting/args_helpers.rb +30 -3
  72. data/lib/eco/csv.rb +4 -2
  73. data/lib/eco/data.rb +1 -0
  74. data/lib/eco/data/crypto/encryption.rb +3 -3
  75. data/lib/eco/data/files/directory.rb +28 -20
  76. data/lib/eco/data/files/helpers.rb +6 -4
  77. data/lib/eco/data/fuzzy_match.rb +161 -0
  78. data/lib/eco/data/fuzzy_match/array_helpers.rb +75 -0
  79. data/lib/eco/data/fuzzy_match/chars_position_score.rb +37 -0
  80. data/lib/eco/data/fuzzy_match/ngrams_score.rb +78 -0
  81. data/lib/eco/data/fuzzy_match/pairing.rb +101 -0
  82. data/lib/eco/data/fuzzy_match/result.rb +73 -0
  83. data/lib/eco/data/fuzzy_match/results.rb +59 -0
  84. data/lib/eco/data/fuzzy_match/score.rb +44 -0
  85. data/lib/eco/data/fuzzy_match/stop_words.rb +35 -0
  86. data/lib/eco/data/fuzzy_match/string_helpers.rb +69 -0
  87. data/lib/eco/version.rb +1 -1
  88. metadata +87 -10
  89. data/lib/eco/api/microcases/refresh_abilities.rb +0 -19
  90. data/lib/eco/api/organization/presets_reference.json +0 -59
  91. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +0 -30
@@ -0,0 +1,112 @@
1
+ module Eco
2
+ module API
3
+ module Organization
4
+
5
+ # Class to find out duplicates in the People Manager
6
+ #
7
+ # @attr_writer attribute [String, Proc, nil] the target attribute to be read.
8
+ class PeopleSimilarity < Eco::API::Organization::People
9
+ include Eco::Data::FuzzyMatch
10
+
11
+ attr_accessor :attribute
12
+
13
+ # @!group Config
14
+ # @return [String, Proc, nil] the target attribute to be read.
15
+ def attribute=(attr)
16
+ @attribute ||= "name"
17
+ end
18
+
19
+ # Define the order or relevant of per user matches
20
+ # @param values[Array<Symbol>] the algorithms' results it should be ordered by
21
+ # * Possible values: `:dice`, `:levenshtein`, `:jaro_winkler`, `:ngrams`, `:words_ngrams`, `:chars_position`
22
+ def order=(values)
23
+ @order = values
24
+ end
25
+
26
+ def order
27
+ @order ||= [:words_ngrams, :dice]
28
+ end
29
+
30
+ # Define the order or relevant of per user matches
31
+ # @param value [Float] the threshold that all of the algorithms should comply with
32
+ def threshold=(value)
33
+ @threshold = value
34
+ end
35
+
36
+ def threshold
37
+ @threshold ||= 0.15
38
+ end
39
+
40
+ # @!endgroup
41
+
42
+ # @!group Searchers
43
+
44
+ # It gathers those that have the same `email`
45
+ # @return [Hash] where `keys` are `email`s and `values` an `Array<Person>`
46
+ def repeated_emails
47
+ init_caches
48
+ @by_email.select do |email, people|
49
+ people.count > 1
50
+ end
51
+ end
52
+
53
+ # @!endgroup
54
+
55
+ # @!group Analysers
56
+
57
+ # Analyses People bases on `options`
58
+ # @return [Hash] where the _keys_ are the people `id`s and the _values_ the `Eco::Data::FuzzyMatch::Results`
59
+ def analyse(**options)
60
+ options = { read: self.attribute }.merge(options)
61
+ each_with_object({}) do |person, results|
62
+ results[person.id] = find_all_with_score(person, **options)
63
+ end
64
+ end
65
+
66
+ # Launches a reanalyis on `analysed` based on `options`
67
+ # @param analysed [Hash] where the _keys_ are the people `id`s and the _values_ the `Eco::Data::FuzzyMatch::Results`
68
+ def re_analyse(analysed, **options)
69
+ analysed.each_with_object({}) do |(id, results), out|
70
+ out[id] = results.relevant_results(**options)
71
+ end
72
+ end
73
+
74
+ # @!group Helpers
75
+
76
+ # @return [String] well structured text
77
+ def analysis(analysed, format: :txt)
78
+ case
79
+ when format == :txt
80
+ analysed.each_with_object("") do |(id, results), out|
81
+ msg = results.results.map {|r| r.print}.join("\n ")
82
+ "'#{self[id].identify}':\n " + msg
83
+ end
84
+ end
85
+ end
86
+
87
+ # @note
88
+ # 1. Unless `:analysed` is provided, it launches an analysis cutting with Jaro Winker min 0.5
89
+ # 2. It then re-sorts and cuts based on `options`
90
+ # @return [Hash] where the _keys_ are the people `id`s and the _values_ the `Eco::Data::FuzzyMatch::Results`
91
+ def print_analysis(**options)
92
+ analysed = options[:analysed] || results_with_false_positives.analyse(**options)
93
+ analysed.each_with_object({}) do |(id, results), out|
94
+ puts analysis(analysed)
95
+ end
96
+ end
97
+ # @!endgroup
98
+
99
+ protected
100
+
101
+ def on_change
102
+ remove_instance_variable(@fuzzy_match)
103
+ super
104
+ end
105
+
106
+ private
107
+
108
+
109
+ end
110
+ end
111
+ end
112
+ end
@@ -28,7 +28,11 @@ module Eco
28
28
  end
29
29
 
30
30
  def schema(id_name)
31
- @by_id.fetch(schema_id(id_name), nil)
31
+ self[id_name]
32
+ end
33
+
34
+ def [](id_name)
35
+ @by_id[schema_id(id_name)]
32
36
  end
33
37
 
34
38
  private
@@ -44,7 +44,11 @@ module Eco
44
44
  end
45
45
 
46
46
  def policy_group(id_name)
47
- @by_id.fetch(policy_group_id(id_name), nil)
47
+ self[id_name]
48
+ end
49
+
50
+ def [](id_name)
51
+ @by_id[policy_group_id(id_name)]
48
52
  end
49
53
 
50
54
  def user_pg_ids(initial: [], final: [], non_custom: (non_custom_not_used = true; []), preserve_custom: true)
@@ -5,42 +5,57 @@ module Eco
5
5
  class PresetsFactory
6
6
  ABILITIES = File.join(__dir__, 'presets_values.json')
7
7
  INTEGRITY = File.join(__dir__, 'presets_integrity.json')
8
- DEFAULT_CUSTOM = 'presets_custom.json'
9
- DEFAULT_MAP = 'presets_map.json'
10
8
 
11
- def initialize(presets_custom: DEFAULT_CUSTOM, presets_map: DEFAULT_MAP, enviro: nil, policy_groups: nil)
12
- fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
13
- @enviro = enviro
9
+ class << self
14
10
 
15
- @policy_groups = policy_groups
16
- @presets_custom_file = presets_custom || DEFAULT_CUSTOM
17
- @presets_map_file = presets_map || DEFAULT_MAP
18
- end
11
+ def all_abilities(hash = {})
12
+ Hash[abilities.each_with_object(nil).to_a].merge(hash)
13
+ end
19
14
 
20
- def new(*policy_group_ids_or_names)
15
+ def abilities_model
16
+ @abilities_model ||= JSON.load(File.open(ABILITIES))
17
+ end
21
18
 
22
- names = policy_group_ids_or_names.map do |id_name|
23
- policy_groups.to_name(id_name)&.downcase
24
- end.compact
19
+ def integrity_model
20
+ @integrity_model ||= JSON.load(File.open(INTEGRITY))
21
+ end
25
22
 
26
- if presets_map
27
- preset_names = names.map { |name| presets_map.fetch(name, nil) }
28
- else # option to do not use preset mapping (so just the policy group name)
29
- preset_names = names
23
+ def abilities
24
+ @abilities ||= abilities_model.keys
30
25
  end
31
- compile(*preset_names)
26
+
27
+ end
28
+
29
+ def initialize(enviro: nil, policy_groups: nil)
30
+ fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
31
+ @enviro = enviro
32
+ @policy_groups = policy_groups
32
33
  end
33
34
 
34
35
  # @return [Array<String>] all the abilities
35
36
  def keys
36
- abilities_model.keys
37
+ self.class.abilities
38
+ end
39
+
40
+ def valid?(preset)
41
+ validate(perset).length == 0
42
+ end
43
+
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
37
53
  end
38
54
 
39
55
  private
40
56
 
41
- def compile(*preset_names)
42
- fatal("You need to specify an existing file for the custom presets.") if !@presets_custom
43
- @presets_custom.values_at(*preset_names).compact.reduce({}) do |p1, p2|
57
+ def compile(*presets)
58
+ presets.compact.reduce({}) do |p1, p2|
44
59
  merge(p1, p2)
45
60
  end
46
61
  end
@@ -58,11 +73,6 @@ module Eco
58
73
  end
59
74
  end
60
75
 
61
- # unsused: only play with the given abilities
62
- def empty_model
63
- JSON.parse(abilities_model.to_json).transform_values {|v| nil }
64
- end
65
-
66
76
  def preset_errors(preset)
67
77
  return "No preset given" if !preset
68
78
  errors = preset.map do |k, v|
@@ -111,7 +121,7 @@ module Eco
111
121
  end
112
122
 
113
123
  def integrity_model
114
- @integrity_model ||= JSON.load(File.open(INTEGRITY))
124
+ self.class.integrity_model
115
125
  end
116
126
 
117
127
  def value_exists?(ability, value)
@@ -125,11 +135,11 @@ module Eco
125
135
  end
126
136
 
127
137
  def ability_value_idx(ability, value)
128
- abilities_model[ability].index(value)
138
+ abilities_model[ability].index(value) || -1
129
139
  end
130
140
 
131
141
  def abilities_model
132
- @abilities_model ||= JSON.load(File.open(ABILITIES))
142
+ self.class.abilities_model
133
143
  end
134
144
 
135
145
  def policy_groups
@@ -142,56 +152,6 @@ module Eco
142
152
  @policy_groups
143
153
  end
144
154
 
145
- def presets_custom
146
- return @presets_custom if instance_variable_defined?(:@presets_custom)
147
- @presets_custom = nil
148
- if @presets_custom_file
149
- if (file = File.expand_path(@presets_custom_file)) && File.exists?(file)
150
- @presets_custom = JSON.load(File.open(file)).tap do |custom_presets|
151
- errors = custom_presets.each_with_object([]) do |(key, preset), errors|
152
- if err = preset_errors(preset)
153
- errors << "{ '#{key}' preset -> #{err}}"
154
- end
155
- if err = preset_integrity(preset)
156
- errors << "{ '#{key}' preset -> #{err}}"
157
- end
158
- end
159
-
160
- fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0
161
- end
162
- end
163
- end
164
- end
165
-
166
- def presets_map
167
- return @presets_map if instance_variable_defined?(:@presets_map)
168
- @presets_map = nil
169
- if @presets_map_file
170
- if (file = File.expand_path(@presets_map_file)) && File.exists?(file)
171
- fatal("Maps file specified without 'presets_custom.json' file. Aborting!") if !presets_custom
172
- @presets_map = JSON.load(File.open(file)).tap do |map_presets|
173
-
174
- errors = []
175
- if policy_groups.length > 0
176
- errors = policy_groups.map do |pg|
177
- exists = map_presets[pg.name.downcase] || presets_custom[pg.name.downcase]
178
- exists ? nil : "'#{pg.name}'"
179
- end.compact
180
-
181
- warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0
182
- end
183
-
184
- errors = map_presets.map do |source, dest|
185
- presets_custom[dest] ? nil : "'#{dest}'"
186
- end.compact
187
-
188
- warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0
189
-
190
- end
191
- end
192
- end
193
- end
194
-
195
155
  def fatal(msg)
196
156
  raise msg if !@enviro
197
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
@@ -93,12 +74,15 @@ module Eco
93
74
  def entry_factory(schema: nil)
94
75
  schema = to_schema(schema) || self.schema
95
76
  return @entry_factories[schema&.id] if @entry_factories.key?(schema&.id)
77
+ unless @entry_factories.empty?
78
+ @entry_factories[schema&.id] = @entry_factories.values.first.newFactory(schema: schema)
79
+ return @entry_factories[schema&.id]
80
+ end
96
81
 
97
82
  mappings = []
98
83
  if map_file = config.people.fields_mapper
99
84
  mappings = map_file ? file_manager.load_json(map_file) : []
100
85
  end
101
-
102
86
  @entry_factories[schema&.id] = Eco::API::Common::People::EntryFactory.new(
103
87
  enviro,
104
88
  schema: schema,
@@ -146,7 +130,7 @@ module Eco
146
130
  # @see Eco::API::Common::People::EntryFactory#new
147
131
  # @return [Eco::API::Common::People::PersonEntry] parsed entry.
148
132
  def new_entry(data, dependencies: {})
149
- entry_factory.new(data, dependencies: dependencies)
133
+ entry_factory(schema: data&.details&.schema_id).new(data, dependencies: dependencies)
150
134
  end
151
135
 
152
136
  # @see Eco::API::Common::People::EntryFactory#entries