ogam 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0bd4c39f063cc77d960339f8a828b160734f410e6b31d3ecb0cec94e5ddfaf21
4
- data.tar.gz: 83a9fd885e54efff8ba93672d42bb32b517f98f1478893c43939ad7cfaa1f31f
3
+ metadata.gz: d4a72e5717ef8b802f79499f53fb603ddc9ba61dbd06fa166c9a3045d877b748
4
+ data.tar.gz: d4cf4b93af132811e4a6ba058e0440ddc60115a414a7c967bb96c0a34363ffbd
5
5
  SHA512:
6
- metadata.gz: 309fbc943d2d17660050afa90b18c3a2c08329f85fdd2dfaefd92d7c93c8a37ccae3a62e3cf8b5a957299f840269d69c40813811e40c8c37d87f5aea71dbf6c2
7
- data.tar.gz: 7d38bbbedda7261c9dc127626fdeef0cd1b2bd41f59812fb7a359fae72e5682ccb22165ffed2ae354bbe1481d90dff4e9718b73656ee5088f24d742b63e44cc7
6
+ metadata.gz: d4443a1c829534b5ebea13518d00e8d7d1b66dd8cfb2a2b095bbec52478392ac9f09cb8fc0d240482dfaa202e04ef210149e310ecc3a65316be51960a72440bf
7
+ data.tar.gz: 93d3527909a5d33d90f66cb70dcd170622ae9a947294548552431052005e3e2cd66235d6930cbe6e4e5f57a02952f09baa36cb9c3804bdad5672dcb42d0d986c
data/.rubocop.yml CHANGED
@@ -16,3 +16,9 @@ Metrics/BlockLength:
16
16
  Exclude:
17
17
  - 'spec/**/*.rb'
18
18
  - 'ogatstyle.gemspec'
19
+
20
+ Rails/Output:
21
+ Enabled: false
22
+
23
+ Rails/SaveBang:
24
+ Enabled: false
@@ -0,0 +1,59 @@
1
+ Metrics/AbcSize:
2
+ Exclude:
3
+ - db/**/*
4
+ - spec/**/*
5
+ - config/routes.rb
6
+ - lib/ogam/group_assigner.rb
7
+
8
+ Metrics/BlockLength:
9
+ Exclude:
10
+ - db/**/*
11
+ - spec/**/*
12
+ - config/routes.rb
13
+
14
+ Metrics/BlockNesting:
15
+ Exclude:
16
+ - db/**/*
17
+ - spec/**/*
18
+ - config/routes.rb
19
+
20
+ Metrics/ClassLength:
21
+ Exclude:
22
+ - db/**/*
23
+ - spec/**/*
24
+ - config/routes.rb
25
+ - lib/ogam/group_assigner.rb
26
+
27
+ Metrics/CyclomaticComplexity:
28
+ Exclude:
29
+ - db/**/*
30
+ - spec/**/*
31
+ - config/routes.rb
32
+ - lib/ogam/group_assigner.rb
33
+
34
+ Metrics/MethodLength:
35
+ Exclude:
36
+ - db/**/*
37
+ - spec/**/*
38
+ - config/routes.rb
39
+ - lib/ogam/group_assigner.rb
40
+
41
+ Metrics/ModuleLength:
42
+ Exclude:
43
+ - db/**/*
44
+ - spec/**/*
45
+ - config/routes.rb
46
+
47
+ Metrics/ParameterLists:
48
+ Exclude:
49
+ - db/**/*
50
+ - spec/**/*
51
+ - config/routes.rb
52
+
53
+ Metrics/PerceivedComplexity:
54
+ Exclude:
55
+ - db/**/*
56
+ - spec/**/*
57
+ - config/routes.rb
58
+ - lib/ogam/group_assigner.rb
59
+
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config --exclude-limit 1000 --no-offense-counts --no-auto-gen-timestamp`
3
- # using RuboCop version 0.79.0.
3
+ # using RuboCop version 0.80.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
data/.travis.yml CHANGED
@@ -3,22 +3,20 @@ language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
5
  - 2.6.5
6
- before_install:
7
- - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
8
- - gem install bundler -v '~> 1.17.3'
9
6
  before_script:
10
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
7
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64
8
+ > ./cc-test-reporter
11
9
  - chmod +x ./cc-test-reporter
12
- - ./cc-test-reporter before-build
10
+ - './cc-test-reporter before-build'
13
11
  script:
14
12
  - bundle exec rubocop
15
13
  - bundle exec rake
16
14
  after_script:
17
- - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
15
+ - './cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT'
18
16
  deploy:
19
17
  provider: rubygems
20
18
  api_key:
21
- secure: kazURR5idcANlaYnqiGLnkXfsYWluTc3H87sg73H6s2JwEkA69FieZA3tUrDVX8kfbOxsqDphyv3fZmqrQtmG74ksUPVGsouoth7gpUMNh6Uj8Hr/e3Cgt/4b/SeGCAdpIsx869w8fA9kEKuf1NhQ0taazfiqToau3amyal6QblxMaalX8ThvWtO98ViJkEeIszxRK+uzd3t1aIEW/Lds9jWUm9Rf1tI/ajlRecwStA74pCQHJ2wuPQRcAB8DYz82pNjlyGwIZd14Gmbys0en+r+av3tYLjzwN+z9y7vlXwp1xAeRKU9GoJCHuevKmT1joRnFFfp3xGoKn63b6yS7KHVFT7v4l+qRbMnL+m/wrz5Xv3ZUyk0un9O3+MFLy0jIYP1adcvh+KkqenAktzSekjGstZn8oRk8lAEYfGLUD45nijdGjbYF7VAUPhoP5g6vxGlbZDCHslHuLGMjiEd/vP+SGEhtEMzk6cvir1yoMzryilRTfBfkut7iVsy/MaMvTShxF0qd1hdtx+Htfcq2zad98lAASsciz8su6u9vaqmqyxLKxdMptbWks4bCd0lB9g6fjIT79cIzjwQB6Hv3WNDpMLGDxWH8PkZf4ZEIlvV1goLlzt5t0SoZryn1hWbO4VJMvGLXtVj9MvdgskN95Ww9FTvO3wlm0PVK5iQWkU=
19
+ secure: K5oq3QVDaNCEoeDlj9PQgE5oslHTTzzO33SA4XVRPqV/k6S2ASs0iCyAp0LPPFup9NPivw8C64mb41erSY4f/lW8a2tkfVYL28avBiy4n/kioo/hFKjL3WBoUGknsamoDQUBTCrUPtoQE5puducV44cH9AyPFJulXbw4hA8KXisi7xYc+e5NGSsTDhjlj4GBN0G3AOBgZ/x1dqLdG8/NV7Bqwr3fKHqpBEI4nKtGTzWUsoC+4SQVkWKwPzKcme47AiRZa5moKs6xzJQzamJm6sGzk0SQ24PSvDxv/AE6AJe67xbdSzxgN2haSLLTV99AW4ku9dP6JE8i799f9337lmCWNE24GCqlAAURR3GDGtMcv0pUCR12V39wJSdA0C3h4xDriGgSHM69xnY6I4hh0DZCSXMDDAHFQMSF5JU4zOQDKZhme2pUTQwTw0uNoutDp6/YF9kMeq+dzZygKMjtDFVy95IV3N9vXBMHvNQAohNYGVZ47Yy4n6Ss/AOHu6aqWPyFo0VSD4WAzFNJBtZvnMlleS+nRrpxjxaH+LkeqE0VkQU6IFeqipAihEeJDizWMXKcXd7RbFPxmsvcyDHh7kF5th5xCGxqW6O3rC2B64mDNA9NKaROJDMQYszeRb7pOuhoysMnzDOy0CwI9R7jZhlWVSHEZeGv4phMhTKhFGc=
22
20
  gem: ogam
23
21
  on:
24
22
  rvm: 2.6.5
data/Gemfile.lock CHANGED
@@ -1,23 +1,37 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ogam (0.1.0)
4
+ ogam (1.0.0)
5
+ activesupport (~> 6.0.2)
6
+ attr_extras (~> 6.2.3)
5
7
  thor (~> 1.0.1)
6
8
 
7
9
  GEM
8
10
  remote: https://rubygems.org/
9
11
  specs:
12
+ activesupport (6.0.2.1)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (>= 0.7, < 2)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ zeitwerk (~> 2.2)
10
18
  ast (2.4.0)
19
+ attr_extras (6.2.3)
20
+ concurrent-ruby (1.1.6)
11
21
  diff-lcs (1.3)
12
22
  docile (1.3.2)
23
+ i18n (1.8.2)
24
+ concurrent-ruby (~> 1.0)
13
25
  jaro_winkler (1.5.4)
14
26
  json (2.3.0)
27
+ minitest (5.14.0)
15
28
  parallel (1.19.1)
16
29
  parser (2.7.0.2)
17
30
  ast (~> 2.4.0)
18
31
  rack (2.2.2)
19
32
  rainbow (3.0.0)
20
33
  rake (13.0.1)
34
+ rexml (3.2.4)
21
35
  rspec (3.9.0)
22
36
  rspec-core (~> 3.9.0)
23
37
  rspec-expectations (~> 3.9.0)
@@ -31,15 +45,16 @@ GEM
31
45
  diff-lcs (>= 1.2.0, < 2.0)
32
46
  rspec-support (~> 3.9.0)
33
47
  rspec-support (3.9.2)
34
- rubocop (0.79.0)
48
+ rubocop (0.80.0)
35
49
  jaro_winkler (~> 1.5.1)
36
50
  parallel (~> 1.10)
37
51
  parser (>= 2.7.0.1)
38
52
  rainbow (>= 2.2.2, < 4.0)
53
+ rexml
39
54
  ruby-progressbar (~> 1.7)
40
55
  unicode-display_width (>= 1.4.0, < 1.7)
41
- rubocop-ogat (1.46.1)
42
- rubocop (= 0.79.0)
56
+ rubocop-ogat (1.47.0)
57
+ rubocop (= 0.80.0)
43
58
  rubocop-performance (= 1.5.2)
44
59
  rubocop-rails (= 2.4.2)
45
60
  rubocop-rspec (= 1.38.1)
@@ -57,7 +72,11 @@ GEM
57
72
  simplecov-html (~> 0.10.0)
58
73
  simplecov-html (0.10.2)
59
74
  thor (1.0.1)
75
+ thread_safe (0.3.6)
76
+ tzinfo (1.2.6)
77
+ thread_safe (~> 0.1)
60
78
  unicode-display_width (1.6.1)
79
+ zeitwerk (2.2.2)
61
80
 
62
81
  PLATFORMS
63
82
  ruby
@@ -67,7 +86,7 @@ DEPENDENCIES
67
86
  ogam!
68
87
  rake (~> 13.0)
69
88
  rspec (~> 3.0)
70
- rubocop-ogat (~> 1.46.1)
89
+ rubocop-ogat (~> 1.47.0)
71
90
  simplecov (~> 0.17.1)
72
91
 
73
92
  BUNDLED WITH
data/README.md CHANGED
@@ -1,28 +1,22 @@
1
1
  # Ogam
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ogam`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Wrapper around GAM for common tasks with OGAT's G Suite instance.
6
4
 
7
5
  ## Installation
8
6
 
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'ogam'
13
- ```
14
-
15
- And then execute:
7
+ This tool relies on GAM being installed. First install that using the normal installation procedure, then run:
16
8
 
17
- $ bundle
9
+ $ gem install ogam
18
10
 
19
- Or install it yourself as:
11
+ If running on Google Cloud Shell, ensure the gems bin directory is in your path by appending the following line to ~/.bashrc:
20
12
 
21
- $ gem install ogam
13
+ `PATH=".gems/bin:$PATH"`
22
14
 
23
15
  ## Usage
24
16
 
25
- TODO: Write usage instructions here
17
+ For a list of available commands, run:
18
+
19
+ $ ogam help
26
20
 
27
21
  ## Development
28
22
 
@@ -32,4 +26,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
26
 
33
27
  ## Contributing
34
28
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/elliotb/ogam.
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Outwood/ogam.
data/bin/ogam CHANGED
@@ -4,9 +4,119 @@
4
4
  require 'bundler/setup'
5
5
  require 'ogam'
6
6
  require 'thor'
7
+ require 'shellwords'
7
8
 
9
+ # Signature subcommand cli
10
+ class SignatureCLI < Thor
11
+ desc('user EMAIL', 'Sets the signature for user with primary email EMAIL based on their name, job title, and org')
12
+ def user(email)
13
+ set_signatures(Ogam::User.active_by_email(email), pretend: parent_options[:pretend])
14
+ end
15
+
16
+ desc('domain DOMAIN', 'Sets the signature for all users @DOMAIN based on their name, job title, and org')
17
+ def domain(domain)
18
+ set_signatures(Ogam::User.active_for_domain(domain), pretend: parent_options[:pretend])
19
+ end
20
+
21
+ private
22
+
23
+ def set_signatures(users, pretend:)
24
+ users.each do |user|
25
+ Ogam::Execute.single(Ogam::Signature.update_signature_command(user), pretend: pretend)
26
+ end
27
+ end
28
+ end
29
+
30
+ # Command line interface for OGAM
8
31
  class CLI < Thor
9
- # contents of the Thor class
32
+ class_option :pretend, type: :boolean, default: false
33
+
34
+ desc(
35
+ 'assign_to_standard_groups DOMAIN_OR_EMAIL',
36
+ 'Assigns users at DOMAIN (e.g. newbold.outwood.com) or EMAIL address to standard groups based on their job title'
37
+ )
38
+ def assign_to_standard_groups(domain_or_email)
39
+ method = domain_or_email.include?('@') ? :for_user : :for_domain
40
+ Ogam::GroupAssigner.public_send(method, domain_or_email, pretend: options[:pretend])
41
+ end
42
+
43
+ desc(
44
+ 'populate_titles_and_orgs PATH_TO_CSV',
45
+ 'Populates job titles and organisation names from a CSV with columns primary_email, job_title, organisation'
46
+ )
47
+ def populate_titles_and_orgs(csv_path)
48
+ Ogam::Execute.csv_command(
49
+ csv_path,
50
+ 'gam update user ~primary_email organization name ~organisation title ~job_title primary',
51
+ pretend: options[:pretend]
52
+ )
53
+ end
54
+
55
+ desc('create_shared_mailbox EMAIL NAME', 'Create a shared mailbox')
56
+ def create_shared_mailbox(email, name)
57
+ Ogam::Execute.single(Ogam::Group.create_shared_mailbox_command(email, name), pretend: options[:pretend])
58
+ end
59
+
60
+ option :moderated, type: :boolean, default: false
61
+ desc('create_group EMAIL NAME [--moderated]', 'Create a distribution list group, optionally moderated')
62
+ def create_group(email, name)
63
+ Ogam::Execute.single(
64
+ Ogam::Group.create_distribution_list_command(email, name, options[:moderated]), pretend: options[:pretend]
65
+ )
66
+ end
67
+
68
+ option :organisation, type: :string
69
+ desc(
70
+ 'set_job_title EMAIL TITLE [--organisation ORG_NAME]',
71
+ 'Sets job title for a user, and updates their signature, groups, and organisation name. Optionally override org.'
72
+ )
73
+ def set_job_title(email, title)
74
+ validate_job_title!(title)
75
+
76
+ approved_organisations = Ogam::Organisation.all
77
+ org = infer_organisation_if_necessary(options[:organisation], email, approved_organisations)
78
+ validate_organisation!(org, approved_organisations)
79
+
80
+ set_title_and_org_and_make_changes(email, title, org, pretend: options[:pretend])
81
+ end
82
+
83
+ desc('set_signature user/domain EMAIL/DOMAIN', 'Set email signatures for users/domains based on user attributes')
84
+ subcommand 'set_signature', SignatureCLI
85
+
86
+ private
87
+
88
+ def validate_job_title!(title)
89
+ approved_job_titles = Ogam::JobTitle.all
90
+ new_job_titles = Ogam::JobTitle.job_titles(title)
91
+ new_job_titles.each do |s|
92
+ raise ArgumentError, "'#{s}' is not an approved job title." unless approved_job_titles.include?(s)
93
+ end
94
+ end
95
+
96
+ def validate_organisation!(org, approved_organisations)
97
+ org.split(' / ').each do |org_part|
98
+ unless approved_organisations.value?(org_part)
99
+ raise ArgumentError, "'#{org_part}' is not an approved organisation name."
100
+ end
101
+ end
102
+ end
103
+
104
+ def infer_organisation_if_necessary(org, email, approved_organisations)
105
+ return org if org.present?
106
+
107
+ approved_organisations.fetch(email.split('@').last, '')
108
+ end
109
+
110
+ def set_title_and_org_and_make_changes(email, title, org, pretend: true)
111
+ Ogam::Execute.single(
112
+ "gam update user #{email} organization name #{Shellwords.escape(org)} title #{Shellwords.escape(title)} primary",
113
+ pretend: pretend
114
+ )
115
+ Ogam::GroupAssigner.for_user(email, pretend: pretend)
116
+ Ogam::User.active_by_email(email).each do |user|
117
+ Ogam::Execute.single(Ogam::Signature.update_signature_command(user), pretend: pretend)
118
+ end
119
+ end
10
120
  end
11
121
 
12
122
  CLI.start(ARGV)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'tempfile'
5
+ require 'English'
6
+
7
+ module Ogam
8
+ # Methods to execute GAM commands
9
+ class Execute
10
+ def self.single(command, pretend: true)
11
+ puts "#{pretend ? '[PRETEND] ' : nil}Executing #{command}"
12
+ return if pretend
13
+
14
+ system("~/bin/gam/#{command}", exception: true)
15
+ end
16
+
17
+ def self.fetch(command, pretend: true)
18
+ puts "#{pretend ? '[PRETEND] ' : nil}Fetching results from #{command}"
19
+ return if pretend
20
+
21
+ `~/bin/gam/#{command}`
22
+ end
23
+
24
+ def self.fetch_csv(command, pretend: true)
25
+ result = fetch(command, pretend: pretend)
26
+ return unless result
27
+
28
+ CSV.parse(result, headers: true)
29
+ end
30
+
31
+ def self.csv_command(csv_path, command, pretend: true)
32
+ single("gam csv #{Shellwords.escape(csv_path)} #{command}", pretend: pretend)
33
+ end
34
+
35
+ def self.multiple(commands, pretend: true)
36
+ return if commands.empty?
37
+
38
+ puts "#{pretend ? '[PRETEND] ' : nil}Executing multiple commands:"
39
+ commands.each do |command|
40
+ puts "\t#{command}"
41
+ end
42
+ return if pretend
43
+
44
+ Tempfile.create do |f|
45
+ commands.each { |command| f.puts command }
46
+ single("gam batch #{f.path}", pretend: pretend)
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/ogam/group.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ module Ogam
5
+ # Methods relating to groups
6
+ class Group
7
+ def self.groups_for_domain(domain)
8
+ Ogam::Execute.fetch_csv("gam print groups domain #{domain}", pretend: false).map { |r| r['Email'] }
9
+ end
10
+
11
+ def self.add_group_member_command(user:, group:)
12
+ "gam update group #{group} add member user #{user}"
13
+ end
14
+
15
+ def self.create_shared_mailbox_command(email_address, name)
16
+ <<~COMMAND
17
+ gam create group #{email_address} name #{Shellwords.escape(name)} allow_external_members false who_can_join invited_can_join primary_language en-GB who_can_view_membership all_in_domain_can_view include_in_global_address_list true is_archived true members_can_post_as_the_group false allow_web_posting true send_message_deny_notification false reply_to reply_to_ignore message_moderation_level moderate_none who_can_contact_owner all_in_domain_can_contact who_can_leave_group all_members_can_leave who_can_add all_managers_can_add who_can_post_message anyone_can_post who_can_invite all_managers_can_invite who_can_view_group all_members_can_view show_in_group_directory true archive_only false spam_moderation_level moderate
18
+ COMMAND
19
+ end
20
+
21
+ def self.create_distribution_list_command(email_address, name, moderated)
22
+ <<~COMMAND
23
+ gam create group #{email_address} name #{Shellwords.escape(name)} allow_external_members false who_can_join invited_can_join primary_language en-GB who_can_view_membership all_in_domain_can_view include_in_global_address_list true is_archived false members_can_post_as_the_group false allow_web_posting false send_message_deny_notification true reply_to reply_to_sender message_moderation_level #{moderated ? 'moderate_all_messages' : 'moderate_none'} who_can_contact_owner all_managers_can_contact who_can_leave_group none_can_leave who_can_add none_can_add who_can_post_message all_in_domain_can_post who_can_invite none_can_invite who_can_view_group all_managers_can_view show_in_group_directory true archive_only false spam_moderation_level moderate
24
+ COMMAND
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ogam
4
+ # Assign users from a domain to standard groups based on their job titles
5
+ class GroupAssigner
6
+ static_facade :call, :users, :domain, [pretend: true]
7
+
8
+ SUBJECT_TEACHER_TITLES = ['Teacher of ', 'Head of ', 'Second in ', 'Third in '].freeze
9
+ SUBJECT_MAPPING = {
10
+ 'art-teachers' => ['Art'],
11
+ 'business-teachers' => ['Business Studies'],
12
+ 'dance-teachers' => ['Performing Arts'],
13
+ 'design-technology-teachers' => ['Technology'],
14
+ 'drama-teachers' => ['Drama', 'Music & Drama', 'Performing Arts'],
15
+ 'english-teachers' => ['English'],
16
+ 'epq-teachers' => [],
17
+ 'geography-teachers' => ['Humanities'],
18
+ 'guidance-teachers' => [],
19
+ 'health-social-care-teachers' => ['Health & Social Care'],
20
+ 'history-teachers' => %w[Humanities History],
21
+ 'ict-teachers' => ['Computer Science'],
22
+ 'law-teachers' => ['Law'],
23
+ 'maths-teachers' => ['Maths'],
24
+ 'mfl-teachers' => ['MFL'],
25
+ 'music-teachers' => ['Music', 'Music & Drama'],
26
+ 'pe-teachers' => ['PE'],
27
+ 're-life-teachers' => ['Humanities'],
28
+ 'science-teachers' => ['Science'],
29
+ 'social-sciences-teachers' => ['Social Sciences']
30
+ }.freeze
31
+
32
+ def self.for_domain(domain, pretend: true)
33
+ call(User.active_for_domain(domain), domain, pretend: pretend)
34
+ end
35
+
36
+ def self.for_user(email, pretend: true)
37
+ call(User.active_by_email(email), email.split('@').last, pretend: pretend)
38
+ end
39
+
40
+ def self.groups_for_job_title(job_title, entry, domain)
41
+ groups_domain = "groups.#{domain}"
42
+
43
+ groups = groups_from_entry(entry, groups_domain)
44
+ groups += subject_groups(job_title, groups_domain) if entry['Teaching']
45
+
46
+ groups << "heads-of-departments@#{groups_domain}" if entry['Teaching'] && job_title.include?('Head of ')
47
+ groups << "principal@#{groups_domain}" if entry['SLT'] && ['Principal', 'Acting Principal'].include?(job_title)
48
+
49
+ groups << "learning-managers@#{groups_domain}" if job_title.include?('Learning Manager')
50
+ groups << "academy-council@#{groups_domain}" if job_title.include?('Academy Council')
51
+ groups << 'business-managers@trust-wide.outwood.com' if job_title == 'Business Manager'
52
+
53
+ groups
54
+ end
55
+
56
+ def self.groups_from_entry(entry, groups_domain)
57
+ groups = []
58
+ groups << "teaching-staff@#{groups_domain}" if entry['Teaching']
59
+ groups << "support-staff@#{groups_domain}" if entry['Support']
60
+ if entry['SLT']
61
+ groups << "slt@#{groups_domain}"
62
+ groups << "leadership-team@#{groups_domain}"
63
+ end
64
+ groups << 'finance@trust-wide.outwood.com' if entry['Finance']
65
+ groups << 'data-exams@trust-wide.outwood.com' if entry['Data and Exams']
66
+ groups << 'human-resources@trust-wide.outwood.com' if entry['HR']
67
+ groups << 'ict-support@trust-wide.outwood.com' if entry['ICT Support']
68
+ groups
69
+ end
70
+
71
+ def self.subject_groups(job_title, groups_domain)
72
+ return [] unless SUBJECT_TEACHER_TITLES.any? { |title| job_title.include?(title) }
73
+
74
+ groups = []
75
+ SUBJECT_MAPPING.each do |group_prefix, subject_words|
76
+ has_subject =
77
+ subject_words.any? do |subject|
78
+ SUBJECT_TEACHER_TITLES.any? { |title| job_title.ends_with? "#{title}#{subject}" }
79
+ end
80
+ next unless has_subject
81
+
82
+ groups << "#{group_prefix}@#{groups_domain}"
83
+ end
84
+ groups
85
+ end
86
+
87
+ def call
88
+ Execute.multiple(commands(generate_group_assignments), pretend: pretend)
89
+ end
90
+
91
+ private
92
+
93
+ def commands(group_assignments)
94
+ group_assignments.find_all { |_user, group| valid_groups.include?(group) }.map do |user, group|
95
+ Group.add_group_member_command(user: user, group: group)
96
+ end
97
+ end
98
+
99
+ def valid_groups
100
+ @valid_groups ||= Group.groups_for_domain("groups.#{domain}") + Group.groups_for_domain('trust-wide.outwood.com')
101
+ end
102
+
103
+ def generate_group_assignments
104
+ group_assignments = []
105
+ users.each do |user|
106
+ group_assignments += group_assignments_for_user(user)
107
+ end
108
+ group_assignments
109
+ end
110
+
111
+ def group_assignments_for_user(user)
112
+ group_assignments = []
113
+ email = user['primaryEmail']
114
+ User.job_titles_for_user(user).each do |job_title|
115
+ entry = approved_job_titles.fetch(job_title, nil)
116
+ if entry.nil?
117
+ puts "Warning: Unapproved job title found for user #{email}: #{job_title}"
118
+ next
119
+ end
120
+
121
+ GroupAssigner.groups_for_job_title(job_title, entry, domain).each do |group|
122
+ group_assignments << [email, group]
123
+ end
124
+ end
125
+ group_assignments
126
+ end
127
+
128
+ def approved_job_titles
129
+ @approved_job_titles ||= JobTitle.all
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'csv'
5
+
6
+ module Ogam
7
+ # Allows working with a list of approved job titles
8
+ class JobTitle
9
+ static_facade :all
10
+
11
+ CSV_URL = 'https://docs.google.com/spreadsheets/d/12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60/export?format=csv&id=12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60&gid=1604665254'
12
+
13
+ def self.job_titles(str)
14
+ return [] if str.blank?
15
+
16
+ str.split(' / ')
17
+ end
18
+
19
+ def all
20
+ @all ||= parse(download)
21
+ end
22
+
23
+ private
24
+
25
+ def download
26
+ puts 'Fetching list of accepted job titles'
27
+ URI.parse(CSV_URL).read
28
+ end
29
+
30
+ def parse(str)
31
+ data = {}
32
+ CSV.parse(str, headers: true).each do |row|
33
+ next if row['Job Title'].blank?
34
+
35
+ data[row['Job Title']] = entry_for_row(row)
36
+ end
37
+
38
+ data
39
+ end
40
+
41
+ def entry_for_row(row)
42
+ entry = {}
43
+ (row.headers - ['Job Title']).each do |col|
44
+ entry[col] = row[col] == 'TRUE'
45
+ end
46
+ entry
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'csv'
5
+
6
+ module Ogam
7
+ # Allows working with a list of approved organisations
8
+ class Organisation
9
+ static_facade :all
10
+
11
+ CSV_URL = 'https://docs.google.com/spreadsheets/d/12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60/export?format=csv&id=12nl5OBGSmOSsWilzWfflmAHO-8EyfCjVXYLeUDqjB60&gid=46181347'
12
+
13
+ def all
14
+ @all ||= parse(download)
15
+ end
16
+
17
+ private
18
+
19
+ def download
20
+ puts 'Fetching list of accepted organisations'
21
+ URI.parse(CSV_URL).read
22
+ end
23
+
24
+ def parse(str)
25
+ data = {}
26
+ CSV.parse(str, headers: true).each do |row|
27
+ next if row['Domain'].blank?
28
+
29
+ data[row['Domain'].strip] = row['Organisation Name'].strip
30
+ end
31
+
32
+ data
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ogam
4
+ # Methods relating to signatures
5
+ class Signature
6
+ def self.update_signature_command(user)
7
+ email = user.fetch('primaryEmail')
8
+ html = signature_html(
9
+ user.fetch('name.fullName', nil),
10
+ user.fetch('organizations.0.title', nil),
11
+ user.fetch('organizations.0.name', nil)
12
+ )
13
+
14
+ "gam user #{email} signature \"#{html}\""
15
+ end
16
+
17
+ def self.signature_html(name, job_title, organisation)
18
+ parts = [name, job_title, organisation].reject(&:blank?)
19
+ "<p>#{parts.join('<br />')}</p><hr color=#5b3293 />"
20
+ end
21
+ end
22
+ end
data/lib/ogam/user.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ogam
4
+ # Methods relating to users
5
+ class User
6
+ def self.active_for_domain(domain)
7
+ Ogam::Execute.fetch_csv("gam print users allfields domain #{domain} query 'isSuspended=False'", pretend: false)
8
+ end
9
+
10
+ def self.active_by_email(email)
11
+ Ogam::Execute.fetch_csv(
12
+ "gam print users allfields query 'email=#{email} isSuspended=False'",
13
+ pretend: false
14
+ )
15
+ end
16
+
17
+ def self.job_titles_for_user(user)
18
+ JobTitle.job_titles(user['organizations.0.title'])
19
+ end
20
+ end
21
+ end
data/lib/ogam/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ogam
4
- VERSION = '0.1.0'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/ogam.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/all'
4
+ require 'attr_extras'
3
5
  require 'ogam/version'
6
+ require 'ogam/execute'
7
+ require 'ogam/group'
8
+ require 'ogam/job_title'
9
+ require 'ogam/user'
10
+ require 'ogam/group_assigner'
11
+ require 'ogam/signature'
12
+ require 'ogam/organisation'
4
13
 
5
14
  module Ogam
6
15
  class Error < StandardError; end
data/ogam.gemspec CHANGED
@@ -26,11 +26,13 @@ Gem::Specification.new do |spec|
26
26
  spec.executables << 'ogam'
27
27
  spec.require_paths = ['lib']
28
28
 
29
+ spec.add_runtime_dependency 'activesupport', '~> 6.0.2'
30
+ spec.add_runtime_dependency 'attr_extras', '~> 6.2.3'
29
31
  spec.add_runtime_dependency 'thor', '~> 1.0.1'
30
32
 
31
33
  spec.add_development_dependency 'bundler', '~> 2.1'
32
34
  spec.add_development_dependency 'rake', '~> 13.0'
33
35
  spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'rubocop-ogat', '~> 1.46.1'
36
+ spec.add_development_dependency 'rubocop-ogat', '~> 1.47.0'
35
37
  spec.add_development_dependency 'simplecov', '~> 0.17.1'
36
38
  end
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ogam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elliot Bowes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-18 00:00:00.000000000 Z
11
+ date: 2020-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 6.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 6.0.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: attr_extras
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.2.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.2.3
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: thor
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +100,14 @@ dependencies:
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: 1.46.1
103
+ version: 1.47.0
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: 1.46.1
110
+ version: 1.47.0
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: simplecov
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -117,6 +145,13 @@ files:
117
145
  - bin/ogam
118
146
  - bin/setup
119
147
  - lib/ogam.rb
148
+ - lib/ogam/execute.rb
149
+ - lib/ogam/group.rb
150
+ - lib/ogam/group_assigner.rb
151
+ - lib/ogam/job_title.rb
152
+ - lib/ogam/organisation.rb
153
+ - lib/ogam/signature.rb
154
+ - lib/ogam/user.rb
120
155
  - lib/ogam/version.rb
121
156
  - ogam.gemspec
122
157
  homepage: https://github.com/Outwood/ogam
@@ -139,7 +174,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
174
  - !ruby/object:Gem::Version
140
175
  version: '0'
141
176
  requirements: []
142
- rubygems_version: 3.1.2
177
+ rubyforge_project:
178
+ rubygems_version: 2.7.7
143
179
  signing_key:
144
180
  specification_version: 4
145
181
  summary: Wrapper around GAM for common tasks with OGAT's G Suite instance.