canvas_sync 0.10.3 → 0.11.1

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 (66) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +57 -2
  3. data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +13 -0
  4. data/lib/canvas_sync.rb +43 -8
  5. data/lib/canvas_sync/api_syncable.rb +8 -0
  6. data/lib/canvas_sync/generators/templates/migrations/create_admins.rb +7 -4
  7. data/lib/canvas_sync/generators/templates/migrations/create_roles.rb +3 -4
  8. data/lib/canvas_sync/generators/templates/models/account.rb +2 -0
  9. data/lib/canvas_sync/generators/templates/models/admin.rb +4 -5
  10. data/lib/canvas_sync/generators/templates/models/role.rb +0 -4
  11. data/lib/canvas_sync/generators/templates/models/term.rb +0 -2
  12. data/lib/canvas_sync/importers/bulk_importer.rb +7 -4
  13. data/lib/canvas_sync/job.rb +24 -1
  14. data/lib/canvas_sync/jobs/fork_gather.rb +59 -0
  15. data/lib/canvas_sync/jobs/report_starter.rb +12 -1
  16. data/lib/canvas_sync/jobs/sync_admins_job.rb +9 -5
  17. data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +2 -8
  18. data/lib/canvas_sync/jobs/sync_assignments_job.rb +2 -8
  19. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +2 -8
  20. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +2 -8
  21. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +23 -11
  22. data/lib/canvas_sync/jobs/sync_roles_job.rb +8 -5
  23. data/lib/canvas_sync/jobs/sync_simple_table_job.rb +17 -7
  24. data/lib/canvas_sync/jobs/sync_submissions_job.rb +1 -5
  25. data/lib/canvas_sync/jobs/sync_terms_job.rb +8 -2
  26. data/lib/canvas_sync/jobs/sync_users_job.rb +5 -7
  27. data/lib/canvas_sync/processors/assignment_groups_processor.rb +3 -2
  28. data/lib/canvas_sync/processors/assignments_processor.rb +3 -2
  29. data/lib/canvas_sync/processors/context_module_items_processor.rb +3 -2
  30. data/lib/canvas_sync/processors/context_modules_processor.rb +3 -2
  31. data/lib/canvas_sync/processors/normal_processor.rb +2 -1
  32. data/lib/canvas_sync/processors/provisioning_report_processor.rb +7 -2
  33. data/lib/canvas_sync/processors/submissions_processor.rb +3 -2
  34. data/lib/canvas_sync/version.rb +1 -1
  35. data/spec/canvas_sync/canvas_sync_spec.rb +17 -0
  36. data/spec/canvas_sync/jobs/fork_gather_spec.rb +73 -0
  37. data/spec/canvas_sync/jobs/job_spec.rb +39 -5
  38. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +2 -1
  39. data/spec/canvas_sync/jobs/sync_assignment_groups_job_spec.rb +1 -1
  40. data/spec/canvas_sync/jobs/sync_assignments_job_spec.rb +2 -2
  41. data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +2 -2
  42. data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +2 -2
  43. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +20 -11
  44. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +2 -1
  45. data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +1 -0
  46. data/spec/canvas_sync/jobs/sync_submissions_job_spec.rb +1 -1
  47. data/spec/canvas_sync/jobs/sync_users_job_spec.rb +1 -1
  48. data/spec/canvas_sync/models/admins_spec.rb +3 -5
  49. data/spec/canvas_sync/models/roles_spec.rb +5 -5
  50. data/spec/canvas_sync/models/term_spec.rb +3 -3
  51. data/spec/dummy/app/models/account.rb +2 -0
  52. data/spec/dummy/app/models/admin.rb +4 -5
  53. data/spec/dummy/app/models/role.rb +0 -4
  54. data/spec/dummy/app/models/term.rb +0 -2
  55. data/spec/dummy/db/migrate/{20190702203628_create_roles.rb → 20190927204545_create_roles.rb} +3 -4
  56. data/spec/dummy/db/migrate/{20190702203629_create_admins.rb → 20190927204546_create_admins.rb} +7 -4
  57. data/spec/dummy/db/schema.rb +12 -9
  58. data/spec/dummy/db/test.sqlite3 +0 -0
  59. data/spec/dummy/log/development.log +1248 -0
  60. data/spec/dummy/log/test.log +43258 -0
  61. data/spec/factories/admin_factory.rb +0 -1
  62. data/spec/support/fake_canvas.rb +2 -2
  63. data/spec/support/fixtures/canvas_responses/roles.json +6 -0
  64. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +3 -0
  65. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +4 -0
  66. metadata +25 -12
@@ -12,7 +12,7 @@ module CanvasSync
12
12
  # @return [nil]
13
13
  def perform(job_chain, report_name, report_params, processor, options, allow_redownloads: false)
14
14
  account_id = options[:account_id] || job_chain[:global_options][:account_id] || "self"
15
-
15
+ options[:sync_start_time] = DateTime.now.utc.iso8601
16
16
  report_id = if allow_redownloads
17
17
  get_cached_report(job_chain, account_id, report_name, report_params)
18
18
  else
@@ -28,6 +28,17 @@ module CanvasSync
28
28
  )
29
29
  end
30
30
 
31
+ protected
32
+
33
+ def merge_report_params(job_chain, options={}, params={}, term_scope: true)
34
+ term_scope = job_chain[:global_options][:canvas_term_id] if term_scope == true
35
+ if term_scope.present?
36
+ params[:enrollment_term_id] = term_scope
37
+ end
38
+ params.merge!(options[:report_params]) if options[:report_params].present?
39
+ { parameters: params }
40
+ end
41
+
31
42
  private
32
43
 
33
44
  def get_cached_report(job_chain, account_id, report_name, report_params)
@@ -7,12 +7,16 @@ module CanvasSync
7
7
  # @param job_chain [Hash]
8
8
  # @param options [Hash]
9
9
  def perform(job_chain, _options)
10
- updated_admins = []
11
- CanvasSync.get_canvas_sync_client(job_chain[:global_options]).account_admins("self").all_pages!.each do |admin_params|
12
- admin = Admin.create_or_update(admin_params)
13
- updated_admins.push(admin.id)
10
+ updated_admin_ids = []
11
+ api_client = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
12
+ CanvasSync.sync_scope(Account).find_each do |acc|
13
+ api_client.account_admins(acc.canvas_id).all_pages_each do |admin_params|
14
+ admin_params[:account_id] = acc.canvas_id
15
+ admin = update_or_create_model(Admin, admin_params)
16
+ updated_admin_ids.push(admin.id)
17
+ end
14
18
  end
15
- Admin.where.not(id: updated_admins).delete_all
19
+ Admin.where.not(id: updated_admin_ids).update_all(workflow_state: 'inactive')
16
20
  CanvasSync.invoke_next(job_chain)
17
21
  end
18
22
  end
@@ -8,17 +8,11 @@ module CanvasSync
8
8
  #
9
9
  # @param job_chain [Hash]
10
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
-
11
+ def perform(job_chain, options)
18
12
  super(
19
13
  job_chain,
20
14
  "proserv_assignment_group_export_csv",
21
- report_params,
15
+ merge_report_params(job_chain, options),
22
16
  CanvasSync::Processors::AssignmentGroupsProcessor.to_s,
23
17
  {},
24
18
  )
@@ -8,17 +8,11 @@ module CanvasSync
8
8
  #
9
9
  # @param job_chain [Hash]
10
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
-
11
+ def perform(job_chain, options)
18
12
  super(
19
13
  job_chain,
20
14
  "proserv_assignment_export_csv",
21
- report_params,
15
+ merge_report_params(job_chain, options),
22
16
  CanvasSync::Processors::AssignmentsProcessor.to_s,
23
17
  {},
24
18
  )
@@ -8,17 +8,11 @@ module CanvasSync
8
8
  #
9
9
  # @param job_chain [Hash]
10
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
-
11
+ def perform(job_chain, options)
18
12
  super(
19
13
  job_chain,
20
14
  "proserv_context_module_items_csv",
21
- report_params,
15
+ merge_report_params(job_chain, options),
22
16
  CanvasSync::Processors::ContextModuleItemsProcessor.to_s,
23
17
  {},
24
18
  )
@@ -8,17 +8,11 @@ module CanvasSync
8
8
  #
9
9
  # @param job_chain [Hash]
10
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
-
11
+ def perform(job_chain, options)
18
12
  super(
19
13
  job_chain,
20
14
  "proserv_context_modules_csv",
21
- report_params,
15
+ merge_report_params(job_chain, options),
22
16
  CanvasSync::Processors::ContextModulesProcessor.to_s,
23
17
  {},
24
18
  )
@@ -8,13 +8,23 @@ module CanvasSync
8
8
  # models to sync.
9
9
  def perform(job_chain, options)
10
10
  if options[:term_scope]
11
- Term.send(options[:term_scope]).find_each do |term|
12
- # Deep copy the job_chain so each report gets the correct term id passed into
13
- # its options with no side effects
14
- term_id = get_term_id(term)
15
- duped_job_chain = Marshal.load(Marshal.dump(job_chain))
16
- duped_job_chain[:global_options][:canvas_term_id] = term_id
17
- start_report(report_params(options, term_id), duped_job_chain, options)
11
+ sub_reports = CanvasSync.fork(@job_log, job_chain, keys: [:canvas_term_id]) do |job_chain|
12
+ Term.send(options[:term_scope]).find_each.map do |term|
13
+ # Deep copy the job_chain so each report gets the correct term id passed into
14
+ # its options with no side effects
15
+ term_id = get_term_id(term)
16
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
17
+ duped_job_chain[:global_options][:canvas_term_id] = term_id
18
+ {
19
+ job_chain: duped_job_chain,
20
+ params: report_params(options, term_id),
21
+ options: options,
22
+ }
23
+ end
24
+ end
25
+
26
+ sub_reports.each do |r|
27
+ start_report(r[:params], r[:job_chain], r[:options])
18
28
  end
19
29
  else
20
30
  start_report(report_params(options), job_chain, options)
@@ -35,16 +45,18 @@ module CanvasSync
35
45
 
36
46
  def report_params(options, canvas_term_id=nil)
37
47
  params = {
38
- "parameters[include_deleted]" => true
48
+ include_deleted: true,
39
49
  }
40
50
 
41
51
  options[:models].each do |model|
42
- params["parameters[#{model}]"] = true
52
+ params[model] = true
43
53
  end
44
54
 
45
- params["parameters[enrollment_term_id]"] = canvas_term_id if canvas_term_id
55
+ params[:enrollment_term_id] = canvas_term_id if canvas_term_id
56
+
57
+ params.merge!(options[:report_parameters]) if options[:report_parameters].present?
46
58
 
47
- params
59
+ { parameters: params }
48
60
  end
49
61
 
50
62
  def get_term_id(term)
@@ -7,12 +7,15 @@ module CanvasSync
7
7
  # @param job_chain [Hash]
8
8
  # @param options [Hash]
9
9
  def perform(job_chain, _options)
10
- updated_roles = []
11
- CanvasSync.get_canvas_sync_client(job_chain[:global_options]).list_roles("self").all_pages!.each do |role_params|
12
- role = Role.create_or_update(role_params)
13
- updated_roles.push(role.id)
10
+ updated_role_ids = []
11
+ api_client = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
12
+ CanvasSync.sync_scope(Account).find_each do |acc|
13
+ api_client.list_roles(acc.canvas_id, state: %w[active inactive]).all_pages_each do |role_params|
14
+ role = update_or_create_model(Role, role_params)
15
+ updated_role_ids.push(role.id)
16
+ end
14
17
  end
15
- Role.where.not(id: updated_roles).delete_all
18
+ Role.where.not(id: updated_role_ids).update_all(workflow_state: 'inactive')
16
19
  CanvasSync.invoke_next(job_chain)
17
20
  end
18
21
  end
@@ -9,13 +9,23 @@ module CanvasSync
9
9
  # @param options [Hash]
10
10
  def perform(job_chain, options)
11
11
  if options[:term_scope]
12
- Term.send(options[:term_scope]).find_each do |term|
13
- # Deep copy the job_chain so each report gets the correct term id passed into
14
- # its options with no side effects
15
- term_id = get_term_id(term)
16
- duped_job_chain = Marshal.load(Marshal.dump(job_chain))
17
- duped_job_chain[:global_options][:canvas_term_id] = term_id
18
- start_report(report_params(options, term_id), duped_job_chain, options)
12
+ sub_reports = CanvasSync.fork(@job_log, job_chain, keys: [:canvas_term_id]) do |job_chain|
13
+ Term.send(options[:term_scope]).find_each.map do |term|
14
+ # Deep copy the job_chain so each report gets the correct term id passed into
15
+ # its options with no side effects
16
+ term_id = get_term_id(term)
17
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
18
+ duped_job_chain[:global_options][:canvas_term_id] = term_id
19
+ {
20
+ job_chain: duped_job_chain,
21
+ params: report_params(options, term_id),
22
+ options: options,
23
+ }
24
+ end
25
+ end
26
+
27
+ sub_reports.each do |r|
28
+ start_report(r[:params], r[:job_chain], r[:options])
19
29
  end
20
30
  else
21
31
  start_report(report_params(options), job_chain, options)
@@ -9,14 +9,10 @@ module CanvasSync
9
9
  # @param job_chain [Hash]
10
10
  # @param options [Hash]
11
11
  def perform(job_chain, options)
12
- report_params = {}
13
- report_params["parameters[enrollment_term_id]"] = job_chain[:global_options][:canvas_term_id] if job_chain[:global_options][:canvas_term_id].present?
14
- report_params["parameters[include_all]"] = true if options[:include_all]
15
-
16
12
  super(
17
13
  job_chain,
18
14
  "proserv_student_submissions_csv",
19
- report_params,
15
+ merge_report_params(job_chain, options, { include_all: options[:include_all] }, {}),
20
16
  CanvasSync::Processors::SubmissionsProcessor.to_s,
21
17
  {},
22
18
  )
@@ -10,9 +10,15 @@ module CanvasSync
10
10
  def perform(job_chain, _options)
11
11
  CanvasSync.get_canvas_sync_client(job_chain[:global_options]).terms("self").all_pages!.each do |term_params|
12
12
  if job_chain[:global_options][:account_id]
13
- Term.create_or_update(term_params, job_chain[:global_options][:account_id])
13
+ # These branches are primarily to support Legacy apps
14
+ if Term.respond_to?(:create_or_update) && Term.method(:create_or_update).arity.abs == 2
15
+ Term.create_or_update(term_params, job_chain[:global_options][:account_id])
16
+ else
17
+ term_params[:account_id] |= job_chain[:global_options][:account_id]
18
+ update_or_create_model(Term, term_params)
19
+ end
14
20
  else
15
- Term.create_or_update(term_params)
21
+ update_or_create_model(Term, term_params)
16
22
  end
17
23
  end
18
24
 
@@ -1,11 +1,6 @@
1
1
  module CanvasSync
2
2
  module Jobs
3
3
  class SyncUsersJob < ReportStarter
4
- REPORT_PARAMS = {
5
- "parameters[users]" => true,
6
- "parameters[include_deleted]" => true
7
- }.freeze
8
-
9
4
  # Starts a provisioning report for just users.
10
5
  #
11
6
  # Provisioning reports do not scope users by term, so when we are
@@ -14,11 +9,14 @@ module CanvasSync
14
9
  #
15
10
  # @param job_chain [Hash]
16
11
  # @param options [Hash]
17
- def perform(job_chain, _options)
12
+ def perform(job_chain, options)
18
13
  super(
19
14
  job_chain,
20
15
  "proservices_provisioning_csv",
21
- REPORT_PARAMS,
16
+ merge_report_params(job_chain, options, {
17
+ users: true,
18
+ include_deleted: true,
19
+ }, term_scope: false),
22
20
  CanvasSync::Processors::ProvisioningReportProcessor.to_s,
23
21
  { models: ["users"] },
24
22
  )
@@ -8,15 +8,16 @@ module CanvasSync
8
8
  # @param options [Hash]
9
9
  class AssignmentGroupsProcessor < ReportProcessor
10
10
  def self.process(report_file_path, _options, report_id)
11
- new(report_file_path)
11
+ new(report_file_path, _options)
12
12
  end
13
13
 
14
- def initialize(report_file_path)
14
+ def initialize(report_file_path, options)
15
15
  CanvasSync::Importers::BulkImporter.import(
16
16
  report_file_path,
17
17
  mapping[:assignment_groups][:report_columns],
18
18
  AssignmentGroup,
19
19
  mapping[:assignment_groups][:conflict_target].to_sym,
20
+ import_args: options
20
21
  )
21
22
  end
22
23
  end
@@ -8,15 +8,16 @@ module CanvasSync
8
8
  # @param options [Hash]
9
9
  class AssignmentsProcessor < ReportProcessor
10
10
  def self.process(report_file_path, _options, report_id)
11
- new(report_file_path)
11
+ new(report_file_path, _options)
12
12
  end
13
13
 
14
- def initialize(report_file_path)
14
+ def initialize(report_file_path, options)
15
15
  CanvasSync::Importers::BulkImporter.import(
16
16
  report_file_path,
17
17
  mapping[:assignments][:report_columns],
18
18
  Assignment,
19
19
  mapping[:assignments][:conflict_target].to_sym,
20
+ import_args: options
20
21
  )
21
22
  end
22
23
  end
@@ -8,15 +8,16 @@ module CanvasSync
8
8
  # @param options [Hash]
9
9
  class ContextModuleItemsProcessor < ReportProcessor
10
10
  def self.process(report_file_path, _options, report_id)
11
- new(report_file_path)
11
+ new(report_file_path, _options)
12
12
  end
13
13
 
14
- def initialize(report_file_path)
14
+ def initialize(report_file_path, options)
15
15
  CanvasSync::Importers::BulkImporter.import(
16
16
  report_file_path,
17
17
  mapping[:context_module_items][:report_columns],
18
18
  ContextModuleItem,
19
19
  mapping[:context_module_items][:conflict_target].to_sym,
20
+ import_args: options
20
21
  )
21
22
  end
22
23
  end
@@ -8,15 +8,16 @@ module CanvasSync
8
8
  # @param options [Hash]
9
9
  class ContextModulesProcessor < ReportProcessor
10
10
  def self.process(report_file_path, _options, report_id)
11
- new(report_file_path)
11
+ new(report_file_path, _options)
12
12
  end
13
13
 
14
- def initialize(report_file_path)
14
+ def initialize(report_file_path, options)
15
15
  CanvasSync::Importers::BulkImporter.import(
16
16
  report_file_path,
17
17
  mapping[:context_modules][:report_columns],
18
18
  ContextModule,
19
19
  mapping[:context_modules][:conflict_target].to_sym,
20
+ import_args: options
20
21
  )
21
22
  end
22
23
  end
@@ -18,7 +18,8 @@ module CanvasSync
18
18
  report_file_path,
19
19
  mapping[options[:mapping].to_sym][:report_columns],
20
20
  options[:klass].constantize,
21
- conflict_target ? conflict_target.to_sym : conflict_target
21
+ conflict_target ? conflict_target.to_sym : conflict_target,
22
+ import_args: options
22
23
  )
23
24
  end
24
25
  end
@@ -21,7 +21,6 @@ module CanvasSync
21
21
 
22
22
  def initialize(report_file_path, options) # rubocop:disable Metrics/AbcSize
23
23
  @options = options
24
-
25
24
  if options[:models].length == 1
26
25
  run_import(options[:models][0], report_file_path)
27
26
  else
@@ -75,6 +74,7 @@ module CanvasSync
75
74
  mapping[:users][:report_columns],
76
75
  User,
77
76
  mapping[:users][:conflict_target].to_sym,
77
+ import_args: @options
78
78
  )
79
79
  end
80
80
 
@@ -83,7 +83,8 @@ module CanvasSync
83
83
  report_file_path,
84
84
  mapping[:accounts][:report_columns],
85
85
  Account,
86
- mapping[:accounts][:conflict_target].to_sym
86
+ mapping[:accounts][:conflict_target].to_sym,
87
+ import_args: @options
87
88
  )
88
89
  end
89
90
 
@@ -93,6 +94,7 @@ module CanvasSync
93
94
  mapping[:courses][:report_columns],
94
95
  Course,
95
96
  mapping[:courses][:conflict_target].to_sym,
97
+ import_args: @options
96
98
  )
97
99
  end
98
100
 
@@ -102,6 +104,7 @@ module CanvasSync
102
104
  mapping[:enrollments][:report_columns],
103
105
  Enrollment,
104
106
  mapping[:enrollments][:conflict_target].to_sym,
107
+ import_args: @options
105
108
  )
106
109
  end
107
110
 
@@ -111,6 +114,7 @@ module CanvasSync
111
114
  mapping[:sections][:report_columns],
112
115
  Section,
113
116
  mapping[:sections][:conflict_target].to_sym,
117
+ import_args: @options
114
118
  )
115
119
  end
116
120
 
@@ -120,6 +124,7 @@ module CanvasSync
120
124
  mapping[:xlist][:report_columns],
121
125
  Section,
122
126
  mapping[:xlist][:conflict_target].to_sym,
127
+ import_args: @options
123
128
  )
124
129
  end
125
130
  end