canvas_sync 0.3.22 → 0.3.23

Sign up to get free protection for your applications and to get access to all the features.
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