canvas_sync 0.22.8 → 0.22.11
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.
- checksums.yaml +4 -4
- data/lib/canvas_sync/concerns/auto_relations.rb +11 -0
- data/lib/canvas_sync/concerns/live_event_sync.rb +2 -2
- data/lib/canvas_sync/concerns/user/sharded.rb +18 -0
- data/lib/canvas_sync/generators/templates/migrations/create_assignment_overrides.rb +25 -0
- data/lib/canvas_sync/generators/templates/models/assignment_override.rb +28 -0
- data/lib/canvas_sync/jobs/sync_assignment_overrides_job.rb +20 -0
- data/lib/canvas_sync/processors/assignment_overrides_processor.rb +41 -0
- data/lib/canvas_sync/processors/model_mappings.yml +43 -0
- data/lib/canvas_sync/version.rb +1 -1
- data/lib/canvas_sync.rb +4 -0
- data/spec/canvas_sync/processors/assignment_overrides_processor_spec.rb +45 -0
- data/spec/dummy/app/models/assignment_override.rb +34 -0
- data/spec/dummy/app/models/learning_outcome_result.rb +4 -4
- data/spec/dummy/app/models/rubric.rb +4 -4
- data/spec/dummy/app/models/rubric_assessment.rb +1 -0
- data/spec/dummy/app/models/rubric_association.rb +3 -3
- data/spec/dummy/app/models/user.rb +1 -0
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/db/migrate/20241223080202_create_assignment_overrides.rb +25 -0
- data/spec/dummy/db/migrate/{20240408223326_create_course_nicknames.rb → 20250319194134_create_course_nicknames.rb} +1 -1
- data/spec/dummy/db/migrate/{20240509105100_create_rubrics.rb → 20250319194135_create_rubrics.rb} +4 -4
- data/spec/dummy/db/schema.rb +191 -178
- data/spec/dummy/tmp/local_secret.txt +1 -0
- data/spec/factories/assignment_override_factory.rb +10 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/fixtures/reports/assignment_overrides.csv +3 -0
- data/spec/support/fixtures/reports/assignment_overrides_conflict.csv +2 -0
- metadata +26 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bbd02c6460435a1bb3bc0c33c997d60c09c8f0fffbc8afe8292cbc855d51306
|
4
|
+
data.tar.gz: 99f488b171bb99dd51b1f20b4d39963f0f94624a5decf96b9e8d270355efe5b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eed8224aa09c93c4d95db72db479edd40e3a75faa5f548e208b99afffb43b453de8b4a00202ffe01f1c7c8b2c6a2c11b978f203e5a406760e61236a0bfa7e372
|
7
|
+
data.tar.gz: 58e2541a97e2885bea180b30ad9ae8af85c074c635dc12ec309985685dd74de48ccadca81d87ec8791d0b4b5691b11eb198e47a2c9ad18e6b64c4f7fdf126320
|
@@ -12,9 +12,9 @@ module CanvasSync::Concerns
|
|
12
12
|
meta = event[:metadata]
|
13
13
|
payload = event[:payload] || event[:body]
|
14
14
|
|
15
|
-
canvas_id = payload[:id] || payload[:"#{name.underscore}_id"]
|
16
|
-
inst = self.find_or_initialize_by(canvas_id: canvas_id)
|
17
15
|
model, _, subtype = meta[:event_name].rpartition('_')
|
16
|
+
canvas_id = payload[:id] || payload[:"#{model}_id"] || payload[:"#{name.underscore}_id"]
|
17
|
+
inst = self.find_or_initialize_by(canvas_id: canvas_id)
|
18
18
|
|
19
19
|
result = inst.run_callbacks(:process_live_event) do
|
20
20
|
inst.process_live_event(subtype.to_sym, payload, meta)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CreateAssignmentOverrides < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :assignment_overrides do |t|
|
4
|
+
t.bigint :canvas_id, null: false
|
5
|
+
t.bigint :canvas_assignment_id
|
6
|
+
t.bigint :canvas_group_id
|
7
|
+
t.bigint :canvas_course_id
|
8
|
+
t.bigint :canvas_section_id
|
9
|
+
t.bigint :student_ids, array: true
|
10
|
+
t.datetime :due_at
|
11
|
+
t.boolean :all_day
|
12
|
+
t.datetime :all_day_date
|
13
|
+
t.datetime :unlock_at
|
14
|
+
t.datetime :lock_at
|
15
|
+
t.string :title
|
16
|
+
t.string :workflow_state
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index :assignment_overrides, :canvas_id, unique: true
|
22
|
+
add_index :assignment_overrides, :canvas_assignment_id
|
23
|
+
add_index :assignment_overrides, :canvas_course_id
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class AssignmentOverride < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
6
|
+
|
7
|
+
canvas_sync_features :defaults
|
8
|
+
|
9
|
+
validates :canvas_id, uniqueness: true, presence: true
|
10
|
+
belongs_to :assignment, primary_key: :canvas_id, foreign_key: :canvas_assignment_id, optional: true
|
11
|
+
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
12
|
+
|
13
|
+
api_syncable({
|
14
|
+
canvas_id: :id,
|
15
|
+
canvas_assignment_id: :assignment_id,
|
16
|
+
title: :title,
|
17
|
+
workflow_state: :workflow_state,
|
18
|
+
due_at: :due_at,
|
19
|
+
all_day: :all_day,
|
20
|
+
all_day_date: :all_day_date,
|
21
|
+
unlock_at: :unlock_at,
|
22
|
+
lock_at: :lock_at,
|
23
|
+
student_ids: ->(r) { JSON.parse(r[:student_ids]) if r[:student_ids].present? },
|
24
|
+
canvas_group_id: :group_id,
|
25
|
+
canvas_section_id: :course_section_id,
|
26
|
+
canvas_course_id: -> (r) { r.dig(:assignment, :course_id) }
|
27
|
+
}, -> (api) { api.assignment_override(canvas_assignment_id, canvas_id) })
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CanvasSync
|
2
|
+
module Jobs
|
3
|
+
class SyncAssignmentOverridesJob < ReportStarter
|
4
|
+
# Syncs SyncAssignmentOverrides
|
5
|
+
#
|
6
|
+
# Starts a report processor for the assignment overrides report
|
7
|
+
# (the proserv_assignment_overrides_csv report must be enabled)
|
8
|
+
#
|
9
|
+
# @param options [Hash]
|
10
|
+
def perform(options)
|
11
|
+
super(
|
12
|
+
"proserv_assignment_overrides_csv",
|
13
|
+
merge_report_params(options),
|
14
|
+
CanvasSync::Processors::AssignmentOverridesProcessor.to_s,
|
15
|
+
{},
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative "./report_processor"
|
2
|
+
|
3
|
+
module CanvasSync
|
4
|
+
module Processors
|
5
|
+
# Processes a assignment overrides report using the bulk importer.
|
6
|
+
#
|
7
|
+
# @param report_file_path [String]
|
8
|
+
# @param options [Hash]
|
9
|
+
class AssignmentOverridesProcessor < ReportProcessor
|
10
|
+
# Class method that starts processing (for consistent interface with other processors).
|
11
|
+
def self.process(report_file_path, options, report_id)
|
12
|
+
new(report_file_path, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(report_file_path, options)
|
16
|
+
do_bulk_import(report_file_path, AssignmentOverride, options: options) do |row|
|
17
|
+
# Handle transforms here instead of in mappings
|
18
|
+
row[:student_ids] = parse_student_ids(row[:student_ids])
|
19
|
+
|
20
|
+
# Convert empty/null/'0' values to nil
|
21
|
+
# For Ruby 2.7
|
22
|
+
row[:group_id] = parse_nil_value(row[:group_id])
|
23
|
+
row[:course_section_id] = parse_nil_value(row[:course_section_id])
|
24
|
+
row
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse_student_ids(value)
|
31
|
+
value.present? ? JSON.parse(value) : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_nil_value(value)
|
35
|
+
# Convert "0", "null", or empty string to nil
|
36
|
+
return nil if %w[0 null].include?(value) || value == ''
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -293,6 +293,49 @@ submissions:
|
|
293
293
|
database_column_name: workflow_state
|
294
294
|
type: string
|
295
295
|
|
296
|
+
assignment_overrides:
|
297
|
+
conflict_target: id
|
298
|
+
report_columns:
|
299
|
+
id:
|
300
|
+
database_column_name: canvas_id
|
301
|
+
type: integer
|
302
|
+
assignment_id:
|
303
|
+
database_column_name: canvas_assignment_id
|
304
|
+
type: integer
|
305
|
+
student_ids:
|
306
|
+
database_column_name: student_ids
|
307
|
+
type: array
|
308
|
+
group_id:
|
309
|
+
database_column_name: canvas_group_id
|
310
|
+
type: integer
|
311
|
+
course_section_id:
|
312
|
+
database_column_name: canvas_section_id
|
313
|
+
type: integer
|
314
|
+
course_id:
|
315
|
+
database_column_name: canvas_course_id
|
316
|
+
type: integer
|
317
|
+
title:
|
318
|
+
database_column_name: title
|
319
|
+
type: string
|
320
|
+
due_at:
|
321
|
+
database_column_name: due_at
|
322
|
+
type: datetime
|
323
|
+
unlock_at:
|
324
|
+
database_column_name: unlock_at
|
325
|
+
type: datetime
|
326
|
+
lock_at:
|
327
|
+
database_column_name: lock_at
|
328
|
+
type: datetime
|
329
|
+
all_day:
|
330
|
+
database_column_name: all_day
|
331
|
+
type: boolean
|
332
|
+
all_day_date:
|
333
|
+
database_column_name: all_day_date
|
334
|
+
type: datetime
|
335
|
+
workflow_state:
|
336
|
+
database_column_name: workflow_state
|
337
|
+
type: string
|
338
|
+
|
296
339
|
assignment_groups:
|
297
340
|
conflict_target: id
|
298
341
|
report_columns:
|
data/lib/canvas_sync/version.rb
CHANGED
data/lib/canvas_sync.rb
CHANGED
@@ -54,6 +54,7 @@ module CanvasSync
|
|
54
54
|
rubric_associations
|
55
55
|
rubric_assessments
|
56
56
|
course_progresses
|
57
|
+
assignment_overrides
|
57
58
|
].freeze
|
58
59
|
|
59
60
|
SUPPORTED_TERM_SCOPE_MODELS = %w[
|
@@ -66,6 +67,7 @@ module CanvasSync
|
|
66
67
|
rubric_associations
|
67
68
|
rubric_assessments
|
68
69
|
course_progresses
|
70
|
+
assignment_overrides
|
69
71
|
].freeze
|
70
72
|
|
71
73
|
DEFAULT_TERM_SCOPE_MODELS = %w[
|
@@ -78,6 +80,7 @@ module CanvasSync
|
|
78
80
|
rubric_associations
|
79
81
|
rubric_assessments
|
80
82
|
course_progresses
|
83
|
+
assignment_overrides
|
81
84
|
].freeze
|
82
85
|
|
83
86
|
SUPPORTED_LIVE_EVENTS = %w[
|
@@ -166,6 +169,7 @@ module CanvasSync
|
|
166
169
|
assignments: CanvasSync::Jobs::SyncAssignmentsJob,
|
167
170
|
submissions: CanvasSync::Jobs::SyncSubmissionsJob,
|
168
171
|
assignment_groups: CanvasSync::Jobs::SyncAssignmentGroupsJob,
|
172
|
+
assignment_overrides: CanvasSync::Jobs::SyncAssignmentOverridesJob,
|
169
173
|
context_modules: CanvasSync::Jobs::SyncContextModulesJob,
|
170
174
|
context_module_items: CanvasSync::Jobs::SyncContextModuleItemsJob,
|
171
175
|
content_migrations: CanvasSync::Jobs::SyncContentMigrationsJob,
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::Processors::AssignmentOverridesProcessor do
|
4
|
+
describe '#process' do
|
5
|
+
it 'inserts assignment overrides' do
|
6
|
+
expect {
|
7
|
+
described_class.process('spec/support/fixtures/reports/assignment_overrides.csv', {}, 1)
|
8
|
+
}.to change { AssignmentOverride.count }.by(2)
|
9
|
+
|
10
|
+
override = AssignmentOverride.first
|
11
|
+
expect(override).to have_attributes(
|
12
|
+
canvas_id: 1,
|
13
|
+
canvas_assignment_id: 1,
|
14
|
+
student_ids: [1, 2],
|
15
|
+
canvas_group_id: nil,
|
16
|
+
canvas_section_id: nil,
|
17
|
+
canvas_course_id: 1,
|
18
|
+
title: "Override 1",
|
19
|
+
workflow_state: "active"
|
20
|
+
)
|
21
|
+
expect(override.due_at.strftime('%Y-%m-%d %H:%M:%S')).to eq('2024-05-01 09:00:00')
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'record already present in the database' do
|
25
|
+
before do
|
26
|
+
described_class.process('spec/support/fixtures/reports/assignment_overrides.csv', {}, 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'updates the override' do
|
30
|
+
expect {
|
31
|
+
described_class.process('spec/support/fixtures/reports/assignment_overrides_conflict.csv', {}, 1)
|
32
|
+
}.to change { AssignmentOverride.count }.by(0)
|
33
|
+
|
34
|
+
override = AssignmentOverride.first
|
35
|
+
expect(override).to have_attributes(
|
36
|
+
canvas_id: 1,
|
37
|
+
student_ids: [1, 2, 3],
|
38
|
+
title: "Override 1 Updated",
|
39
|
+
workflow_state: "active"
|
40
|
+
)
|
41
|
+
expect(override.due_at.strftime('%Y-%m-%d %H:%M:%S')).to eq('2024-06-01 09:00:00')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,34 @@
|
|
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 AssignmentOverride < ApplicationRecord
|
10
|
+
include CanvasSync::Record
|
11
|
+
include CanvasSync::Concerns::ApiSyncable
|
12
|
+
|
13
|
+
canvas_sync_features :defaults
|
14
|
+
|
15
|
+
validates :canvas_id, uniqueness: true, presence: true
|
16
|
+
belongs_to :assignment, primary_key: :canvas_id, foreign_key: :canvas_assignment_id, optional: true
|
17
|
+
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
18
|
+
|
19
|
+
api_syncable({
|
20
|
+
canvas_id: :id,
|
21
|
+
canvas_assignment_id: :assignment_id,
|
22
|
+
title: :title,
|
23
|
+
workflow_state: :workflow_state,
|
24
|
+
due_at: :due_at,
|
25
|
+
all_day: :all_day,
|
26
|
+
all_day_date: :all_day_date,
|
27
|
+
unlock_at: :unlock_at,
|
28
|
+
lock_at: :lock_at,
|
29
|
+
student_ids: ->(r) { JSON.parse(r[:student_ids]) if r[:student_ids].present? },
|
30
|
+
canvas_group_id: :group_id,
|
31
|
+
canvas_section_id: :course_section_id,
|
32
|
+
canvas_course_id: -> (r) { r.dig(:assignment, :course_id) }
|
33
|
+
}, -> (api) { api.assignment_override(canvas_assignment_id, canvas_id) })
|
34
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# # #
|
4
|
-
# AUTO GENERATED
|
5
|
-
# This
|
6
|
-
# You can
|
7
|
-
#
|
4
|
+
# AUTO GENERATED MODEL
|
5
|
+
# This model was auto generated by the CanvasSync Gem.
|
6
|
+
# You can customize it as needed, but make sure you test
|
7
|
+
# any changes you make to the auto generated methods.
|
8
8
|
#
|
9
9
|
|
10
10
|
|
@@ -13,9 +13,9 @@ class Rubric < ApplicationRecord
|
|
13
13
|
canvas_sync_features :defaults
|
14
14
|
|
15
15
|
validates :canvas_id, uniqueness: true, presence: true
|
16
|
-
belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
17
|
-
belongs_to :rubric, primary_key: :canvas_id, foreign_key: :canvas_rubric_id
|
18
|
-
belongs_to :context, polymorphic: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
|
16
|
+
belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
17
|
+
belongs_to :rubric, primary_key: :canvas_id, foreign_key: :canvas_rubric_id # based on another rubric
|
18
|
+
belongs_to :context, polymorphic: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
|
19
19
|
|
20
20
|
has_many :rubric_assessments, through: :rubric_associations, dependent: :destroy
|
21
21
|
has_many :rubric_associations, primary_key: :canvas_id, foreign_key: :canvas_rubric_id
|
@@ -32,5 +32,5 @@ class Rubric < ApplicationRecord
|
|
32
32
|
free_form_criterion_comments: :free_form_criterion_comments,
|
33
33
|
hide_score_total: :hide_score_total,
|
34
34
|
data: :data,
|
35
|
-
}, -> (api) { api.
|
35
|
+
}, -> (api) { api.rubrics("self", canvas_id) })
|
36
36
|
end
|
@@ -8,13 +8,13 @@
|
|
8
8
|
|
9
9
|
class RubricAssociation < ApplicationRecord
|
10
10
|
include CanvasSync::Record
|
11
|
+
include CanvasSync::Concerns::ApiSyncable
|
11
12
|
|
12
13
|
canvas_sync_features :defaults
|
13
14
|
|
14
15
|
validates :canvas_id, uniqueness: true, presence: true
|
15
16
|
belongs_to :rubric, primary_key: :canvas_id, foreign_key: :canvas_rubric_id, optional: true
|
16
|
-
belongs_to :association_object, polymorphic: true, primary_key: :canvas_id, foreign_type: :canvas_association_type,
|
17
|
-
belongs_to :context, polymorphic: true, primary_key: :canvas_id, foreign_key: :canvas_context_id,
|
18
|
-
|
17
|
+
belongs_to :association_object, polymorphic: true, primary_key: :canvas_id, foreign_type: :canvas_association_type, optional: true
|
18
|
+
belongs_to :context, polymorphic: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, optional: true
|
19
19
|
has_many :rubric_assessments
|
20
20
|
end
|
@@ -27,6 +27,7 @@ class User < ApplicationRecord
|
|
27
27
|
has_many :admin_roles, through: :admins, source: :role
|
28
28
|
has_many :submissions, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
29
29
|
has_many :group_memberships, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
30
|
+
has_many :rubrics, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
30
31
|
|
31
32
|
api_syncable({
|
32
33
|
sis_id: :sis_user_id,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CreateAssignmentOverrides < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :assignment_overrides do |t|
|
4
|
+
t.bigint :canvas_id, null: false
|
5
|
+
t.bigint :canvas_assignment_id
|
6
|
+
t.bigint :canvas_group_id
|
7
|
+
t.bigint :canvas_course_id
|
8
|
+
t.bigint :canvas_section_id
|
9
|
+
t.bigint :student_ids, array: true
|
10
|
+
t.datetime :due_at
|
11
|
+
t.boolean :all_day
|
12
|
+
t.datetime :all_day_date
|
13
|
+
t.datetime :unlock_at
|
14
|
+
t.datetime :lock_at
|
15
|
+
t.string :title
|
16
|
+
t.string :workflow_state
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index :assignment_overrides, :canvas_id, unique: true
|
22
|
+
add_index :assignment_overrides, :canvas_assignment_id
|
23
|
+
add_index :assignment_overrides, :canvas_course_id
|
24
|
+
end
|
25
|
+
end
|
@@ -9,7 +9,7 @@
|
|
9
9
|
class CreateCourseNicknames < ActiveRecord::Migration[5.1]
|
10
10
|
def change
|
11
11
|
create_table :course_nicknames do |t|
|
12
|
-
t.bigint :canvas_user_preference_value_id, null:
|
12
|
+
t.bigint :canvas_user_preference_value_id, null: true
|
13
13
|
t.integer :canvas_user_id
|
14
14
|
t.integer :canvas_course_id
|
15
15
|
t.string :nickname
|
data/spec/dummy/db/migrate/{20240509105100_create_rubrics.rb → 20250319194135_create_rubrics.rb}
RENAMED
@@ -1,8 +1,8 @@
|
|
1
1
|
# #
|
2
|
-
# AUTO GENERATED
|
3
|
-
# This
|
4
|
-
# You can
|
5
|
-
#
|
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
6
|
#
|
7
7
|
|
8
8
|
|