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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f5aacd90f1c55b16e6cd1faac3695c27d102a026
4
- data.tar.gz: 537204f3c66ab4f9a639afbdc23abbabb31e7b52
3
+ metadata.gz: 8f7a480e67851b3de7f12b0df8569aa09aa36431
4
+ data.tar.gz: 51923b6e9707be4f3969160cfa495d1bb75c19c0
5
5
  SHA512:
6
- metadata.gz: 9681ea9b57ea44cfdb2676192515ff7992efcdbf9af34b46619bc3af680774bf8792efbafe29a7f151d522ae18d41e7fe4e23f15e2bb0945d988c22d1dbccc2e
7
- data.tar.gz: 6e8d2dc6ba412e169cdf4a0b204b1893647773b62264e59d655a2dd87bd522e36b620976c9169e9b8590112284b6872b7c77ca00503cccb839f0854f5108e841
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.
@@ -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
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.8.1".freeze
2
+ VERSION = "0.8.2".freeze
3
3
  end
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.1
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-05 00:00:00.000000000 Z
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