canvas_sync 0.17.43 → 0.18.0.beta1

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.
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