canvas_sync 0.8.1 → 0.8.2
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/README.md +20 -1
- data/lib/canvas_sync.rb +1 -0
- data/lib/canvas_sync/api_syncable.rb +65 -0
- data/lib/canvas_sync/generators/templates/models/account.rb +8 -0
- data/lib/canvas_sync/generators/templates/models/assignment.rb +21 -0
- data/lib/canvas_sync/generators/templates/models/course.rb +14 -0
- data/lib/canvas_sync/generators/templates/models/enrollment.rb +16 -0
- data/lib/canvas_sync/generators/templates/models/section.rb +12 -0
- data/lib/canvas_sync/generators/templates/models/submission.rb +11 -0
- data/lib/canvas_sync/generators/templates/models/user.rb +13 -0
- data/lib/canvas_sync/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f7a480e67851b3de7f12b0df8569aa09aa36431
|
4
|
+
data.tar.gz: 51923b6e9707be4f3969160cfa495d1bb75c19c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '029af7cd4b8d5f277c5688c9439fe98435cf40dd2f8f88093b8c743a527d4864f3c3a7af4e4544bdd8ec0372fff954aea6f688a59dee6f3608e3fd4af3b4abe1'
|
7
|
+
data.tar.gz: 4d4825e66d7e8f2e3850c6422eba37fc0afcee18b9380039837c15238f25fc73aab8dda0c58784b4557f84360f2d4b5ee4492582ae568167d57ab208c3315613
|
data/README.md
CHANGED
@@ -189,7 +189,7 @@ Overrides are useful for two scenarios:
|
|
189
189
|
|
190
190
|
In order to create an override, place a file called `canvas_sync_provisioning_mapping.yml` in your Rails `config` directory. Define the tables and columns you want to override using the following format:
|
191
191
|
|
192
|
-
```
|
192
|
+
```ruby
|
193
193
|
users:
|
194
194
|
conflict_target: canvas_user_id # This must be a unique field that is present in the report and the database
|
195
195
|
report_columns: # The keys specified here are the column names in the report CSV
|
@@ -198,6 +198,25 @@ users:
|
|
198
198
|
type: integer
|
199
199
|
```
|
200
200
|
|
201
|
+
### API Sync
|
202
|
+
Several models implement the `ApiSyncable` Concern. This is done in the Model Templates so as to be customizable and tweakable.
|
203
|
+
Models that `include CanvasSync::ApiSyncable` should also call the `api_syncable` class method to configure the Synchronization.
|
204
|
+
`api_syncable` takes two arguments and an optional block callback:
|
205
|
+
```ruby
|
206
|
+
class CanvasSyncModel < ApplicationRecord
|
207
|
+
api_syncable(
|
208
|
+
{
|
209
|
+
local_field: :response_field, # api_response[:response_field] will be mapped to local_field on the model.
|
210
|
+
local_field: -> (api_response) { api_response[:some_field] + 5 }, # A calculated result will be mapped to local_field on the model. The lambda is executed in the context of the model instance.
|
211
|
+
},
|
212
|
+
-> (bearcat) { bearcat.some_request(some_model_getter) } # A lambda, executed in the context of the model instance, to actually make the API call. Should accept 0 or 1 parameters. Must accept 0 parameters if your `canvas_sync_client` requires an `account_id`
|
213
|
+
) do |api_response, mapped_fields| # Must accept 1-2 parameters
|
214
|
+
# Override behavior for actually applying the response to the model instance
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
|
201
220
|
## Legacy Support
|
202
221
|
|
203
222
|
If you have an old style tool that needs to sync data on a row by row basis, you can pass in the `legacy_support: true` option. In order for this to work, your models must have a `create_or_update_from_csv` class method defined that accepts a row argument. This method will get passed each row from the CSV, and it's up to you to persist it.
|
data/lib/canvas_sync.rb
CHANGED
@@ -3,6 +3,7 @@ require "canvas_sync/version"
|
|
3
3
|
require "canvas_sync/engine"
|
4
4
|
require "canvas_sync/job"
|
5
5
|
require "canvas_sync/sidekiq_job"
|
6
|
+
require "canvas_sync/api_syncable"
|
6
7
|
require "canvas_sync/jobs/report_starter"
|
7
8
|
require "canvas_sync/jobs/report_checker"
|
8
9
|
require "canvas_sync/jobs/report_processor_job"
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CanvasSync::ApiSyncable
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
attr_accessor :api_sync_map, :api_sync_fetch, :api_sync_block
|
6
|
+
|
7
|
+
def api_syncable(map, fetch, &blk)
|
8
|
+
self.api_sync_map = map
|
9
|
+
self.api_sync_fetch = fetch
|
10
|
+
self.api_sync_block = blk
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def sync_from_api(retries: 3)
|
15
|
+
params = api_call_with_retry(retries || 3) {
|
16
|
+
blk = self.class.api_sync_fetch
|
17
|
+
case blk.arity
|
18
|
+
when 1
|
19
|
+
self.instance_exec(canvas_sync_client, &blk)
|
20
|
+
else
|
21
|
+
self.instance_exec(&blk)
|
22
|
+
end
|
23
|
+
}
|
24
|
+
update_from_api_params!(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
def update_from_api_params!(api_params)
|
28
|
+
api_params = api_params.with_indifferent_access
|
29
|
+
apply_block = self.class.api_sync_block
|
30
|
+
map = self.class.api_sync_map
|
31
|
+
|
32
|
+
mapped_params = {}
|
33
|
+
if map.present?
|
34
|
+
map.each do |local_name, remote_name|
|
35
|
+
mapped_params[local_name] = remote_name.respond_to?(:call) ? self.instance_exec(api_params, &remote_name) : api_params[remote_name]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if apply_block.present?
|
40
|
+
case apply_block.arity
|
41
|
+
when 1
|
42
|
+
self.instance_exec(api_params, &apply_block)
|
43
|
+
when 2
|
44
|
+
self.instance_exec(api_params, mapped_params, &apply_block)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
mapped_params.each do |local_name, val|
|
48
|
+
send("#{local_name}=", val)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
save! if changed?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def api_call_with_retry(retries=3)
|
58
|
+
tries ||= retries
|
59
|
+
yield
|
60
|
+
rescue Faraday::ConnectionFailed => e
|
61
|
+
raise e if (tries -= 1).zero?
|
62
|
+
sleep 5
|
63
|
+
retry
|
64
|
+
end
|
65
|
+
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Account < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_account_id, uniqueness: true, presence: true
|
7
|
+
|
8
|
+
api_syncable({
|
9
|
+
name: :name,
|
10
|
+
status: :status,
|
11
|
+
canvas_parent_id: :canvas_parent_id,
|
12
|
+
}, -> (api) { api.account(canvas_account_id) })
|
5
13
|
end
|
@@ -1,10 +1,31 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Assignment < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_assignment_id, uniqueness: true, presence: true
|
5
7
|
belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_course_id
|
6
8
|
belongs_to :assignment_group, optional: true, primary_key: :canvas_assignment_group_id, foreign_key: :canvas_assignment_group_id
|
7
9
|
has_many :submissions, primary_key: :canvas_assignment_id, foreign_key: :canvas_assignment_id
|
8
10
|
has_many :context_module_items, primary_key: :canvas_assignment_id, foreign_key: :content_id, dependent: :destroy
|
9
11
|
has_many :context_modules, through: :context_module_items
|
12
|
+
|
13
|
+
api_syncable({
|
14
|
+
title: :tile,
|
15
|
+
description: :description,
|
16
|
+
due_at: :due_at,
|
17
|
+
unlock_at: :unlock_at,
|
18
|
+
points_posible: :points_posible,
|
19
|
+
min_score: :min_score,
|
20
|
+
max_score: :max_score,
|
21
|
+
mastery_score: :mastery_score,
|
22
|
+
grading_type: :grading_type,
|
23
|
+
submission_types: lambda { |_a, p| p["submission_types"].join(",") },
|
24
|
+
workflow_state: :workflow_state,
|
25
|
+
context_id: :course_id,
|
26
|
+
context_type: lambda { |_a, _p| "Course" },
|
27
|
+
canvas_assignment_group_id: :canvas_assignment_group_id,
|
28
|
+
grading_scheme_id: :grading_scheme_id,
|
29
|
+
grading_standard_id: :grading_standard_id,
|
30
|
+
}, -> (api) { api.assignment(context_id, canvas_assignment_id) })
|
10
31
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Course < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_course_id, uniqueness: true, presence: true
|
5
7
|
belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_term_id, optional: true
|
6
8
|
has_many :enrollments, primary_key: :canvas_course_id, foreign_key: :canvas_course_id
|
@@ -8,4 +10,16 @@ class Course < ApplicationRecord
|
|
8
10
|
has_many :assignments, as: :context, primary_key: :canvas_course_id
|
9
11
|
has_many :submissions, primary_key: :canvas_course_id, foreign_key: :canvas_course_id
|
10
12
|
has_many :assignment_groups, primary_key: :canvas_course_id, foreign_key: :canvas_course_id
|
13
|
+
|
14
|
+
api_syncable({
|
15
|
+
sis_id: :sis_id,
|
16
|
+
short_name: :short_name,
|
17
|
+
long_name: :long_name,
|
18
|
+
status: :status,
|
19
|
+
canvas_term_id: :canvas_term_id,
|
20
|
+
canvas_account_id: :canvas_account_id,
|
21
|
+
term_sis_id: :term_sis_id,
|
22
|
+
start_date: :start_date,
|
23
|
+
end_date: :end_date,
|
24
|
+
}, -> (api) { api.course(canvas_course_id) })
|
11
25
|
end
|
@@ -1,8 +1,24 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Enrollment < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_enrollment_id, uniqueness: true, presence: true
|
5
7
|
belongs_to :user, primary_key: :canvas_user_id, foreign_key: :canvas_user_id, optional: true
|
6
8
|
belongs_to :course, primary_key: :canvas_course_id, foreign_key: :canvas_course_id, optional: true
|
7
9
|
belongs_to :section, primary_key: :canvas_section_id, foreign_key: :canvas_section_id, optional: true
|
10
|
+
|
11
|
+
api_syncable({
|
12
|
+
canvas_enrollment_id: :canvas_enrollment_id,
|
13
|
+
canvas_course_id: :canvas_course_id,
|
14
|
+
course_sis_id: :course_sis_id,
|
15
|
+
canvas_user_id: :canvas_user_id,
|
16
|
+
user_sis_id: :user_sis_id,
|
17
|
+
canvas_section_id: :canvas_section_id,
|
18
|
+
section_sis_id: :section_sis_id,
|
19
|
+
role: :role,
|
20
|
+
role_id: :role_id,
|
21
|
+
status: :status,
|
22
|
+
base_role_type: :base_role_type,
|
23
|
+
}, -> (api) { api.enrollment("self", canvas_enrollment_id) })
|
8
24
|
end
|
@@ -1,7 +1,19 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Section < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_section_id, uniqueness: true, presence: true
|
5
7
|
belongs_to :course, primary_key: :canvas_course_id, foreign_key: :canvas_course_id, optional: true
|
6
8
|
has_many :enrollments, primary_key: :canvas_section_id, foreign_key: :canvas_section_id
|
9
|
+
|
10
|
+
api_syncable({
|
11
|
+
sis_id: :sis_id,
|
12
|
+
canvas_course_id: :canvas_course_id,
|
13
|
+
canvas_nonxlist_course_id: :canvas_nonxlist_course_id,
|
14
|
+
name: :name,
|
15
|
+
status: :status,
|
16
|
+
start_date: :start_at,
|
17
|
+
end_date: :end_at,
|
18
|
+
}, -> (api) { api.section(canvas_section_id) })
|
7
19
|
end
|
@@ -1,8 +1,19 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Submission < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_submission_id, uniqueness: true, presence: true
|
5
7
|
belongs_to :assignment, primary_key: :canvas_assignment_id, foreign_key: :canvas_assignment_id, optional: true
|
6
8
|
belongs_to :user, primary_key: :canvas_user_id, foreign_key: :canvas_user_id, optional: true
|
7
9
|
belongs_to :course, primary_key: :canvas_course_id, foreign_key: :canvas_course_id, optional: true
|
10
|
+
|
11
|
+
api_syncable({
|
12
|
+
submitted_at: :submitted_at,
|
13
|
+
graded_at: :graded_at,
|
14
|
+
score: :score,
|
15
|
+
points_possible: :points_possible,
|
16
|
+
excused: :excused,
|
17
|
+
workflow_state: :workflow_state,
|
18
|
+
}, -> (api) { api.user_course_assignment_submission(assignment.context.canvas_course_id, canvas_assignment_id, canvas_user_id) })
|
8
19
|
end
|
@@ -1,9 +1,22 @@
|
|
1
1
|
<%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class User < ApplicationRecord
|
4
|
+
include CanvasSync::ApiSyncable
|
5
|
+
|
4
6
|
validates :canvas_user_id, uniqueness: true, presence: true
|
5
7
|
has_many :enrollments, primary_key: :canvas_user_id, foreign_key: :canvas_user_id
|
6
8
|
has_many :admins, foreign_key: :canvas_user_id, primary_key: :canvas_user_id
|
7
9
|
has_many :admin_roles, through: :admins, source: :role
|
8
10
|
has_many :submissions, primary_key: :canvas_user_id, foreign_key: :canvas_user_id
|
11
|
+
|
12
|
+
api_syncable({
|
13
|
+
sis_id: :sis_id,
|
14
|
+
email: :email,
|
15
|
+
login_id: :login_id,
|
16
|
+
full_name: :name,
|
17
|
+
sortable_name: :sortable_name,
|
18
|
+
first_name: :first_name,
|
19
|
+
last_name: :last_name,
|
20
|
+
status: :status,
|
21
|
+
}, -> (api) { api.user_detail(canvas_user_id) })
|
9
22
|
end
|
data/lib/canvas_sync/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: canvas_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nate Collings
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -320,6 +320,7 @@ files:
|
|
320
320
|
- db/migrate/20170915210836_create_canvas_sync_job_log.rb
|
321
321
|
- db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb
|
322
322
|
- lib/canvas_sync.rb
|
323
|
+
- lib/canvas_sync/api_syncable.rb
|
323
324
|
- lib/canvas_sync/config.rb
|
324
325
|
- lib/canvas_sync/engine.rb
|
325
326
|
- lib/canvas_sync/generators/install_generator.rb
|