canvas_sync 0.17.43 → 0.18.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/canvas_sync/concerns/ability_helper.rb +103 -0
  3. data/lib/canvas_sync/concerns/account/ancestry.rb +2 -0
  4. data/lib/canvas_sync/concerns/role/base.rb +57 -0
  5. data/lib/canvas_sync/generators/templates/models/account.rb +2 -0
  6. data/lib/canvas_sync/generators/templates/models/admin.rb +2 -0
  7. data/lib/canvas_sync/generators/templates/models/assignment.rb +2 -0
  8. data/lib/canvas_sync/generators/templates/models/assignment_group.rb +2 -0
  9. data/lib/canvas_sync/generators/templates/models/content_migration.rb +2 -0
  10. data/lib/canvas_sync/generators/templates/models/context_module.rb +2 -0
  11. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +2 -0
  12. data/lib/canvas_sync/generators/templates/models/course.rb +2 -0
  13. data/lib/canvas_sync/generators/templates/models/enrollment.rb +2 -0
  14. data/lib/canvas_sync/generators/templates/models/grading_period.rb +2 -0
  15. data/lib/canvas_sync/generators/templates/models/grading_period_group.rb +2 -0
  16. data/lib/canvas_sync/generators/templates/models/group.rb +2 -0
  17. data/lib/canvas_sync/generators/templates/models/group_membership.rb +2 -0
  18. data/lib/canvas_sync/generators/templates/models/learning_outcome.rb +2 -0
  19. data/lib/canvas_sync/generators/templates/models/pseudonym.rb +2 -0
  20. data/lib/canvas_sync/generators/templates/models/role.rb +2 -0
  21. data/lib/canvas_sync/generators/templates/models/section.rb +2 -0
  22. data/lib/canvas_sync/generators/templates/models/submission.rb +2 -0
  23. data/lib/canvas_sync/generators/templates/models/term.rb +2 -0
  24. data/lib/canvas_sync/generators/templates/models/user.rb +2 -0
  25. data/lib/canvas_sync/generators/templates/models/user_observer.rb +2 -0
  26. data/lib/canvas_sync/record.rb +92 -0
  27. data/lib/canvas_sync/version.rb +1 -1
  28. data/spec/dummy/log/test.log +4132 -0
  29. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d7516d1ed3059c0c62d1f1ac058841dd289aca1ff8007576be2b0d823def9b7
4
- data.tar.gz: 39fc2b3ade4599edaec6282b1db7b45f8cb81817573d7f35e7de1377624b1474
3
+ metadata.gz: de4a872d4a40555407e9dd9e2ab4e10aabfe61a68e06be46caaface500d89358
4
+ data.tar.gz: a79a7d11bdf1565662aab1832cc9cd664cc7e41b52e8586c57f579ef0b2f9f6a
5
5
  SHA512:
6
- metadata.gz: '0978316cfc1c66f2411d1fe00b3c9375a4032b8b6be49d63807fb596bc07f25367fe013e3e5bbba32085e0ebf9a4bfcd977c19c5e20a11c41bf02de0da40bd2a'
7
- data.tar.gz: 7496829655356548947c2782a024c76370f9d1f74ac6b96b17d2df2c4afd177f5e43be6d0b60b1cee79955e15ba5a387e002c784bcc0dde2ee48b3bae7f61783
6
+ metadata.gz: 0b50db7fb0d71fc8e707793e4ba3275407e2a8e3e00d6991d5e233c1aeb479859e9d6a36206b77e7d52797f4dd422ae1ab0a4c8d249767d885587b2616368eba
7
+ data.tar.gz: 61bb66d592d83b9e263d443328a05d32a1bd17e551a87cbd59c68821820bf471c80fa708719edc35dbbbe6530fa0496d2e4c99ee70ddb7836195412b3e8687d2
@@ -0,0 +1,103 @@
1
+ module CanvasSync::Concerns
2
+ module AbilityHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ # PandaPal Domain
6
+
7
+ def panda_pal_session
8
+ @panda_pal_session ||= @controller.current_session&.data || {}
9
+ end
10
+
11
+ def rails_session
12
+ @rails_session ||= @controller.session
13
+ end
14
+
15
+ def is_lti_launch?
16
+ panda_pal_session.present?
17
+ end
18
+
19
+ def launch_params
20
+ @launch_params ||= panda_pal_session[:launch_params] || {}
21
+ end
22
+
23
+ def canvas_role_labels
24
+ labels = launch_params['custom_canvas_role']
25
+ labels.is_a?(String) ? labels.split(',') : []
26
+ end
27
+
28
+ def lti_roles
29
+ @lti_roles ||= LTIRoles::RoleManager.new(launch_params['ext_roles'] || '')
30
+ end
31
+
32
+ def canvas_site_admin?
33
+ lti_roles.system_roles.include?("sys_admin")
34
+ end
35
+
36
+ # Middle Domain
37
+
38
+ def launch_context
39
+ @launch_context ||= begin
40
+ if launch_params['launch_type'] == "global_navigation"
41
+ :global
42
+ elsif launch_params['custom_canvas_course_id'].present?
43
+ ::Course.find_by(canvas_id: launch_params['custom_canvas_course_id'])
44
+ else
45
+ ::Account.find_by(canvas_id: launch_params['custom_canvas_account_id'])
46
+ end
47
+ end
48
+ end
49
+
50
+ def launch_account
51
+ @launch_account ||= launch_context.respond_to?(:account) ? launch_context.account : ::Account.find_by(canvas_id: launch_params['custom_canvas_account_id'])
52
+ end
53
+
54
+ # CanvasSync Domain
55
+
56
+ def canvas_permissions
57
+ panda_pal_session[:canvas_permissions] ||= ::Role.role_permissions(canvas_roles)
58
+ end
59
+
60
+ def canvas_roles
61
+ @canvas_roles ||= Role.for_labels(canvas_role_labels, launch_account)
62
+ end
63
+
64
+ def canvas_root_account_roles
65
+ role_labels = if "::Admin".safe_constantize && ::Admin < ::ActiveRecord::Base
66
+ adm_query = ::Admin.where(canvas_account_id: current_organization.canvas_account_id, workflow_state: "active")
67
+ adm_query.pluck(:role_name)
68
+ else
69
+ Rails.cache.fetch([self.class.name, "RootAccountAdminLinks", canvas_user_id], expires_in: 1.hour) do
70
+ admin_entries = canvas_sync_client.account_admins('self', user_id: [canvas_user_id])
71
+ admin_entries = admin_entries.select{|ent| ent[:workflow_state] == 'active' }
72
+ admin_entries.map{|ent| ent[:role] }
73
+ end
74
+ end
75
+ ::Role.for_labels(role_labels, ::Account.find_by(canvas_parent_account_id: nil))
76
+ end
77
+
78
+ def canvas_account_roles
79
+ canvas_roles.where(base_role_type: 'AccountMembership')
80
+ end
81
+
82
+ def canvas_course_roles
83
+ canvas_roles.where.not(base_role_type: 'AccountMembership')
84
+ end
85
+
86
+ def canvas_super_user?
87
+ cache_on_session(:canvas_super_user?) do
88
+ canvas_site_admin? || canvas_root_account_roles.map(&:label).include?("Account Admin")
89
+ end
90
+ end
91
+
92
+ def canvas_user_id
93
+ user.canvas_id
94
+ end
95
+
96
+ private
97
+
98
+ def cache_on_session(key, &blk)
99
+ panda_pal_session[:ability_cache] ||= {}
100
+ panda_pal_session[:ability_cache][key] ||= blk.call
101
+ end
102
+ end
103
+ end
@@ -11,6 +11,8 @@ module CanvasSync::Concerns
11
11
  extend ActiveSupport::Concern
12
12
  include CanvasSync::Record
13
13
 
14
+ CanvasSync::Record.define_feature self, default: ->{ column_names.include?("ancestry") }
15
+
14
16
  included do
15
17
  has_ancestry
16
18
  before_save :relink_ancestry, if: :canvas_parent_account_id_changed?
@@ -0,0 +1,57 @@
1
+ module CanvasSync::Concerns
2
+ module Role
3
+ module Base
4
+ extend ActiveSupport::Concern
5
+
6
+ DEFAULT_ROLE_LABELS = %w[TeacherEnrollment TaEnrollment StudentEnrollment DesignerEnrollment ObserverEnrollment].freeze
7
+
8
+ CanvasSync::Record.define_feature self, default: true
9
+
10
+ class_methods do
11
+ def for_labels(labels, account)
12
+ built_ins = []
13
+ labels = labels.split(',') if labels.is_a?(String)
14
+ custom_labels = Array(labels).reject do |l|
15
+ if DEFAULT_ROLE_LABELS.include?(l)
16
+ built_ins << l
17
+ elsif l == 'Account Admin' || l == 'AccountAdmin'
18
+ built_ins << 'AccountMembership'
19
+ else
20
+ next
21
+ end
22
+ true
23
+ end
24
+
25
+ account_ids = Rails.cache.fetch([self.class.name, "AccountAncestry", account.canvas_id], expires_in: 6.hours) do
26
+ if account.respond_to?(:path_ids)
27
+ account.path.pluck(:canvas_id)
28
+ else
29
+ [].tap do |pids|
30
+ acc = account
31
+ loop do
32
+ break unless acc
33
+ pids.unshift(acc.canvas_id)
34
+ acc = acc.canvas_parent
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ where(workflow_state: 'built_in', base_role_type: built_ins)
41
+ .or(where.not(workflow_state: 'built_in').where(label: custom_labels, canvas_account_id: account_ids))
42
+ end
43
+
44
+ def joined_permissions(roles)
45
+ final = {}
46
+ roles.each do |role|
47
+ role.permissions.each do |perm_name, perm|
48
+ final[perm_name] = false if final[perm_name].nil?
49
+ final[perm_name] = true if perm['enabled'] == true
50
+ end
51
+ end
52
+ final
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -5,6 +5,8 @@ class Account < ApplicationRecord
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
  # include CanvasSync::Concerns::Account::Ancestry # Add support for the ancestry Gem
7
7
 
8
+ canvas_sync_features :defaults
9
+
8
10
  validates :canvas_id, uniqueness: true, presence: true
9
11
 
10
12
  has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_account_id
@@ -4,6 +4,8 @@ class Admin < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
9
11
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
@@ -4,6 +4,8 @@ class Assignment < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
9
11
  belongs_to :assignment_group, optional: true, primary_key: :canvas_id, foreign_key: :canvas_assignment_group_id
@@ -4,6 +4,8 @@ class AssignmentGroup < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
9
11
  has_many :assignments, primary_key: :canvas_id, foreign_key: :canvas_assignment_group_id
@@ -4,6 +4,8 @@ class ContentMigration < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
9
11
 
@@ -7,6 +7,8 @@ class ContextModule < ApplicationRecord
7
7
  include CanvasSync::Record
8
8
  include CanvasSync::Concerns::ApiSyncable
9
9
 
10
+ canvas_sync_features :defaults
11
+
10
12
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
11
13
  has_many :context_module_items, primary_key: :canvas_id, foreign_key: :canvas_context_module_id
12
14
  has_many :assignments, through: :context_module_items
@@ -4,6 +4,8 @@ class ContextModuleItem < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  belongs_to :context_module, primary_key: :canvas_id, foreign_key: :canvas_context_module_id, optional: true
8
10
  belongs_to :content, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_content_id, foreign_type: :canvas_content_type
9
11
  belongs_to :assignment, foreign_key: :canvas_assignment_id, primary_key: :canvas_id
@@ -4,6 +4,8 @@ class Course < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_id, optional: true
9
11
  has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_course_id
@@ -4,6 +4,8 @@ class Enrollment < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
9
11
  belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
@@ -3,6 +3,8 @@
3
3
  class GradingPeriod < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
 
6
+ canvas_sync_features :defaults
7
+
6
8
  validates :canvas_id, uniqueness: true, presence: true
7
9
  belongs_to :grading_period_group, primary_key: :canvas_id, foreign_key: :canvas_grading_period_group_id, optional: true
8
10
  end
@@ -3,5 +3,7 @@
3
3
  class GradingPeriodGroup < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
 
6
+ canvas_sync_features :defaults
7
+
6
8
  validates :canvas_id, uniqueness: true, presence: true
7
9
  end
@@ -4,6 +4,8 @@ class Group < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
9
11
  belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
@@ -4,6 +4,8 @@ class GroupMembership < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :group, primary_key: :canvas_id, foreign_key: :canvas_group_id
9
11
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
@@ -6,6 +6,8 @@ class LearningOutcome < ApplicationRecord
6
6
  include CanvasSync::Record
7
7
  include CanvasSync::Concerns::ApiSyncable
8
8
 
9
+ canvas_sync_features :defaults
10
+
9
11
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
10
12
 
11
13
  api_syncable({
@@ -3,6 +3,8 @@
3
3
  class Pseudonym < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
 
6
+ canvas_sync_features :defaults
7
+
6
8
  validates :canvas_id, uniqueness: true, presence: true
7
9
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
8
10
  end
@@ -4,6 +4,8 @@ class Role < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  has_many :admins, foreign_key: :canvas_role_id, primary_key: :canvas_id
9
11
 
@@ -4,6 +4,8 @@ class Section < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
9
11
  has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_section_id
@@ -4,6 +4,8 @@ class Submission < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  belongs_to :assignment, primary_key: :canvas_id, foreign_key: :canvas_assignment_id, optional: true
9
11
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
@@ -4,6 +4,8 @@ class Term < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
9
11
  belongs_to :grading_period_group, primary_key: :canvas_id, foreign_key: :grading_period_group_id, optional: true
@@ -4,6 +4,8 @@ class User < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
  has_many :pseudonyms, primary_key: :canvas_id, foreign_key: :canvas_user_id
9
11
  has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_user_id
@@ -4,6 +4,8 @@ class UserObserver < ApplicationRecord
4
4
  include CanvasSync::Record
5
5
  include CanvasSync::Concerns::ApiSyncable
6
6
 
7
+ canvas_sync_features :defaults
8
+
7
9
  validates :canvas_id, uniqueness: true, presence: true
8
10
 
9
11
  belongs_to :observing_user, primary_key: :canvas_id, foreign_key: :observing_user_id, class_name: 'User', optional: true
@@ -6,6 +6,98 @@ module CanvasSync
6
6
  extend ActiveSupport::Concern
7
7
  include CanvasSync::Concerns::SyncMapping
8
8
 
9
+ def self.registered_features
10
+ @canvas_sync_features ||= {}
11
+ end
12
+
13
+ def self.define_feature(modul, feature_ident = nil, models: nil, default: false, &blk)
14
+ feature_ident = modul.name.demodulize.underscore if feature_ident.nil?
15
+ if models == nil
16
+ pmod = _get_module_parent(modul)
17
+ models = pmod.name.demodulize if pmod && _get_module_parent(pmod) == CanvasSync::Concerns
18
+ end
19
+
20
+ @canvas_sync_features ||= {}
21
+ @canvas_sync_features[feature_ident] = {
22
+ name: feature_ident,
23
+ module: modul,
24
+ default: default,
25
+ models: Array.wrap(models).map(&:to_s),
26
+ setup: blk || ->{ include modul },
27
+ }
28
+ end
29
+
30
+ def self._get_module_parent(modul)
31
+ modul.respond_to?(:parent) ? modul.parent : modul.module_parent
32
+ end
33
+
34
+ class_methods do
35
+ def applicable_canvas_sync_features
36
+ @applicable_canvas_sync_features ||= begin
37
+ relev_feats = {}
38
+ CanvasSync::Record.registered_features.each do |k, cfg|
39
+ next if cfg[:models].present? && !cfg[:models].include?(self.name.demodulize)
40
+ relev_feats[k] = cfg
41
+ end
42
+ relev_feats
43
+ end
44
+ end
45
+
46
+ def canvas_sync_feature(feature, config = nil)
47
+ feat = applicable_canvas_sync_features[feature]
48
+
49
+ if !feat
50
+ raise "Unkown feature :#{feature} for model #{self}"
51
+ end
52
+
53
+ setup = feat[:setup]
54
+ if setup.arity != 0
55
+ instance_exec(config, &setup)
56
+ elsif config.present?
57
+ raise ArgumentError, "Feature #{feature} does not accept configuration"
58
+ else
59
+ instance_exec(&setup)
60
+ end
61
+ end
62
+
63
+ def canvas_sync_features(*args, skip: [], **kwargs)
64
+ features = {}
65
+
66
+ include_defaults = args.length == 0 || args.include?(:defaults)
67
+ if include_defaults
68
+ applicable_canvas_sync_features.each do |key, cfg|
69
+ next if skip.include?(key)
70
+
71
+ case cfg[:default]
72
+ when true
73
+ features[key] = {}
74
+ when Proc
75
+ features[key] = {} if instance_exec(&cfg[:default])
76
+ end
77
+ end
78
+ end
79
+
80
+ args.each do |a|
81
+ next if a == :defaults
82
+ features[a] ||= {}
83
+ end
84
+
85
+ kwargs.each do |key, opts|
86
+ if opts == false
87
+ features.delete(key)
88
+ elsif opts == true
89
+ features[key] ||= {}
90
+ else
91
+ features[key] = opts
92
+ end
93
+ end
94
+
95
+ features.each do |key, opts|
96
+ canvas_sync_feature(key, opts)
97
+ end
98
+ end
99
+ end
100
+
9
101
  included do
10
102
  define_model_callbacks :sync_import
11
103
  end
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.17.43".freeze
2
+ VERSION = "0.18.0.beta1".freeze
3
3
  end