canvas_sync 0.17.20 → 0.17.23.beta5

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -1
  3. data/config/initializers/apartment.rb +10 -0
  4. data/lib/canvas_sync/concerns/sync_mapping.rb +114 -0
  5. data/lib/canvas_sync/generators/install_generator.rb +1 -0
  6. data/lib/canvas_sync/generators/templates/migrations/create_grading_period_groups.rb +22 -0
  7. data/lib/canvas_sync/generators/templates/migrations/create_grading_periods.rb +22 -0
  8. data/lib/canvas_sync/generators/templates/migrations/create_user_observers.rb +17 -0
  9. data/lib/canvas_sync/generators/templates/models/grading_period.rb +8 -0
  10. data/lib/canvas_sync/generators/templates/models/grading_period_group.rb +9 -0
  11. data/lib/canvas_sync/generators/templates/models/term.rb +1 -0
  12. data/lib/canvas_sync/generators/templates/models/user_observer.rb +11 -0
  13. data/lib/canvas_sync/importers/bulk_importer.rb +27 -16
  14. data/lib/canvas_sync/job_batches/chain_builder.rb +1 -1
  15. data/lib/canvas_sync/processors/assignment_groups_processor.rb +1 -7
  16. data/lib/canvas_sync/processors/assignments_processor.rb +1 -7
  17. data/lib/canvas_sync/processors/context_module_items_processor.rb +1 -7
  18. data/lib/canvas_sync/processors/context_modules_processor.rb +1 -7
  19. data/lib/canvas_sync/processors/model_mappings.yml +68 -0
  20. data/lib/canvas_sync/processors/normal_processor.rb +3 -3
  21. data/lib/canvas_sync/processors/provisioning_report_processor.rb +21 -63
  22. data/lib/canvas_sync/processors/report_processor.rb +14 -9
  23. data/lib/canvas_sync/processors/submissions_processor.rb +1 -7
  24. data/lib/canvas_sync/record.rb +4 -0
  25. data/lib/canvas_sync/version.rb +1 -1
  26. data/lib/canvas_sync.rb +4 -1
  27. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +40 -0
  28. data/spec/dummy/app/models/grading_period.rb +14 -0
  29. data/spec/dummy/app/models/grading_period_group.rb +15 -0
  30. data/spec/dummy/app/models/user_observer.rb +17 -0
  31. data/spec/dummy/db/migrate/20210907233329_create_user_observers.rb +23 -0
  32. data/spec/dummy/db/migrate/20210907233330_create_grading_periods.rb +28 -0
  33. data/spec/dummy/db/migrate/20210907233331_create_grading_period_groups.rb +28 -0
  34. data/spec/dummy/db/schema.rb +42 -1
  35. data/spec/dummy/log/development.log +1105 -1186
  36. data/spec/dummy/log/test.log +9781 -42513
  37. data/spec/support/fixtures/reports/grading_period_groups.csv +2 -0
  38. data/spec/support/fixtures/reports/grading_periods.csv +3 -0
  39. data/spec/support/fixtures/reports/user_observers.csv +3 -0
  40. metadata +34 -25
  41. data/spec/dummy/db/test.sqlite3 +0 -0
@@ -13,12 +13,12 @@ module CanvasSync
13
13
  end
14
14
 
15
15
  def initialize(report_file_path, options)
16
- conflict_target = mapping[options[:mapping].to_sym][:conflict_target]
16
+ m = mapping[options[:mapping].to_sym]
17
17
  CanvasSync::Importers::BulkImporter.import(
18
18
  report_file_path,
19
- mapping[options[:mapping].to_sym][:report_columns],
19
+ m[:report_columns],
20
20
  options[:klass].constantize,
21
- conflict_target ? conflict_target.to_sym : conflict_target,
21
+ m[:conflict_target],
22
22
  import_args: options
23
23
  )
24
24
  end
@@ -69,94 +69,52 @@ module CanvasSync
69
69
  end
70
70
 
71
71
  def bulk_process_users(report_file_path)
72
- CanvasSync::Importers::BulkImporter.import(
73
- report_file_path,
74
- mapping[:users][:report_columns],
75
- User,
76
- mapping[:users][:conflict_target].to_sym,
77
- import_args: @options
78
- )
72
+ do_bulk_import(report_file_path, User, options: @options)
73
+ end
74
+
75
+ def bulk_process_user_observers(report_file_path)
76
+ do_bulk_import(report_file_path, UserObserver, options: @options)
79
77
  end
80
78
 
81
79
  def bulk_process_pseudonyms(report_file_path)
82
- CanvasSync::Importers::BulkImporter.import(
83
- report_file_path,
84
- mapping[:pseudonyms][:report_columns],
85
- Pseudonym,
86
- mapping[:pseudonyms][:conflict_target].to_sym,
87
- import_args: @options
88
- )
80
+ do_bulk_import(report_file_path, Pseudonym, options: @options)
89
81
  end
90
82
 
91
83
  def bulk_process_accounts(report_file_path)
92
- CanvasSync::Importers::BulkImporter.import(
93
- report_file_path,
94
- mapping[:accounts][:report_columns],
95
- Account,
96
- mapping[:accounts][:conflict_target].to_sym,
97
- import_args: @options
98
- )
84
+ do_bulk_import(report_file_path, Account, options: @options)
99
85
  end
100
86
 
101
87
  def bulk_process_courses(report_file_path)
102
- CanvasSync::Importers::BulkImporter.import(
103
- report_file_path,
104
- mapping[:courses][:report_columns],
105
- Course,
106
- mapping[:courses][:conflict_target].to_sym,
107
- import_args: @options
108
- )
88
+ do_bulk_import(report_file_path, Course, options: @options)
109
89
  end
110
90
 
111
91
  def bulk_process_enrollments(report_file_path)
112
- CanvasSync::Importers::BulkImporter.import(
113
- report_file_path,
114
- mapping[:enrollments][:report_columns],
115
- Enrollment,
116
- mapping[:enrollments][:conflict_target].to_sym,
117
- import_args: @options
118
- )
92
+ do_bulk_import(report_file_path, Enrollment, options: @options)
119
93
  end
120
94
 
121
95
  def bulk_process_sections(report_file_path)
122
- CanvasSync::Importers::BulkImporter.import(
123
- report_file_path,
124
- mapping[:sections][:report_columns],
125
- Section,
126
- mapping[:sections][:conflict_target].to_sym,
127
- import_args: @options
128
- )
96
+ do_bulk_import(report_file_path, Section, options: @options)
129
97
  end
130
98
 
131
99
  def bulk_process_xlist(report_file_path)
132
- CanvasSync::Importers::BulkImporter.import(
133
- report_file_path,
134
- mapping[:xlist][:report_columns],
135
- Section,
136
- mapping[:xlist][:conflict_target].to_sym,
137
- import_args: @options
138
- )
100
+ do_bulk_import(report_file_path, Section, options: @options, mapping_key: :xlist)
139
101
  end
140
102
 
141
103
  def bulk_process_groups(report_file_path)
142
- CanvasSync::Importers::BulkImporter.import(
143
- report_file_path,
144
- mapping[:groups][:report_columns],
145
- Group,
146
- mapping[:groups][:conflict_target].to_sym,
147
- import_args: @options
148
- )
104
+ do_bulk_import(report_file_path, Group, options: @options)
105
+ end
106
+
107
+ def bulk_process_grading_periods(report_file_path)
108
+ do_bulk_import(report_file_path, GradingPeriod, options: @options)
109
+ end
110
+
111
+ def bulk_process_grading_period_groups(report_file_path)
112
+ do_bulk_import(report_file_path, GradingPeriodGroup, options: @options)
149
113
  end
150
114
 
151
115
  # Note that group membership is singular because we override the model name param in sync_provisioning_report_job
152
116
  def bulk_process_group_membership(report_file_path)
153
- CanvasSync::Importers::BulkImporter.import(
154
- report_file_path,
155
- mapping[:group_memberships][:report_columns],
156
- GroupMembership,
157
- mapping[:group_memberships][:conflict_target].to_sym,
158
- import_args: @options
159
- )
117
+ do_bulk_import(report_file_path, GroupMembership, options: @options)
160
118
  end
161
119
  end
162
120
  end
@@ -5,17 +5,22 @@ module CanvasSync
5
5
  # Base report processing class
6
6
  class ReportProcessor
7
7
  def mapping
8
- @mapping ||= begin
9
- mapping = YAML.load_file(File.join(__dir__, "model_mappings.yml")).deep_symbolize_keys!
10
- override_filepath = Rails.root.join("config/canvas_sync_provisioning_mapping.yml")
8
+ CanvasSync::Concerns::SyncMapping::Mapping.default_mappings
9
+ end
11
10
 
12
- if File.file?(override_filepath)
13
- override = YAML.load_file(override_filepath).deep_symbolize_keys!
14
- mapping = mapping.merge(override)
15
- end
11
+ def mapping_for(model, key = nil)
12
+ model.try(:get_sync_mapping, key) || mapping[key || CanvasSync::Concerns::SyncMapping::Mapping.normalize_model_name(model)]
13
+ end
16
14
 
17
- mapping
18
- end
15
+ def do_bulk_import(report_file_path, model, options: {}, mapping_key: nil)
16
+ m = mapping_for(model, mapping_key)
17
+ CanvasSync::Importers::BulkImporter.import(
18
+ report_file_path,
19
+ m[:report_columns],
20
+ model,
21
+ m[:conflict_target],
22
+ import_args: options
23
+ )
19
24
  end
20
25
  end
21
26
  end
@@ -12,13 +12,7 @@ module CanvasSync
12
12
  end
13
13
 
14
14
  def initialize(report_file_path, options)
15
- CanvasSync::Importers::BulkImporter.import(
16
- report_file_path,
17
- mapping[:submissions][:report_columns],
18
- Submission,
19
- mapping[:submissions][:conflict_target].to_sym,
20
- import_args: options
21
- )
15
+ do_bulk_import(report_file_path, Submission, options: options)
22
16
  end
23
17
  end
24
18
  end
@@ -1,6 +1,10 @@
1
+
2
+ require_relative 'concerns/sync_mapping'
3
+
1
4
  module CanvasSync
2
5
  module Record
3
6
  extend ActiveSupport::Concern
7
+ include CanvasSync::Concerns::SyncMapping
4
8
 
5
9
  included do
6
10
  define_model_callbacks :sync_import
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.17.20".freeze
2
+ VERSION = "0.17.23.beta5".freeze
3
3
  end
data/lib/canvas_sync.rb CHANGED
@@ -41,6 +41,9 @@ module CanvasSync
41
41
  context_modules
42
42
  context_module_items
43
43
  xlist
44
+ user_observers
45
+ grading_periods
46
+ grading_period_groups
44
47
  ].freeze
45
48
 
46
49
  SUPPORTED_TERM_SCOPE_MODELS = %w[
@@ -176,7 +179,7 @@ module CanvasSync
176
179
  # These Models use the provisioning report, but are not term-scoped,
177
180
  # so we sync them outside of the term scoping to ensure work is not duplicated
178
181
  if term_scope.present?
179
- models -= (first_provisioning_models = models & ['users', 'pseudonyms'])
182
+ models -= (first_provisioning_models = models & ['users', 'pseudonyms', 'user_observers', 'grading_periods', 'grading_period_groups'])
180
183
  current_chain.insert(generate_provisioning_jobs(first_provisioning_models, options))
181
184
  end
182
185
 
@@ -62,6 +62,46 @@ RSpec.describe CanvasSync::Processors::ProvisioningReportProcessor do
62
62
  expect(group.canvas_user_id).to eq 10
63
63
  end
64
64
 
65
+ it 'processes user_observers' do
66
+ expect {
67
+ subject.process('spec/support/fixtures/reports/user_observers.csv', { models: ['user_observers'] }, 1)
68
+ }.to change { UserObserver.count }.by(2)
69
+ obj = UserObserver.find_by(observed_user_id: 3)
70
+ expect(obj.workflow_state).to eq 'active'
71
+ expect(obj.observing_user_id).to eq 21
72
+ end
73
+
74
+ it 'processes grading_periods' do
75
+ expect {
76
+ subject.process('spec/support/fixtures/reports/grading_periods.csv', { models: ['grading_periods'] }, 1)
77
+ }.to change { GradingPeriod.count }.by(2)
78
+ obj = GradingPeriod.find_by(canvas_id: 1)
79
+ puts obj.inspect
80
+ expect(obj.title).to eq 'Period 1'
81
+ expect(obj.weight).to eq 0.2
82
+ expect(obj.workflow_state).to eq 'active'
83
+ end
84
+
85
+ it 'processes grading_period_groups' do
86
+ expect {
87
+ subject.process('spec/support/fixtures/reports/grading_period_groups.csv', { models: ['grading_period_groups'] }, 1)
88
+ }.to change { GradingPeriodGroup.count }.by(1)
89
+ obj = GradingPeriodGroup.find_by(canvas_id: 1)
90
+ puts obj.inspect
91
+ expect(obj.title).to eq 'Test Group'
92
+ expect(obj.workflow_state).to eq 'active'
93
+ end
94
+
95
+ it 'model with composite key behaves as expected' do
96
+ expect {
97
+ subject.process('spec/support/fixtures/reports/user_observers.csv', { models: ['user_observers'] }, 1)
98
+ }.to change { UserObserver.count }.by(2)
99
+
100
+ expect {
101
+ subject.process('spec/support/fixtures/reports/user_observers.csv', { models: ['user_observers'] }, 1)
102
+ }.to change { UserObserver.count }.by(0)
103
+ end
104
+
65
105
  context 'options[:models] is multiple models' do
66
106
  it 'extracts the ZIP and processes each model' do
67
107
  user_count = User.count
@@ -0,0 +1,14 @@
1
+ # #
2
+ # AUTO GENERATED MODEL
3
+ # This model was auto generated by the CanvasSync Gem.
4
+ # You can customize it as needed, but make sure you test
5
+ # any changes you make to the auto generated methods.
6
+ #
7
+
8
+
9
+ class GradingPeriod < ApplicationRecord
10
+ include CanvasSync::Record
11
+
12
+ validates :canvas_id, uniqueness: true, presence: true
13
+ belongs_to :grading_period_group, primary_key: :canvas_id, foreign_key: :canvas_grading_period_group_id, optional: true
14
+ end
@@ -0,0 +1,15 @@
1
+ # #
2
+ # AUTO GENERATED MODEL
3
+ # This model was auto generated by the CanvasSync Gem.
4
+ # You can customize it as needed, but make sure you test
5
+ # any changes you make to the auto generated methods.
6
+ #
7
+
8
+
9
+ class GradingPeriodGroup < ApplicationRecord
10
+ include CanvasSync::Record
11
+
12
+ validates :canvas_id, uniqueness: true, presence: true
13
+ belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
14
+ belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
15
+ end
@@ -0,0 +1,17 @@
1
+ # #
2
+ # AUTO GENERATED MODEL
3
+ # This model was auto generated by the CanvasSync Gem.
4
+ # You can customize it as needed, but make sure you test
5
+ # any changes you make to the auto generated methods.
6
+ #
7
+
8
+
9
+ class UserObserver < ApplicationRecord
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
12
+
13
+ validates :canvas_id, uniqueness: true, presence: true
14
+
15
+ belongs_to :observing_user, primary_key: :canvas_id, foreign_key: :observing_user_id, class_name: 'User', optional: true
16
+ belongs_to :observed_user, primary_key: :canvas_id, foreign_key: :observed_user_id, class_name: 'User', optional: true
17
+ end
@@ -0,0 +1,23 @@
1
+ # #
2
+ # AUTO GENERATED MIGRATION
3
+ # This migration was auto generated by the CanvasSync Gem.
4
+ # You can add new columns to this table, but removing or
5
+ # re-naming ones created here may break Canvas Syncing.
6
+ #
7
+
8
+
9
+ class CreateUserObservers < ActiveRecord::Migration[5.1]
10
+ def change
11
+ create_table :user_observers do |t|
12
+ t.bigint :observing_user_id
13
+ t.bigint :observed_user_id
14
+ t.string :workflow_state
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :user_observers, [:observed_user_id, :observing_user_id], unique: true
20
+ add_index :user_observers, :observing_user_id
21
+ add_index :user_observers, :observed_user_id
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ # #
2
+ # AUTO GENERATED MIGRATION
3
+ # This migration was auto generated by the CanvasSync Gem.
4
+ # You can add new columns to this table, but removing or
5
+ # re-naming ones created here may break Canvas Syncing.
6
+ #
7
+
8
+
9
+ class CreateGradingPeriods < ActiveRecord::Migration[5.1]
10
+ def change
11
+ create_table :grading_periods do |t|
12
+ t.bigint :canvas_id, null: false
13
+ t.string :title
14
+ t.float :weight
15
+ t.datetime :start_date
16
+ t.datetime :end_date
17
+ t.datetime :close_date
18
+ t.bigint :canvas_grading_period_group_id
19
+
20
+ t.string :workflow_state
21
+
22
+ t.timestamps
23
+ end
24
+
25
+ add_index :grading_periods, :canvas_id, unique: true
26
+ add_index :grading_periods, :canvas_grading_period_group_id
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # #
2
+ # AUTO GENERATED MIGRATION
3
+ # This migration was auto generated by the CanvasSync Gem.
4
+ # You can add new columns to this table, but removing or
5
+ # re-naming ones created here may break Canvas Syncing.
6
+ #
7
+
8
+
9
+ class CreateGradingPeriodGroups < ActiveRecord::Migration[5.1]
10
+ def change
11
+ create_table :grading_period_groups do |t|
12
+ t.bigint :canvas_id, null: false
13
+ t.bigint :canvas_course_id
14
+ t.bigint :canvas_account_id
15
+ t.string :title
16
+ t.boolean :weighted
17
+ t.boolean :display_totals_for_all_grading_periods
18
+
19
+ t.string :workflow_state
20
+
21
+ t.timestamps
22
+ end
23
+
24
+ add_index :grading_period_groups, :canvas_id, unique: true
25
+ add_index :grading_period_groups, :canvas_course_id
26
+ add_index :grading_period_groups, :canvas_account_id
27
+ end
28
+ end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 2020_10_30_210836) do
13
+ ActiveRecord::Schema.define(version: 2021_09_07_233331) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -173,6 +173,36 @@ ActiveRecord::Schema.define(version: 2020_10_30_210836) do
173
173
  t.index ["canvas_user_id"], name: "index_enrollments_on_canvas_user_id"
174
174
  end
175
175
 
176
+ create_table "grading_period_groups", force: :cascade do |t|
177
+ t.bigint "canvas_id", null: false
178
+ t.bigint "canvas_course_id"
179
+ t.bigint "canvas_account_id"
180
+ t.string "title"
181
+ t.boolean "weighted"
182
+ t.boolean "display_totals_for_all_grading_periods"
183
+ t.string "workflow_state"
184
+ t.datetime "created_at", null: false
185
+ t.datetime "updated_at", null: false
186
+ t.index ["canvas_account_id"], name: "index_grading_period_groups_on_canvas_account_id"
187
+ t.index ["canvas_course_id"], name: "index_grading_period_groups_on_canvas_course_id"
188
+ t.index ["canvas_id"], name: "index_grading_period_groups_on_canvas_id", unique: true
189
+ end
190
+
191
+ create_table "grading_periods", force: :cascade do |t|
192
+ t.bigint "canvas_id", null: false
193
+ t.string "title"
194
+ t.float "weight"
195
+ t.datetime "start_date"
196
+ t.datetime "end_date"
197
+ t.datetime "close_date"
198
+ t.bigint "canvas_grading_period_group_id"
199
+ t.string "workflow_state"
200
+ t.datetime "created_at", null: false
201
+ t.datetime "updated_at", null: false
202
+ t.index ["canvas_grading_period_group_id"], name: "index_grading_periods_on_canvas_grading_period_group_id"
203
+ t.index ["canvas_id"], name: "index_grading_periods_on_canvas_id", unique: true
204
+ end
205
+
176
206
  create_table "group_memberships", force: :cascade do |t|
177
207
  t.bigint "canvas_id", null: false
178
208
  t.bigint "canvas_user_id", null: false
@@ -275,6 +305,17 @@ ActiveRecord::Schema.define(version: 2020_10_30_210836) do
275
305
  t.index ["canvas_id"], name: "index_terms_on_canvas_id", unique: true
276
306
  end
277
307
 
308
+ create_table "user_observers", force: :cascade do |t|
309
+ t.bigint "observing_user_id"
310
+ t.bigint "observed_user_id"
311
+ t.string "workflow_state"
312
+ t.datetime "created_at", null: false
313
+ t.datetime "updated_at", null: false
314
+ t.index ["observed_user_id", "observing_user_id"], name: "index_user_observers_on_observed_user_id_and_observing_user_id", unique: true
315
+ t.index ["observed_user_id"], name: "index_user_observers_on_observed_user_id"
316
+ t.index ["observing_user_id"], name: "index_user_observers_on_observing_user_id"
317
+ end
318
+
278
319
  create_table "users", force: :cascade do |t|
279
320
  t.bigint "canvas_id", null: false
280
321
  t.string "sis_id"