canvas_sync 0.3.22 → 0.3.23

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/lib/canvas_sync.rb +61 -11
  4. data/lib/canvas_sync/generators/templates/models/assignment.rb +2 -0
  5. data/lib/canvas_sync/generators/templates/models/context_module.rb +10 -0
  6. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +6 -0
  7. data/lib/canvas_sync/generators/templates/models/create_context_module_items.rb +19 -0
  8. data/lib/canvas_sync/generators/templates/models/create_context_modules.rb +20 -0
  9. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +28 -0
  10. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +28 -0
  11. data/lib/canvas_sync/processors/context_module_items_processor.rb +24 -0
  12. data/lib/canvas_sync/processors/context_modules_processor.rb +24 -0
  13. data/lib/canvas_sync/processors/model_mappings.yml +47 -0
  14. data/lib/canvas_sync/version.rb +1 -1
  15. data/spec/canvas_sync/canvas_sync_spec.rb +3 -3
  16. data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +29 -0
  17. data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +29 -0
  18. data/spec/canvas_sync/models/assignment_spec.rb +9 -0
  19. data/spec/canvas_sync/models/context_module_item_spec.rb +10 -0
  20. data/spec/canvas_sync/models/context_module_spec.rb +19 -0
  21. data/spec/canvas_sync/processors/context_module_items_processor_spec.rb +13 -0
  22. data/spec/canvas_sync/processors/context_modules_processor_spec.rb +13 -0
  23. data/spec/dummy/app/models/assignment.rb +2 -0
  24. data/spec/dummy/app/models/context_module.rb +16 -0
  25. data/spec/dummy/app/models/context_module_item.rb +12 -0
  26. data/spec/dummy/db/migrate/20180411215348_create_context_modules.rb +26 -0
  27. data/spec/dummy/db/migrate/20180411215633_create_context_module_items.rb +25 -0
  28. data/spec/dummy/db/schema.rb +29 -1
  29. data/spec/dummy/log/development.log +1195 -0
  30. data/spec/dummy/log/test.log +8076 -0
  31. data/spec/factories/context_module_factory.rb +8 -0
  32. data/spec/factories/context_module_item_factory.rb +7 -0
  33. data/spec/spec_helper.rb +2 -0
  34. data/spec/support/fixtures/reports/context_module_items.csv +3 -0
  35. data/spec/support/fixtures/reports/context_modules.csv +3 -0
  36. metadata +38 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 304b93956a2e4a40531c28aa515acd34b14d9efcab6d29ab02408b7347251229
4
- data.tar.gz: 36ab39420cd2ea998d7f5725fdbb70a8319a809e5296bb01764c7308aec28a30
3
+ metadata.gz: ccdb328d97ea616a2a39b64ba9850567fefb03ae271306645fe878f82dbdf77d
4
+ data.tar.gz: dca87f606c8c5f4cede335ea725e8b18240592a2464a54295bc96c1812aaf373
5
5
  SHA512:
6
- metadata.gz: ea1aa7b409b498a8609e49f9527555ff5c370c3b84d4c25c265e6a1aad12d86b3492395a1c9da3444a50ea1ef19b2cf62af872736e66ba2ad0fa9a3a9468a7ee
7
- data.tar.gz: 231e24c9a70aa1c3fe1439991e4cf648938839a9dd2cff86d1cba1875c67a59d794ceca6d7f66b8fcde956fb2492c1c579899c94a852a29a3f4ea693be7efd13
6
+ metadata.gz: 2ccfc2afaa8a06b8ba6dde376c12354910ecd9116341168d900b13ad67218d7480fd74485ea11dcf7d2d1c8478c65cb78d28090f7025024a485e5a1682b1a994
7
+ data.tar.gz: 402f8066f80bbd3c0858492ffc6afb91f32b1006d7d46c89368a589b50ed8c720eaaa617a18323fec3fe582b7deec3ef4edb4760c0658123048e781b44236faf
data/README.md CHANGED
@@ -32,6 +32,8 @@ The following custom reports are required for the specified models:
32
32
  - assignments = "Assignments Report" (proserv_assignment_export_csv)
33
33
  - submissions = "Student Submissions" (proserv_student_submissions_csv)
34
34
  - assignment_groups = "Assignment Group Export" (proserv_assignment_group_export_csv)
35
+ - context_modules = "Professional Services Context Modules Report" (proserv_context_modules_csv)
36
+ - context_module_items = "Professional Services Context Module Items Report" (proserv_context_module_items_csv)
35
37
 
36
38
  ## Prerequisites
37
39
 
data/lib/canvas_sync.rb CHANGED
@@ -9,6 +9,8 @@ require "canvas_sync/jobs/sync_provisioning_report_job"
9
9
  require "canvas_sync/jobs/sync_assignments_job"
10
10
  require "canvas_sync/jobs/sync_submissions_job"
11
11
  require "canvas_sync/jobs/sync_assignment_groups_job"
12
+ require "canvas_sync/jobs/sync_context_modules_job"
13
+ require "canvas_sync/jobs/sync_context_module_items_job"
12
14
  require "canvas_sync/jobs/sync_terms_job"
13
15
  require "canvas_sync/jobs/sync_users_job"
14
16
  require "canvas_sync/jobs/sync_roles_job"
@@ -18,7 +20,22 @@ Dir[File.dirname(__FILE__) + "/canvas_sync/importers/*.rb"].each { |file| requi
18
20
  Dir[File.dirname(__FILE__) + "/canvas_sync/generators/*.rb"].each { |file| require file }
19
21
 
20
22
  module CanvasSync
21
- SUPPORTED_MODELS = %w[users courses accounts terms enrollments sections assignments submissions roles admins assignment_groups xlist].freeze
23
+ SUPPORTED_MODELS = %w[
24
+ users
25
+ courses
26
+ accounts
27
+ terms
28
+ enrollments
29
+ sections
30
+ assignments
31
+ submissions
32
+ roles
33
+ admins
34
+ assignment_groups
35
+ context_modules
36
+ context_module_items
37
+ xlist
38
+ ].freeze
22
39
  SUPPORTED_LIVE_EVENTS = %w[course enrollment submission assignment user syllabus grade].freeze
23
40
 
24
41
  # Runs a standard provisioning sync job with no extra report types.
@@ -80,33 +97,66 @@ module CanvasSync
80
97
  def self.default_provisioning_report_chain(models, term_scope=nil, legacy_support=false, account_id=nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
81
98
  term_scope = term_scope.to_s if term_scope
82
99
 
100
+ ##############################
101
+ # Pre provisioning report jobs
102
+ ##############################
103
+
83
104
  # Always sync Terms first
84
- jobs = [{ job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} }]
105
+ pre_provisioning_jobs = [{ job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} }]
85
106
 
86
107
  # Users, roles, and admins are synced before provisioning because they cannot be scoped to term
87
108
  if models.include?("users") && term_scope.present?
88
- jobs.push(job: CanvasSync::Jobs::SyncUsersJob.to_s, options: {})
109
+ pre_provisioning_jobs.push(job: CanvasSync::Jobs::SyncUsersJob.to_s, options: {})
89
110
  models -= ["users"]
90
111
  end
91
112
 
92
113
  if models.include?("roles")
93
- jobs.push(job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {})
114
+ pre_provisioning_jobs.push(job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {})
94
115
  models -= ["roles"]
95
116
  end
96
117
 
97
118
  if models.include?("admins")
98
- jobs.push(job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {})
119
+ pre_provisioning_jobs.push(job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {})
99
120
  models -= ["admins"]
100
121
  end
101
122
 
102
- jobs.push(
123
+ ###############################
124
+ # Post provisioning report jobs
125
+ ###############################
126
+
127
+ post_provisioning_jobs = []
128
+
129
+ if models.include?("assignments")
130
+ post_provisioning_jobs.push(job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {})
131
+ models -= ["assignments"]
132
+ end
133
+
134
+ if models.include?("submissions")
135
+ post_provisioning_jobs.push(job: CanvasSync::Jobs::SyncSubmissionsJob.to_s, options: {})
136
+ models -= ["submissions"]
137
+ end
138
+
139
+ if models.include?("assignment_groups")
140
+ post_provisioning_jobs.push(job: CanvasSync::Jobs::SyncAssignmentGroupsJob.to_s, options: {})
141
+ models -= ["assignment_groups"]
142
+ end
143
+
144
+ if models.include?("context_modules")
145
+ post_provisioning_jobs.push(job: CanvasSync::Jobs::SyncContextModulesJob.to_s, options: {})
146
+ models -= ["context_modules"]
147
+ end
148
+
149
+ if models.include?("context_module_items")
150
+ post_provisioning_jobs.push(job: CanvasSync::Jobs::SyncContextModuleItemsJob.to_s, options: {})
151
+ models -= ["context_module_items"]
152
+ end
153
+
154
+ provisioning_job = {
103
155
  job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s,
104
- options: { term_scope: term_scope, models: models },
105
- )
156
+ options: { term_scope: term_scope, models: models }
157
+ }
106
158
 
107
- jobs.push(job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {}) if models.include?("assignments")
108
- jobs.push(job: CanvasSync::Jobs::SyncSubmissionsJob.to_s, options: {}) if models.include?("submissions")
109
- jobs.push(job: CanvasSync::Jobs::SyncAssignmentGroupsJob.to_s, options: {}) if models.include?("assignment_groups")
159
+ jobs = pre_provisioning_jobs + Array.wrap(provisioning_job) + post_provisioning_jobs
110
160
 
111
161
  global_options = { legacy_support: legacy_support }
112
162
  global_options[:account_id] = account_id if account_id.present?
@@ -5,4 +5,6 @@ class Assignment < ApplicationRecord
5
5
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_course_id
6
6
  belongs_to :assignment_group, optional: true, primary_key: :canvas_assignment_group_id, foreign_key: :canvas_assignment_group_id
7
7
  has_many :submissions, primary_key: :canvas_assignment_id, foreign_key: :canvas_assignment_id
8
+ has_many :context_module_items, primary_key: :canvas_assignment_id, foreign_key: :content_id, dependent: :destroy
9
+ has_many :context_modules, through: :context_module_items
8
10
  end
@@ -0,0 +1,10 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ # A ContextModule is the same as a Canvas Module. They're called ContextModules for 2 reasons:
4
+ # 1 - Module is a reserved word in Rails and you can't call a model a Module
5
+ # 2 - Canvas calls them ContextModules
6
+ class ContextModule < ApplicationRecord
7
+ belongs_to :canvas_context, polymorphic: true, optional: true
8
+ has_many :context_module_items, primary_key: :canvas_context_module_id, foreign_key: :canvas_context_module_id
9
+ has_many :assignments, through: :context_module_items
10
+ end
@@ -0,0 +1,6 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class ContextModuleItem < ApplicationRecord
4
+ belongs_to :context_module, primary_key: :canvas_context_module_id, foreign_key: :canvas_context_module_id, optional: true
5
+ belongs_to :assignment, foreign_key: :content_id, primary_key: :canvas_assignment_id
6
+ end
@@ -0,0 +1,19 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateContextModuleItems < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :context_module_items do |t|
6
+ t.bigint :canvas_context_module_item_id
7
+ t.bigint :canvas_context_module_id
8
+ t.integer :position
9
+ t.string :content_type
10
+ t.bigint :content_id
11
+ t.string :workflow_state
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :context_module_items, :canvas_context_module_item_id, unique: true
17
+ add_index :context_module_items, :canvas_context_module_id
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateContextModules < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :context_modules do |t|
6
+ t.bigint :canvas_context_module_id
7
+ t.bigint :canvas_context_id
8
+ t.string :canvas_context_type
9
+ t.integer :position
10
+ t.string :name
11
+ t.string :workflow_state
12
+ t.datetime :deleted_at
13
+ t.datetime :unlock_at
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :context_modules, :canvas_context_module_id, unique: true
18
+ add_index :context_modules, [:canvas_context_id, :canvas_context_type], name: "index_context_modules_on_context"
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ module CanvasSync
2
+ module Jobs
3
+ class SyncContextModuleItemsJob < ReportStarter
4
+ # Syncs ContextModuleItems
5
+ #
6
+ # Starts a report processor for the context modules report
7
+ # (the proserv_context_module_items_csv report must be enabled)
8
+ #
9
+ # @param job_chain [Hash]
10
+ # @param options [Hash]
11
+ def perform(job_chain, _options)
12
+ report_params = if job_chain[:global_options][:canvas_term_id].present?
13
+ { "parameters[enrollment_term_id]" => job_chain[:global_options][:canvas_term_id] }
14
+ else
15
+ {}
16
+ end
17
+
18
+ super(
19
+ job_chain,
20
+ "proserv_context_module_items_csv",
21
+ report_params,
22
+ CanvasSync::Processors::ContextModuleItemsProcessor.to_s,
23
+ {},
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module CanvasSync
2
+ module Jobs
3
+ class SyncContextModulesJob < ReportStarter
4
+ # Syncs ContextModules
5
+ #
6
+ # Starts a report processor for the context modules report
7
+ # (the proserv_context_modules_csv report must be enabled)
8
+ #
9
+ # @param job_chain [Hash]
10
+ # @param options [Hash]
11
+ def perform(job_chain, _options)
12
+ report_params = if job_chain[:global_options][:canvas_term_id].present?
13
+ { "parameters[enrollment_term_id]" => job_chain[:global_options][:canvas_term_id] }
14
+ else
15
+ {}
16
+ end
17
+
18
+ super(
19
+ job_chain,
20
+ "proserv_context_modules_csv",
21
+ report_params,
22
+ CanvasSync::Processors::ContextModulesProcessor.to_s,
23
+ {},
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "./report_processor"
2
+
3
+ module CanvasSync
4
+ module Processors
5
+ # Processes a context modules report using the bulk importer.
6
+ #
7
+ # @param report_file_path [String]
8
+ # @param options [Hash]
9
+ class ContextModuleItemsProcessor < ReportProcessor
10
+ def self.process(report_file_path, _options)
11
+ new(report_file_path)
12
+ end
13
+
14
+ def initialize(report_file_path)
15
+ CanvasSync::Importers::BulkImporter.import(
16
+ report_file_path,
17
+ mapping[:context_module_items][:report_columns],
18
+ ContextModuleItem,
19
+ mapping[:context_module_items][:conflict_target].to_sym,
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "./report_processor"
2
+
3
+ module CanvasSync
4
+ module Processors
5
+ # Processes a context modules report using the bulk importer.
6
+ #
7
+ # @param report_file_path [String]
8
+ # @param options [Hash]
9
+ class ContextModulesProcessor < ReportProcessor
10
+ def self.process(report_file_path, _options)
11
+ new(report_file_path)
12
+ end
13
+
14
+ def initialize(report_file_path)
15
+ CanvasSync::Importers::BulkImporter.import(
16
+ report_file_path,
17
+ mapping[:context_modules][:report_columns],
18
+ ContextModule,
19
+ mapping[:context_modules][:conflict_target].to_sym,
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -273,3 +273,50 @@ assignment_groups:
273
273
  updated_at:
274
274
  database_column_name: canvas_updated_at
275
275
  type: datetime
276
+
277
+ context_modules:
278
+ conflict_target: canvas_context_module_id
279
+ report_columns:
280
+ canvas_context_module_id:
281
+ database_column_name: canvas_context_module_id
282
+ type: integer
283
+ canvas_context_id:
284
+ database_column_name: canvas_context_id
285
+ type: integer
286
+ canvas_context_type:
287
+ database_column_name: canvas_context_type
288
+ type: string
289
+ position:
290
+ database_column_name: position
291
+ type: integer
292
+ name:
293
+ database_column_name: name
294
+ type: string
295
+ workflow_state:
296
+ database_column_name: workflow_state
297
+ type: string
298
+ deleted_at:
299
+ database_column_name: deleted_at
300
+ type: datetime
301
+
302
+ context_module_items:
303
+ conflict_target: canvas_context_module_item_id
304
+ report_columns:
305
+ canvas_context_module_item_id:
306
+ database_column_name: canvas_context_module_item_id
307
+ type: integer
308
+ context_module_id:
309
+ database_column_name: canvas_context_module_id
310
+ type: integer
311
+ position:
312
+ database_column_name: position
313
+ type: integer
314
+ content_type:
315
+ database_column_name: content_type
316
+ type: string
317
+ content_id:
318
+ database_column_name: content_id
319
+ type: integer
320
+ workflow_state:
321
+ database_column_name: workflow_state
322
+ type: string
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.3.22".freeze
2
+ VERSION = "0.3.23".freeze
3
3
  end
@@ -114,7 +114,7 @@ RSpec.describe CanvasSync do
114
114
  expect(chain).to eq(
115
115
  jobs: [
116
116
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
117
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments assignments] } },
117
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
118
118
  { job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {} },
119
119
  ],
120
120
  global_options: { legacy_support: false },
@@ -129,7 +129,7 @@ RSpec.describe CanvasSync do
129
129
  expect(chain).to eq(
130
130
  jobs: [
131
131
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
132
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments submissions] } },
132
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
133
133
  { job: CanvasSync::Jobs::SyncSubmissionsJob.to_s, options: {} },
134
134
  ],
135
135
  global_options: { legacy_support: false },
@@ -144,7 +144,7 @@ RSpec.describe CanvasSync do
144
144
  expect(chain).to eq(
145
145
  jobs: [
146
146
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
147
- { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments assignment_groups] } },
147
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
148
148
  { job: CanvasSync::Jobs::SyncAssignmentGroupsJob.to_s, options: {} },
149
149
  ],
150
150
  global_options: { legacy_support: false },
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CanvasSync::Jobs::SyncContextModuleItemsJob do
4
+ describe "#perform" do
5
+ context "a term id is in the global_options" do
6
+ it "enqueues a ReportStarter for the proserv_context_module_items_csv for the given term" do
7
+ expect_any_instance_of(Bearcat::Client).to receive(:start_report)
8
+ .with("self", "proserv_context_module_items_csv", "parameters[enrollment_term_id]" => 1)
9
+ .and_return("id" => 1)
10
+
11
+ expect(CanvasSync::Jobs::ReportChecker).to receive(:set).and_call_original
12
+
13
+ CanvasSync::Jobs::SyncContextModuleItemsJob.perform_now({ jobs: [], global_options: { canvas_term_id: 1 } }, {})
14
+ end
15
+ end
16
+
17
+ context "no term id is specified" do
18
+ it "enqueues a ReportStarter for the proserv_context_module_items_csv for all terms" do
19
+ expect_any_instance_of(Bearcat::Client).to receive(:start_report)
20
+ .with("self", "proserv_context_module_items_csv", {})
21
+ .and_return("id" => 1)
22
+
23
+ expect(CanvasSync::Jobs::ReportChecker).to receive(:set).and_call_original
24
+
25
+ CanvasSync::Jobs::SyncContextModuleItemsJob.perform_now({ jobs: [], global_options: {} }, {})
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe CanvasSync::Jobs::SyncContextModulesJob do
4
+ describe "#perform" do
5
+ context "a term id is in the global_options" do
6
+ it "enqueues a ReportStarter for the proserv_context_modules_csv for the given term" do
7
+ expect_any_instance_of(Bearcat::Client).to receive(:start_report)
8
+ .with("self", "proserv_context_modules_csv", "parameters[enrollment_term_id]" => 1)
9
+ .and_return("id" => 1)
10
+
11
+ expect(CanvasSync::Jobs::ReportChecker).to receive(:set).and_call_original
12
+
13
+ CanvasSync::Jobs::SyncContextModulesJob.perform_now({ jobs: [], global_options: { canvas_term_id: 1 } }, {})
14
+ end
15
+ end
16
+
17
+ context "no term id is specified" do
18
+ it "enqueues a ReportStarter for the proserv_context_modules_csv for all terms" do
19
+ expect_any_instance_of(Bearcat::Client).to receive(:start_report)
20
+ .with("self", "proserv_context_modules_csv", {})
21
+ .and_return("id" => 1)
22
+
23
+ expect(CanvasSync::Jobs::ReportChecker).to receive(:set).and_call_original
24
+
25
+ CanvasSync::Jobs::SyncContextModulesJob.perform_now({ jobs: [], global_options: {} }, {})
26
+ end
27
+ end
28
+ end
29
+ end