eco-helpers 2.0.11 → 2.0.16

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.
@@ -40,6 +40,20 @@ module Eco
40
40
  self
41
41
  end
42
42
 
43
+ # Method to support CLI one-off API requests
44
+ def one_off
45
+ if SCR.get_arg("-api-key")
46
+ key = SCR.get_arg("-api-key", with_param: true)
47
+ enviro = SCR.get_arg("-enviro") ? SCR.get_arg("-enviro", with_param: true) : "live"
48
+ host = "#{enviro}.ecoportal.com"
49
+ org = SCR.get_arg("-org") ? SCR.get_arg("-org", with_param: true) : raise("You should specify -org NAME when using -api-key")
50
+ org = org.downcase.split(/[^a-z]+/).join("_")
51
+ org = org.to_sym
52
+ add(org, key: key, host: host)
53
+ return org
54
+ end
55
+ end
56
+
43
57
  def active_api
44
58
  self["active-api"]
45
59
  end
@@ -14,6 +14,13 @@ module Eco
14
14
  end
15
15
 
16
16
  def working_directory
17
+ if config.apis.active_api&.one_off?
18
+ one_off_dir = File.join("one_off", config.apis.active_name.to_s)
19
+ unless full_path = Eco::Data::Files::Directory.new(one_off_dir).create
20
+ raise "Could not create the folder '#{full_path}'"
21
+ end
22
+ self["dir"] = one_off_dir
23
+ end
17
24
  self["dir"]
18
25
  end
19
26
 
@@ -9,7 +9,7 @@ module Eco
9
9
  end
10
10
 
11
11
  def cache
12
- self["cache"]
12
+ self["cache"] ||= "cache/people.json"
13
13
  end
14
14
 
15
15
  def partial_cache
@@ -35,7 +35,7 @@ module Eco
35
35
  end
36
36
 
37
37
  def requests_folder
38
- self["requests_folder"]
38
+ self["requests_folder"] ||= "requests"
39
39
  end
40
40
 
41
41
  # people to exclude from update feeds
@@ -30,6 +30,7 @@ module Eco
30
30
  @usecases = {}
31
31
  @cache_init = false
32
32
  @cases_by_name = {}
33
+ init_caches
33
34
  end
34
35
 
35
36
  def length
@@ -79,6 +80,7 @@ module Eco
79
80
 
80
81
  # @return [Eco::API::UseCases] a copy of instance object of `self`.
81
82
  def dup
83
+ init_caches
82
84
  self.class.new.merge(self)
83
85
  end
84
86
 
@@ -9,6 +9,7 @@ module Eco
9
9
  end
10
10
  end
11
11
 
12
+ require_relative 'default_cases/abstract_policygroup_abilities_case.rb'
12
13
  require_relative 'default_cases/append_usergroups_case'
13
14
  require_relative 'default_cases/change_email_case'
14
15
  require_relative 'default_cases/codes_to_tags_case'
@@ -32,6 +33,8 @@ require_relative 'default_cases/reset_landing_page_case'
32
33
  require_relative 'default_cases/restore_db_case'
33
34
  require_relative 'default_cases/set_default_tag_case'
34
35
  require_relative 'default_cases/set_supervisor_case'
36
+ require_relative 'default_cases/supers_hierarchy_case'
37
+ require_relative 'default_cases/supers_cyclic_identify_case'
35
38
  require_relative 'default_cases/switch_supervisor_case'
36
39
  require_relative 'default_cases/to_csv_case'
37
40
  require_relative 'default_cases/to_csv_detailed_case'
@@ -0,0 +1,161 @@
1
+ class Eco::API::UseCases::DefaultCases::AbstractPolicyGroupAbilities < Eco::API::Common::Loaders::UseCase
2
+ name "abstract-policygroup-abilities"
3
+ type :export
4
+
5
+ attr_reader :session, :people, :options
6
+
7
+ def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
10
+ generate_csv!
11
+ exit(0)
12
+ end
13
+
14
+ private
15
+
16
+ def file
17
+ @file ||= options.dig(:output, :file) || "suggested_abilities.csv"
18
+ end
19
+
20
+ def generate_csv!
21
+ policy_groups.each_with_object({}) do |group, data|
22
+ data[group.id] ||= {}
23
+ data[group.id][:name] = group.name
24
+ data[group.id][:suggested] = group_suggestions(group.id)
25
+ data[group.id][:percents] = group_abilities(group.id)
26
+ end.yield_self do |data|
27
+ abilities_list = Eco::API::Organization::PresetsFactory.abilities
28
+ CSV.open(file, "w") do |csv|
29
+ csv << ["Type", "UserGroup", "ID", *abilities_list]
30
+ # Dump the final suggestions
31
+ data.each do |id, meta|
32
+ csv << ["Suggested", meta[:name], id, *meta[:suggested].values_at(*abilities_list)]
33
+ end
34
+ # Dump the percentaged levels of each ability
35
+ data.each do |id, meta|
36
+ analysis = meta[:percents].values_at(*abilities_list).each_with_object([]) do |levels, values|
37
+ values << levels.map do |level, percentil|
38
+ "#{level ? level : "null"} => #{percentil}"
39
+ end.join("\n")
40
+ end
41
+ csv << ["Analysis", meta[:name], id, *analysis]
42
+ end
43
+ end
44
+ puts "Generated file #{file}"
45
+ end
46
+ end
47
+
48
+ def policy_groups
49
+ @policy_groups ||= session.policy_groups
50
+ end
51
+
52
+ # Suggests 1 final set of abilities for a usergroup
53
+ def group_suggestions(id)
54
+ group_abilities(id).each_with_object({}) do |(key, levels), set|
55
+ data = levels.to_a.sort_by(&:last).reverse.first
56
+ set[key] = data ? data.shift : nil
57
+ end
58
+ end
59
+
60
+ # Cleans up each ability's levels by:
61
+ # 1. cutting at some minimum threshold percentage
62
+ # 2. directly selecting those greater than 70%
63
+ def group_abilities(id)
64
+ @group_abilities ||= {}
65
+ @group_abilities[id] ||= ability_levels_percent(id).each_with_object({}) do |(ability, levels), out|
66
+ outstanding = nil
67
+ levels.select do |level, percentil|
68
+ outstanding ||= level if percentil >= 75
69
+ percentil > 15
70
+ end.yield_self do |filtered|
71
+ out[ability] = outstanding ? filtered.slice(outstanding) : filtered
72
+ end
73
+ end
74
+ end
75
+
76
+ # With given the percentages of sets of abilities
77
+ # it abstracts the percentage of each level of each ability
78
+ def ability_levels_percent(id)
79
+ @ability_levels_percent ||= {}
80
+ @ability_levels_percent[id] ||= Eco::API::Organization::PresetsFactory.abilities.each_with_object({}) do |key, out|
81
+ out[key] ||= {}
82
+ ability_sets_percent(id).each_with_object(out[key]) do |(set, percentil), levels|
83
+ levels[set[key]] ||= 0
84
+ levels[set[key]] = (levels[set[key]] + percentil).round(2)
85
+ end
86
+ end
87
+ end
88
+
89
+ # Give a percentage to each set of abilities
90
+ def ability_sets_percent(id)
91
+ @ability_sets_percent ||= {}
92
+ @ability_sets_percent[id] ||= scoped_relevant_raw_data(id).yield_self do |data|
93
+ # Transform ability sets counter to percentage
94
+ total = data[:count]
95
+ abilities = data[:abilities]
96
+ data[:abilities].transform_values do |val|
97
+ percent(val, total)
98
+ end
99
+ end
100
+ end
101
+
102
+ # Get rid of data simingly irrelevant
103
+ def scoped_relevant_raw_data(id)
104
+ sp = single_percent(id)
105
+ sing = single(id); mult = multiple(id)
106
+
107
+ # Scope Relevant Raw Data
108
+ case
109
+ when sp >= 80
110
+ sing
111
+ when sp < 15
112
+ mult
113
+ else # combine
114
+ all_abilities = sing[:abilities].keys | mult[:abilities].keys
115
+ data = {count: sing[:count] + mult[:count], abilities: {}}
116
+ all_abilities.each_with_object(data) do |abilities, merged|
117
+ scount = sing[:abilities][abilities] || 0
118
+ mcount = mult[:abilities][abilities] || 0
119
+ merged[:abilities][abilities] = scount + mcount
120
+ end
121
+ end
122
+ end
123
+
124
+ def single_percent(id)
125
+ percent(single(id)[:count], count(id))
126
+ end
127
+
128
+ def single(id)
129
+ groups_abilities.dig(id, :single) || {count: 0, abilities: {}}
130
+ end
131
+
132
+ def multiple(id)
133
+ groups_abilities.dig(id, :multiple) || {count: 0, abilities: {}}
134
+ end
135
+
136
+ def count(id)
137
+ groups_abilities.dig(id, :count) || 0
138
+ end
139
+
140
+ def groups_abilities
141
+ @groups_abilities ||= people.users.each_with_object({}) do |user, groups|
142
+ abilities = Eco::API::Organization::PresetsFactory.all_abilities(user.account.permissions_custom)
143
+ ids = user.account.policy_group_ids
144
+ category = ids.count > 1 ? :multiple : :single
145
+
146
+ ids.each do |id|
147
+ groups[id] ||= {count: 0}
148
+ groups[id][:count] += 1
149
+ groups[id][category] ||= {count: 0, abilities: {}}
150
+ groups[id][category][:count] += 1
151
+ groups[id][category][:abilities][abilities] ||= 0
152
+ groups[id][category][:abilities][abilities] += 1
153
+ end
154
+ end
155
+ end
156
+
157
+ def percent(num, total)
158
+ (100 * num.to_f / total).round(2)
159
+ end
160
+
161
+ end
@@ -2,18 +2,17 @@ class Eco::API::UseCases::DefaultCases::HrisCase < Eco::API::Common::Loaders::Us
2
2
  name "hris"
3
3
  type :sync
4
4
 
5
+ attr_reader :creation, :update, :supers, :leavers
6
+
5
7
  def main(entries, people, session, options, usecase)
6
8
  micro = session.micro
7
- creation = session.new_job("main", "create", :create, usecase)
8
- update = session.new_job("main", "update", :update, usecase)
9
- supers = session.new_job("post", "supers", :update, usecase, :core)
10
- leavers = session.new_job("post", "leavers", :update, usecase, :account)
9
+ @creation = session.new_job("main", "create", :create, usecase)
10
+ @update = session.new_job("main", "update", :update, usecase)
11
+ @supers = session.new_job("post", "supers", :update, usecase, :core)
12
+ @leavers = session.new_job("post", "leavers", :update, usecase, :account)
11
13
 
12
14
  micro.with_each_leaver(entries, people, options) do |person|
13
- leavers.add(person) do |person|
14
- person.supervisor_id = nil
15
- person.account = nil if person.account
16
- end
15
+ leavers.add(person, &method(:leavers_callback))
17
16
  end
18
17
 
19
18
  micro.with_each(entries, people, options) do |entry, person|
@@ -24,4 +23,11 @@ class Eco::API::UseCases::DefaultCases::HrisCase < Eco::API::Common::Loaders::Us
24
23
  end
25
24
  end
26
25
 
26
+ private
27
+
28
+ def leavers_callback(person)
29
+ person.supervisor_id = nil
30
+ person.account = nil if person.account
31
+ end
32
+
27
33
  end
@@ -0,0 +1,72 @@
1
+ class Eco::API::UseCases::DefaultCases::SupersCyclicIdentify < Eco::API::Common::Loaders::UseCase
2
+ name "identify-cyclic-supers"
3
+ type :export
4
+
5
+ attr_reader :session, :people, :options
6
+
7
+ def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
10
+ save!(cyclic_sets)
11
+ exit(0)
12
+ end
13
+
14
+ private
15
+
16
+ def cyclic_sets
17
+ Eco::API::Common::People::SupervisorHelpers.identify_cyclic_chains(people)
18
+ end
19
+
20
+ def file
21
+ @file ||= options.dig(:output, :file) || "cyclic_supers.txt"
22
+ end
23
+
24
+ def save!(data)
25
+ if data.empty?
26
+ session.logger.info("There were no cyclic supervisors identified!!")
27
+ return
28
+ end
29
+
30
+ ext = File.extname(file).downcase.delete(".")
31
+
32
+ File.open(file, "w") do |fd|
33
+ if ext == "txt"
34
+ create_file(data, file: file, format: :txt)
35
+ elsif ext == "html"
36
+ puts "html is still not supported"
37
+ exit(1)
38
+ create_file(data, file: file, format: :html)
39
+ elsif ext == "json"
40
+ puts "json is still not supported"
41
+ exit(1)
42
+ create_file(data, file: file, format: :json)
43
+ end
44
+ end
45
+ end
46
+
47
+ def create_file(sets, file:, format: :txt)
48
+ File.open(file, "w") do |fd|
49
+ fd << sets_to_str(sets, format: format)
50
+ end
51
+ puts "Generated file #{file}"
52
+ end
53
+
54
+ def sets_to_str(sets, format: :txt)
55
+ raise "Required Array. Given: #{sets.class}" unless sets.is_a?(Array)
56
+ "".tap do |str|
57
+ sets.each do |set|
58
+ str << set_to_str(set, format: format)
59
+ end
60
+ end
61
+ end
62
+
63
+ def set_to_str(set, lev: 0, format: :txt)
64
+ raise "Required Array. Given: #{set.class}" unless set.is_a?(Array)
65
+ "".tap do |str|
66
+ entry = set.shift
67
+ str << "#{" " * lev}#{(lev > 0)? "+-#{lev}- " : ""}#{entry.name} (#{entry.external_id}|#{entry.email}|#{entry.id})\n"
68
+ str << set_to_str(set, lev: lev + 1, format: format) unless !set || set.empty?
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,59 @@
1
+ class Eco::API::UseCases::DefaultCases::SupersHierarchy < Eco::API::Common::Loaders::UseCase
2
+ name "supers-hierarchy"
3
+ type :export
4
+
5
+ attr_reader :session, :people, :options
6
+
7
+ def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
10
+ save!(hierarchy)
11
+ exit(0)
12
+ end
13
+
14
+ private
15
+
16
+ def hierarchy
17
+ Eco::API::Common::People::SupervisorHelpers.supervisors_tree(people)
18
+ end
19
+
20
+ def file
21
+ @file ||= options.dig(:output, :file) || "supers_hierarchy.txt"
22
+ end
23
+
24
+ def save!(data)
25
+ ext = File.extname(file).downcase.delete(".")
26
+
27
+ File.open(file, "w") do |fd|
28
+ if ext == "txt"
29
+ create_file(data, file: file, format: :txt)
30
+ elsif ext == "html"
31
+ puts "html is still not supported"
32
+ exit(1)
33
+ create_file(data, file: file, format: :html)
34
+ elsif ext == "json"
35
+ puts "json is still not supported"
36
+ exit(1)
37
+ create_file(data, file: file, format: :json)
38
+ end
39
+ end
40
+ end
41
+
42
+ def create_file(tree, file:, format: :txt)
43
+ File.open(file, "w") do |fd|
44
+ fd << tree_to_str(tree, format: format)
45
+ end
46
+ puts "Generated file #{file}"
47
+ end
48
+
49
+ def tree_to_str(tree, lev: 0, format: :txt)
50
+ raise "Required Hash tree structure. Given: #{tree.class}" unless tree.is_a?(Hash)
51
+ "".tap do |str|
52
+ tree.each do |entry, subtree|
53
+ str << "#{" " * lev}#{(lev > 0)? "+-#{lev}- " : ""}#{entry.name} (#{entry.external_id}|#{entry.email}|#{entry.id})\n"
54
+ str << tree_to_str(subtree, lev: lev + 1, format: format) unless !subtree || subtree.empty?
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -2,7 +2,11 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
2
2
  name "to-csv"
3
3
  type :export
4
4
 
5
+ attr_reader :session, :options, :people
6
+
5
7
  def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
6
10
  unless people && !people.empty?
7
11
  session.logger.warn("No source people to create the file... aborting!")
8
12
  return false
@@ -14,32 +18,48 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
14
18
  end
15
19
 
16
20
  session.logger.info("going to create file: #{file}")
17
- CSV.open(file, "w") do |csv|
18
- deps = {"supervisor_id" => {people: people}}
19
- entry = session.new_entry(people.first, dependencies: deps)
20
- header = entry.external_entry.keys
21
-
22
- if options.dig(:nice_header) || options.dig(:export, :options, :nice_header)
23
- name_maps = session.schema.fields_by_alt_id.transform_values do |fld|
24
- fld.name
25
- end.merge({
26
- "policy_group_ids" => "User Group(s)",
27
- "email" => "Email",
28
- "name" => "Name",
29
- "supervisor_id" => "Manager ID",
30
- "filter_tags" => "Locations",
31
- "default_tag" => "Default Location",
32
- "id" => "ecoPortal ID"
33
- })
34
- header = header.map {|name| name_maps[name] ? name_maps[name] : name}
35
- end
36
21
 
37
- csv << header
22
+ CSV.open(file, "w") do |csv|
23
+ csv << spot_header
38
24
  people.each do |person|
39
- csv << session.new_entry(person, dependencies: deps).external_entry.values
25
+ csv << target_entry_type(person).values
40
26
  end
41
27
  end
42
28
  exit(0)
43
29
  end
44
30
 
31
+ private
32
+
33
+ def target_entry_type(person)
34
+ session.new_entry(person, dependencies: deps).yield_self do |person_entry|
35
+ options.dig(:export, :options, :internal_names) ? person_entry.mapped_entry : person_entry.external_entry
36
+ end
37
+ end
38
+
39
+ def deps
40
+ @deps ||= {"supervisor_id" => {people: people}}
41
+ end
42
+
43
+ def spot_header
44
+ entry = target_entry_type(people.first)
45
+ header = entry.keys
46
+
47
+ if options.dig(:nice_header) || options.dig(:export, :options, :nice_header)
48
+ name_maps = session.schema.fields_by_alt_id.transform_values do |fld|
49
+ fld.name
50
+ end.merge({
51
+ "policy_group_ids" => "User Group(s)",
52
+ "email" => "Email",
53
+ "name" => "Name",
54
+ "supervisor_id" => "Manager ID",
55
+ "filter_tags" => "Locations",
56
+ "default_tag" => "Default Location",
57
+ "id" => "ecoPortal ID"
58
+ })
59
+ header = header.map {|name| name_maps[name] ? name_maps[name] : name}
60
+ end
61
+ header
62
+ end
63
+
64
+
45
65
  end