canvas_sync 0.17.0.beta12 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15dd14f4944009bc0b3415acce0230b3c466deb05d6842dc492f878b56aca4f8
4
- data.tar.gz: 7beb66a20e2abbaf5016bf7991606680519d2417fe74e4e65c867ded4fa33e54
3
+ metadata.gz: e870dc1c517c8a91279f114c7f3c46b108555a350a76749ceca14e517b96e723
4
+ data.tar.gz: 9b13b29d052e41e917c91afca7fcf693c038e537639be1f66b1c7d0eed635705
5
5
  SHA512:
6
- metadata.gz: 1adc5820509c6395225375c7cb736c02620f06beb974817c58de7fefefeba026d66e8384bb7f8a802fc3bf6c609bdbcb0ab705c77f055aeac41598be7ccd7c27
7
- data.tar.gz: a3f48270de430e38c42301f918248f17de468d243e83f011896cc3b0e5f28fcf4b5f5a49220446c138c7ee720d59e41ced118d7e193aacde6a644ee57f14c555
6
+ metadata.gz: cff6b6a167ce9eac91487cf9ec546c36544d0156e12b3273c57015cdbf18a279be3018f5221dcc55d90df8c6003a78c5150afe489bd2efec834df4e0700ed327
7
+ data.tar.gz: 2162d8b3a32d82624ddaacee645ad81e5a0c70afb2a40354e284a9091c225cb92a06f83e291aeab3fbba06a85b99d3ea98621c78f4f1b63f43bba1d9258aec64
data/README.md CHANGED
@@ -112,6 +112,11 @@ If you pass a date to `updated_after`, this logic will be disabled unless you ex
112
112
  - `sunday` - A full sync will run every Sunday
113
113
  - `saturday/4` - A full sync will run every fourth Saturday
114
114
 
115
+ #### Multiple Sync Chains
116
+ If your app uses multiple Sync Chains, you may run into issues with the automatic `updated_after` and `full_sync_every` logic.
117
+ You can fix this by using custom logic or by setting the `batch_genre` parameter when creating the Job Chain. Chains will only
118
+ use chains of the same genre when computing `updated_after` and `full_sync_every`.
119
+
115
120
  ### Extensible chain
116
121
  It is sometimes desired to extend or customize the chain of jobs that are run with CanvasSync.
117
122
  This can be achieved with the following pattern:
@@ -1,5 +1,7 @@
1
1
  class AddFullSyncToCanvasSyncSyncBatch < CanvasSync::MiscHelper::MigrationClass
2
2
  def change
3
3
  add_column :canvas_sync_sync_batches, :full_sync, :boolean, default: false
4
+ add_column :canvas_sync_sync_batches, :batch_genre, :string
5
+ add_column :canvas_sync_sync_batches, :batch_bid, :string
4
6
  end
5
7
  end
@@ -42,6 +42,22 @@ module CanvasSync
42
42
  xlist
43
43
  ].freeze
44
44
 
45
+ SUPPORTED_TERM_SCOPE_MODELS = %w[
46
+ assignments
47
+ submissions
48
+ assignment_groups
49
+ context_modules
50
+ context_module_items
51
+ ].freeze
52
+
53
+ DEFAULT_TERM_SCOPE_MODELS = %w[
54
+ assignments
55
+ submissions
56
+ assignment_groups
57
+ context_modules
58
+ context_module_items
59
+ ].freeze
60
+
45
61
  SUPPORTED_LIVE_EVENTS = %w[
46
62
  course
47
63
  enrollment
@@ -109,11 +125,13 @@ module CanvasSync
109
125
  def default_provisioning_report_chain(
110
126
  models,
111
127
  term_scope: nil,
128
+ term_scoped_models: DEFAULT_TERM_SCOPE_MODELS,
112
129
  legacy_support: false,
113
130
  account_id: nil,
114
131
  updated_after: nil,
115
132
  full_sync_every: nil,
116
- options: {},
133
+ batch_genre: nil,
134
+ options: {}
117
135
  ) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
118
136
  return unless models.present?
119
137
  models.map! &:to_s
@@ -164,6 +182,10 @@ module CanvasSync
164
182
  try_add_model_job.call('roles')
165
183
  try_add_model_job.call('admins')
166
184
 
185
+ (SUPPORTED_TERM_SCOPE_MODELS - term_scoped_models).each do |mdl|
186
+ try_add_model_job.call(mdl)
187
+ end
188
+
167
189
  ###############################
168
190
  # Per-term provisioning jobs
169
191
  ###############################
@@ -173,11 +195,9 @@ module CanvasSync
173
195
  current_chain << per_term_chain
174
196
  current_chain = per_term_chain
175
197
 
176
- try_add_model_job.call('assignments')
177
- try_add_model_job.call('submissions')
178
- try_add_model_job.call('assignment_groups')
179
- try_add_model_job.call('context_modules')
180
- try_add_model_job.call('context_module_items')
198
+ term_scoped_models.each do |mdl|
199
+ try_add_model_job.call(mdl)
200
+ end
181
201
 
182
202
  current_chain.insert(
183
203
  generate_provisioning_jobs(models, options, only_split: ['users'])
@@ -191,6 +211,7 @@ module CanvasSync
191
211
  legacy_support: legacy_support,
192
212
  updated_after: updated_after,
193
213
  full_sync_every: full_sync_every,
214
+ batch_genre: batch_genre,
194
215
  }
195
216
  global_options[:account_id] = account_id if account_id.present?
196
217
  global_options.merge!(options[:global]) if options[:global].present?
@@ -64,13 +64,12 @@ module CanvasSync
64
64
  columns = columns.dup
65
65
 
66
66
  update_conditions = {
67
- condition: condition_sql(klass, columns),
67
+ condition: condition_sql(klass, columns, import_args[:sync_start_time]),
68
68
  columns: columns
69
69
  }
70
70
  update_conditions[:conflict_target] = conflict_target if conflict_target
71
71
 
72
72
  options = { validate: false, on_duplicate_key_update: update_conditions }.merge(import_args)
73
-
74
73
  options.delete(:on_duplicate_key_update) if options.key?(:on_duplicate_key_ignore)
75
74
  klass.import(columns, rows, options)
76
75
  end
@@ -85,10 +84,14 @@ module CanvasSync
85
84
  # started_at = Time.now
86
85
  # run_the_users_sync!
87
86
  # changed = User.where("updated_at >= ?", started_at)
88
- def self.condition_sql(klass, columns)
87
+ def self.condition_sql(klass, columns, report_start)
89
88
  columns_str = columns.map { |c| "#{klass.quoted_table_name}.#{c}" }.join(", ")
90
89
  excluded_str = columns.map { |c| "EXCLUDED.#{c}" }.join(", ")
91
- "(#{columns_str}) IS DISTINCT FROM (#{excluded_str})"
90
+ condition_sql = "(#{columns_str}) IS DISTINCT FROM (#{excluded_str})"
91
+ if klass.column_names.include?("updated_at") && report_start
92
+ condition_sql += " AND #{klass.quoted_table_name}.updated_at < '#{report_start}'"
93
+ end
94
+ condition_sql
92
95
  end
93
96
 
94
97
  def self.batch_size
@@ -60,10 +60,12 @@ module CanvasSync
60
60
  raise "Could not find a \"#{relative_to}\" job in the chain" if matching_jobs.count == 0
61
61
  raise "Found multiple \"#{relative_to}\" jobs in the chain" if matching_jobs.count > 1
62
62
 
63
- parent_job, sub_index = matching_jobs[0]
64
- chain = self.class.get_chain_parameter(parent_job)
63
+ relative_job, sub_index = matching_jobs[0]
64
+ parent_job = find_parent_job(relative_job)
65
65
  needed_parent_type = placement == :with ? ConcurrentBatchJob : SerialBatchJob
66
66
 
67
+ chain = self.class.get_chain_parameter(parent_job)
68
+
67
69
  if parent_job[:job] != needed_parent_type
68
70
  old_job = chain[sub_index]
69
71
  parent_job = chain[sub_index] = {
@@ -129,6 +131,24 @@ module CanvasSync
129
131
  end
130
132
  end
131
133
 
134
+ def find_parent_job(job_def)
135
+ iterate_job_tree do |job, path|
136
+ return path[-1] if job == job_def
137
+ end
138
+ nil
139
+ end
140
+
141
+ def iterate_job_tree(root: self.base_job, path: [], &blk)
142
+ blk.call(root, path)
143
+
144
+ if self.class._job_type_definitions[root[:job]]
145
+ sub_jobs = self.class.get_chain_parameter(root)
146
+ sub_jobs.each_with_index do |sub_job, i|
147
+ iterate_job_tree(root: sub_job, path: [*path, root], &blk)
148
+ end
149
+ end
150
+ end
151
+
132
152
  class << self
133
153
  def _job_type_definitions
134
154
  @job_type_definitions ||= {}
@@ -1,9 +1,13 @@
1
1
  module CanvasSync
2
2
  module Jobs
3
3
  class BeginSyncChainJob < CanvasSync::Job
4
+ attr_reader :globals
5
+
4
6
  def perform(chain_definition, globals = {})
7
+ @globals = globals
8
+
5
9
  if !globals[:updated_after].present? || globals[:updated_after] == true
6
- last_batch = SyncBatch.where(status: 'completed').last
10
+ last_batch = SyncBatch.where(status: 'completed', batch_genre: genre).last
7
11
  globals[:full_sync_every] ||= "sunday/2"
8
12
  globals[:updated_after] = last_batch&.started_at&.iso8601
9
13
  end
@@ -15,23 +19,27 @@ module CanvasSync
15
19
  sync_batch = SyncBatch.create!(
16
20
  started_at: DateTime.now,
17
21
  full_sync: globals[:updated_after] == nil,
22
+ batch_genre: genre,
18
23
  status: 'processing',
19
24
  )
20
25
 
21
26
  JobBatches::Batch.new.tap do |b|
22
- b.description = "CanvasSync Root Batch"
27
+ b.description = "CanvasSync Root Batch (SyncBatch##{sync_batch.id})"
23
28
  b.on(:complete, "#{self.class.to_s}.batch_completed", sync_batch_id: sync_batch.id)
24
29
  b.on(:success, "#{self.class.to_s}.batch_completed", sync_batch_id: sync_batch.id)
25
30
  b.context = globals
26
31
  b.jobs do
27
32
  JobBatches::SerialBatchJob.perform_now(chain_definition)
28
33
  end
34
+ sync_batch.update(batch_bid: b.bid)
29
35
  end
30
36
  end
31
37
 
32
38
  def should_full_sync?(opt)
39
+ return true unless last_full_sync.present?
33
40
  return false unless opt.is_a?(String)
34
- case r.strip
41
+
42
+ case opt.strip
35
43
  when %r{^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)(?:/(\d+))?$}
36
44
  m = Regexp.last_match
37
45
  day = m[1]
@@ -52,11 +60,15 @@ module CanvasSync
52
60
  end
53
61
 
54
62
  def last_full_sync_record
55
- @last_full_sync_record ||= SyncBatch.where(status: 'completed', full_sync: true).last
63
+ @last_full_sync_record ||= SyncBatch.where(status: 'completed', full_sync: true, batch_genre: genre).last
56
64
  end
57
65
 
58
66
  def last_full_sync
59
- last_full_sync_record.started_at
67
+ last_full_sync_record&.started_at
68
+ end
69
+
70
+ def genre
71
+ globals[:batch_genre] || "default"
60
72
  end
61
73
 
62
74
  def self.batch_completed(status, options)
@@ -11,6 +11,7 @@ module CanvasSync
11
11
  # @return [nil]
12
12
  def perform(report_name, report_params, processor, options, allow_redownloads: false)
13
13
  account_id = options[:account_id] || batch_context[:account_id] || "self"
14
+ options[:sync_start_time] = DateTime.now.utc.iso8601
14
15
 
15
16
  report_id = start_report(account_id, report_name, report_params)
16
17
  # TODO: Restore report caching support (does nayone actually use it?)
@@ -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
 
@@ -84,6 +84,7 @@ module CanvasSync
84
84
  mapping[:pseudonyms][:report_columns],
85
85
  Pseudonym,
86
86
  mapping[:pseudonyms][:conflict_target].to_sym,
87
+ import_args: @options
87
88
  )
88
89
  end
89
90
 
@@ -92,7 +93,8 @@ module CanvasSync
92
93
  report_file_path,
93
94
  mapping[:accounts][:report_columns],
94
95
  Account,
95
- mapping[:accounts][:conflict_target].to_sym
96
+ mapping[:accounts][:conflict_target].to_sym,
97
+ import_args: @options
96
98
  )
97
99
  end
98
100
 
@@ -102,6 +104,7 @@ module CanvasSync
102
104
  mapping[:courses][:report_columns],
103
105
  Course,
104
106
  mapping[:courses][:conflict_target].to_sym,
107
+ import_args: @options
105
108
  )
106
109
  end
107
110
 
@@ -111,6 +114,7 @@ module CanvasSync
111
114
  mapping[:enrollments][:report_columns],
112
115
  Enrollment,
113
116
  mapping[:enrollments][:conflict_target].to_sym,
117
+ import_args: @options
114
118
  )
115
119
  end
116
120
 
@@ -120,6 +124,7 @@ module CanvasSync
120
124
  mapping[:sections][:report_columns],
121
125
  Section,
122
126
  mapping[:sections][:conflict_target].to_sym,
127
+ import_args: @options
123
128
  )
124
129
  end
125
130
 
@@ -129,6 +134,7 @@ module CanvasSync
129
134
  mapping[:xlist][:report_columns],
130
135
  Section,
131
136
  mapping[:xlist][:conflict_target].to_sym,
137
+ import_args: @options
132
138
  )
133
139
  end
134
140
 
@@ -138,6 +144,7 @@ module CanvasSync
138
144
  mapping[:groups][:report_columns],
139
145
  Group,
140
146
  mapping[:groups][:conflict_target].to_sym,
147
+ import_args: @options
141
148
  )
142
149
  end
143
150
 
@@ -148,6 +155,7 @@ module CanvasSync
148
155
  mapping[:group_memberships][:report_columns],
149
156
  GroupMembership,
150
157
  mapping[:group_memberships][:conflict_target].to_sym,
158
+ import_args: @options
151
159
  )
152
160
  end
153
161
  end
@@ -8,15 +8,16 @@ module CanvasSync
8
8
  # @param options [Hash]
9
9
  class SubmissionsProcessor < 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[:submissions][:report_columns],
18
18
  Submission,
19
19
  mapping[:submissions][:conflict_target].to_sym,
20
+ import_args: options
20
21
  )
21
22
  end
22
23
  end
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.17.0.beta12".freeze
2
+ VERSION = "0.17.1".freeze
3
3
  end
@@ -42,7 +42,7 @@ RSpec.describe CanvasSync do
42
42
  ]
43
43
  }]}
44
44
  ]]}
45
- ], {:legacy_support=>false, :updated_after=>nil, :d=>4}],
45
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil, :d=>4}],
46
46
  })
47
47
  end
48
48
 
@@ -61,7 +61,7 @@ RSpec.describe CanvasSync do
61
61
  ]
62
62
  }]}
63
63
  ]]}
64
- ], {:legacy_support=>false, :updated_after=>nil}]
64
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}]
65
65
  })
66
66
  end
67
67
  end
@@ -80,7 +80,7 @@ RSpec.describe CanvasSync do
80
80
  ]
81
81
  }]}
82
82
  ]]}
83
- ], {:legacy_support=>false, :updated_after=>nil}],
83
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
84
84
  })
85
85
  end
86
86
  end
@@ -100,7 +100,7 @@ RSpec.describe CanvasSync do
100
100
  ]
101
101
  }]}
102
102
  ]]}
103
- ], {:legacy_support=>false, :updated_after=>nil}],
103
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
104
104
  })
105
105
  end
106
106
  end
@@ -120,7 +120,7 @@ RSpec.describe CanvasSync do
120
120
  ]
121
121
  }]}
122
122
  ]]}
123
- ], {:legacy_support=>false, :updated_after=>nil}],
123
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
124
124
  })
125
125
  end
126
126
  end
@@ -140,7 +140,7 @@ RSpec.describe CanvasSync do
140
140
  ]
141
141
  }]}
142
142
  ]]}
143
- ], {:legacy_support=>false, :updated_after=>nil}],
143
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
144
144
  })
145
145
  end
146
146
  end
@@ -160,7 +160,7 @@ RSpec.describe CanvasSync do
160
160
  ]
161
161
  }]}
162
162
  ]]}
163
- ], {:legacy_support=>false, :updated_after=>nil}],
163
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
164
164
  })
165
165
  end
166
166
  end
@@ -181,7 +181,7 @@ RSpec.describe CanvasSync do
181
181
  ]
182
182
  }]}
183
183
  ]]}
184
- ], {:legacy_support=>false, :updated_after=>nil}],
184
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
185
185
  )
186
186
  end
187
187
  end
@@ -202,7 +202,7 @@ RSpec.describe CanvasSync do
202
202
  ]
203
203
  }]}
204
204
  ]]}
205
- ], {:legacy_support=>false, :updated_after=>nil}],
205
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
206
206
  )
207
207
  end
208
208
  end
@@ -223,7 +223,7 @@ RSpec.describe CanvasSync do
223
223
  ]
224
224
  }]}
225
225
  ]]}
226
- ], {:legacy_support=>false, :updated_after=>nil}],
226
+ ], {:legacy_support=>false, :updated_after=>nil, :full_sync_every=>nil, :batch_genre=>nil}],
227
227
  )
228
228
  end
229
229
  end