canvas_sync 0.17.20 → 0.17.23.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +24 -1
- data/config/initializers/apartment.rb +10 -0
- data/lib/canvas_sync/concerns/sync_mapping.rb +114 -0
- data/lib/canvas_sync/generators/install_generator.rb +1 -0
- data/lib/canvas_sync/generators/templates/migrations/create_grading_period_groups.rb +22 -0
- data/lib/canvas_sync/generators/templates/migrations/create_grading_periods.rb +22 -0
- data/lib/canvas_sync/generators/templates/migrations/create_user_observers.rb +17 -0
- data/lib/canvas_sync/generators/templates/models/grading_period.rb +8 -0
- data/lib/canvas_sync/generators/templates/models/grading_period_group.rb +9 -0
- data/lib/canvas_sync/generators/templates/models/term.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/user_observer.rb +11 -0
- data/lib/canvas_sync/importers/bulk_importer.rb +27 -16
- data/lib/canvas_sync/job_batches/chain_builder.rb +1 -1
- data/lib/canvas_sync/processors/assignment_groups_processor.rb +1 -7
- data/lib/canvas_sync/processors/assignments_processor.rb +1 -7
- data/lib/canvas_sync/processors/context_module_items_processor.rb +1 -7
- data/lib/canvas_sync/processors/context_modules_processor.rb +1 -7
- data/lib/canvas_sync/processors/model_mappings.yml +68 -0
- data/lib/canvas_sync/processors/normal_processor.rb +3 -3
- data/lib/canvas_sync/processors/provisioning_report_processor.rb +21 -63
- data/lib/canvas_sync/processors/report_processor.rb +14 -9
- data/lib/canvas_sync/processors/submissions_processor.rb +1 -7
- data/lib/canvas_sync/record.rb +4 -0
- data/lib/canvas_sync/version.rb +1 -1
- data/lib/canvas_sync.rb +4 -1
- data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +40 -0
- data/spec/dummy/app/models/grading_period.rb +14 -0
- data/spec/dummy/app/models/grading_period_group.rb +15 -0
- data/spec/dummy/app/models/user_observer.rb +17 -0
- data/spec/dummy/db/migrate/20210907233329_create_user_observers.rb +23 -0
- data/spec/dummy/db/migrate/20210907233330_create_grading_periods.rb +28 -0
- data/spec/dummy/db/migrate/20210907233331_create_grading_period_groups.rb +28 -0
- data/spec/dummy/db/schema.rb +42 -1
- data/spec/dummy/log/development.log +1105 -1186
- data/spec/dummy/log/test.log +9781 -42513
- data/spec/support/fixtures/reports/grading_period_groups.csv +2 -0
- data/spec/support/fixtures/reports/grading_periods.csv +3 -0
- data/spec/support/fixtures/reports/user_observers.csv +3 -0
- metadata +34 -25
- data/spec/dummy/db/test.sqlite3 +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff0bdfef2b2ae070b727fefe7b16c20149a82e8237a922102516de8aaee508ae
|
4
|
+
data.tar.gz: da0f508fe25b22b3a9913d81891cf729effaf55d0d0800001079015101be1179
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48c84c48103000a58b3fd5803c650ccc3bf485c39d8cb09467892de0621d2607757542ed9dd275a741040231639d269978f2e5fe23d7830649470fb409bd2fd6
|
7
|
+
data.tar.gz: 36cd14b22dc554ecb7110770e4cb13ce9e0ec7cc1f000ee6b567d65493f07b1c616ec0b2ac3b7234a8ba3bf633f7468a709c10ae771183ae31237ad726476826
|
data/README.md
CHANGED
@@ -190,7 +190,30 @@ Overrides are useful for two scenarios:
|
|
190
190
|
- You have an existing application where the column names do not match up with what CanvasSync expects
|
191
191
|
- You want to sync some other column in the report that CanvasSync is not configured to sync
|
192
192
|
|
193
|
-
|
193
|
+
Mappings can be modified by editing the Model class like such:
|
194
|
+
```ruby
|
195
|
+
class User < ApplicationRecord
|
196
|
+
include CanvasSync::Record
|
197
|
+
|
198
|
+
sync_mapping(reset: false) do # `reset: false` is the default
|
199
|
+
# The mapping can be totally cleared with `reset: true` in the `sync_mapping` call, or like such:
|
200
|
+
reset_links
|
201
|
+
|
202
|
+
# Add a new column:
|
203
|
+
link_column :column_in_report => :column_in_database, type: :datetime
|
204
|
+
|
205
|
+
# If the column name on the report and in the DB are the same, a shorthand can be used:
|
206
|
+
link_column :omit_from_final_grade, type: :datetime
|
207
|
+
|
208
|
+
# If the defaults define a column you don't want synced, you can remove it from the mapping:
|
209
|
+
unlink_column :column_in_database
|
210
|
+
end
|
211
|
+
|
212
|
+
# ...
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
You can also create a file called `canvas_sync_provisioning_mapping.yml` in your Rails `config` directory. However, this approach requires you to re-specify the complete table in order to modify a table. Define the tables and columns you want to override using the following format:
|
194
217
|
|
195
218
|
```ruby
|
196
219
|
users:
|
@@ -5,6 +5,16 @@
|
|
5
5
|
# * https://github.com/influitive/apartment/issues/508
|
6
6
|
|
7
7
|
Rails.application.config.after_initialize do
|
8
|
+
next unless defined?(Apartment)
|
9
|
+
|
10
|
+
# Apartment already solves this issue (and in a better way) in newer versions
|
11
|
+
begin
|
12
|
+
require('apartment/version')
|
13
|
+
next if Gem::Version.new(Apartment::VERSION) >= Gem::Version.new('2.8.1')
|
14
|
+
rescue LoadError
|
15
|
+
end
|
16
|
+
|
17
|
+
|
8
18
|
begin
|
9
19
|
Rails.application.eager_load!
|
10
20
|
ActiveRecord::Base.descendants.each do |model|
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module CanvasSync::Concerns
|
2
|
+
module SyncMapping
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def sync_mapping(key = nil, reset: false, &blk)
|
7
|
+
key ||= Mapping.normalize_model_name(self)
|
8
|
+
key = key.to_s
|
9
|
+
existing_map = get_sync_mapping(key)
|
10
|
+
mapper = Mapping.new(existing_map&.deep_dup || {}.with_indifferent_access)
|
11
|
+
mapper.reset_links if reset
|
12
|
+
mapper.instance_exec(&blk)
|
13
|
+
@sync_mappings[key] = mapper.map_def.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_sync_mapping(key = nil)
|
17
|
+
key ||= Mapping.normalize_model_name(self)
|
18
|
+
key = key.to_s
|
19
|
+
@sync_mappings ||= {}
|
20
|
+
@sync_mappings[key] || superclass.try(:get_sync_mapping, key) || Mapping.default_for(key)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Mapping
|
25
|
+
attr_reader :map_def
|
26
|
+
|
27
|
+
def initialize(map_def, model: nil)
|
28
|
+
@model = model
|
29
|
+
@map_def = map_def
|
30
|
+
@map_def[:conflict_target] ||= []
|
31
|
+
@map_def[:report_columns] ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.normalize_model_name(model)
|
35
|
+
model = model.name unless model.is_a?(String)
|
36
|
+
model.pluralize.underscore
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.default_for(key)
|
40
|
+
default_mappings[key]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.default_mappings
|
44
|
+
@mappings ||= begin
|
45
|
+
maps = {}
|
46
|
+
default_v1_mappings.each do |mname, legacy|
|
47
|
+
m = maps[mname] = {}
|
48
|
+
|
49
|
+
m[:conflict_target] = Array(legacy[:conflict_target]).map(&:to_sym).map do |lct|
|
50
|
+
legacy[:report_columns][lct][:database_column_name]
|
51
|
+
end
|
52
|
+
|
53
|
+
m[:report_columns] = {}
|
54
|
+
legacy[:report_columns].each do |rcol, opts|
|
55
|
+
m[:report_columns][opts[:database_column_name]] = opts.except(:database_column_name).merge!(
|
56
|
+
report_column: rcol,
|
57
|
+
).freeze
|
58
|
+
end
|
59
|
+
end
|
60
|
+
maps.with_indifferent_access.freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.default_v1_mappings
|
65
|
+
@legacy_mappings ||= begin
|
66
|
+
mapping = YAML.load_file(File.join(__dir__, '../processors', "model_mappings.yml")).deep_symbolize_keys!
|
67
|
+
override_filepath = Rails.root.join("config/canvas_sync_provisioning_mapping.yml")
|
68
|
+
|
69
|
+
if File.file?(override_filepath)
|
70
|
+
override = YAML.load_file(override_filepath).deep_symbolize_keys!
|
71
|
+
mapping = mapping.merge(override)
|
72
|
+
end
|
73
|
+
|
74
|
+
mapping.freeze
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def conflict_target(*columns)
|
79
|
+
if columns.count == 0
|
80
|
+
@map_def[:conflict_target]
|
81
|
+
else
|
82
|
+
@map_def[:conflict_target] = columns.flatten.compact
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def reset_links
|
87
|
+
@map_def[:report_columns] = {}.with_indifferent_access
|
88
|
+
end
|
89
|
+
|
90
|
+
def unlink_column(key)
|
91
|
+
@map_def.delete(key)
|
92
|
+
end
|
93
|
+
|
94
|
+
def link_column(m, type: nil, &blk)
|
95
|
+
if m.is_a?(Hash)
|
96
|
+
raise "Hash should have exactly 1 entry" if m && m.count != 1
|
97
|
+
@map_def[:report_columns][m.values[0]] = {
|
98
|
+
report_column: m.keys[0],
|
99
|
+
type: type,
|
100
|
+
transform: blk,
|
101
|
+
}
|
102
|
+
elsif m.is_a?(Symbol)
|
103
|
+
@map_def[:report_columns][m] = {
|
104
|
+
report_column: m,
|
105
|
+
type: type,
|
106
|
+
transform: blk,
|
107
|
+
}
|
108
|
+
else
|
109
|
+
raise "Cannot handle argument of type #{m.class}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreateGradingPeriodGroups < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :grading_period_groups do |t|
|
6
|
+
t.bigint :canvas_id, null: false
|
7
|
+
t.bigint :canvas_course_id
|
8
|
+
t.bigint :canvas_account_id
|
9
|
+
t.string :title
|
10
|
+
t.boolean :weighted
|
11
|
+
t.boolean :display_totals_for_all_grading_periods
|
12
|
+
|
13
|
+
t.string :workflow_state
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
|
18
|
+
add_index :grading_period_groups, :canvas_id, unique: true
|
19
|
+
add_index :grading_period_groups, :canvas_course_id
|
20
|
+
add_index :grading_period_groups, :canvas_account_id
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreateGradingPeriods < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :grading_periods do |t|
|
6
|
+
t.bigint :canvas_id, null: false
|
7
|
+
t.string :title
|
8
|
+
t.float :weight
|
9
|
+
t.datetime :start_date
|
10
|
+
t.datetime :end_date
|
11
|
+
t.datetime :close_date
|
12
|
+
t.bigint :canvas_grading_period_group_id
|
13
|
+
|
14
|
+
t.string :workflow_state
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
|
19
|
+
add_index :grading_periods, :canvas_id, unique: true
|
20
|
+
add_index :grading_periods, :canvas_grading_period_group_id
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreateUserObservers < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :user_observers do |t|
|
6
|
+
t.bigint :observing_user_id
|
7
|
+
t.bigint :observed_user_id
|
8
|
+
t.string :workflow_state
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :user_observers, [:observed_user_id, :observing_user_id], unique: true
|
14
|
+
add_index :user_observers, :observing_user_id
|
15
|
+
add_index :user_observers, :observed_user_id
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class GradingPeriod < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
|
6
|
+
validates :canvas_id, uniqueness: true, presence: true
|
7
|
+
belongs_to :grading_period_group, primary_key: :canvas_id, foreign_key: :canvas_grading_period_group_id, optional: true
|
8
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class GradingPeriodGroup < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
|
6
|
+
validates :canvas_id, uniqueness: true, presence: true
|
7
|
+
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
8
|
+
belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
|
9
|
+
end
|
@@ -6,6 +6,7 @@ class Term < ApplicationRecord
|
|
6
6
|
|
7
7
|
validates :canvas_id, uniqueness: true, presence: true
|
8
8
|
has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
|
9
|
+
belongs_to :grading_period_group, primary_key: :canvas_id, foreign_key: :grading_period_group_id, optional: true
|
9
10
|
|
10
11
|
api_syncable({
|
11
12
|
canvas_id: :id,
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class UserObserver < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
6
|
+
|
7
|
+
validates :canvas_id, uniqueness: true, presence: true
|
8
|
+
|
9
|
+
belongs_to :observing_user, primary_key: :canvas_id, foreign_key: :observing_user_id, class_name: 'User', optional: true
|
10
|
+
belongs_to :observed_user, primary_key: :canvas_id, foreign_key: :observed_user_id, class_name: 'User', optional: true
|
11
|
+
end
|
@@ -24,30 +24,41 @@ module CanvasSync
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.perform_in_batches(report_file_path, mapping, klass, conflict_target, import_args: {})
|
27
|
-
csv_column_names = mapping.
|
28
|
-
database_column_names = mapping.
|
29
|
-
|
27
|
+
csv_column_names = mapping.values.map { |value| value[:report_column].to_s }
|
28
|
+
database_column_names = mapping.keys
|
29
|
+
|
30
|
+
conflict_target = Array(conflict_target).map(&:to_s)
|
31
|
+
conflict_target_indices = conflict_target.map{|ct| database_column_names.index(ct) }
|
30
32
|
|
31
33
|
row_ids = {}
|
32
34
|
batcher = CanvasSync::BatchProcessor.new(of: batch_size) do |batch|
|
33
35
|
row_ids = {}
|
34
|
-
perform_import(klass, database_column_names, batch,
|
36
|
+
perform_import(klass, database_column_names, batch, conflict_target, import_args)
|
35
37
|
end
|
36
38
|
|
37
39
|
row_buffer_out = ->(row) {
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
formatted_row = mapping.map do |db_col, col_def|
|
41
|
+
value = nil
|
42
|
+
value = row[col_def[:report_column]] if col_def[:report_column]
|
43
|
+
|
44
|
+
if col_def[:type]
|
45
|
+
if col_def[:type].to_sym == :datetime
|
46
|
+
# TODO: add some timezone config to the mapping.
|
47
|
+
# In cases where the timestamp or date doesn't include a timezone, you should be able to specify one
|
48
|
+
value = DateTime.parse(value).utc rescue nil # rubocop:disable Style/RescueModifier
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
value = col_def[:transform].call(value, row) if col_def[:transform]
|
53
|
+
|
54
|
+
value
|
41
55
|
end
|
42
56
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
else
|
49
|
-
row[column]
|
50
|
-
end
|
57
|
+
if conflict_target.present?
|
58
|
+
key = conflict_target_indices.map{|ct| formatted_row[ct] }
|
59
|
+
next if row_ids[key]
|
60
|
+
|
61
|
+
row_ids[key] = true
|
51
62
|
end
|
52
63
|
|
53
64
|
batcher << formatted_row
|
@@ -79,7 +90,7 @@ module CanvasSync
|
|
79
90
|
condition: condition_sql(klass, columns, import_args[:sync_start_time]),
|
80
91
|
columns: columns
|
81
92
|
}
|
82
|
-
update_conditions[:conflict_target] = conflict_target if conflict_target
|
93
|
+
update_conditions[:conflict_target] = conflict_target if conflict_target.present?
|
83
94
|
|
84
95
|
options = { validate: false, on_duplicate_key_update: update_conditions }.merge(import_args)
|
85
96
|
options.delete(:on_duplicate_key_update) if options.key?(:on_duplicate_key_ignore)
|
@@ -40,7 +40,7 @@ module CanvasSync
|
|
40
40
|
def insert_at(position, new_jobs)
|
41
41
|
chain = self.class.get_chain_parameter(base_job)
|
42
42
|
new_jobs = [new_jobs] unless new_jobs.is_a?(Array)
|
43
|
-
chain.insert(
|
43
|
+
chain.insert(position, *new_jobs)
|
44
44
|
end
|
45
45
|
|
46
46
|
def insert(new_jobs, **kwargs)
|
@@ -12,13 +12,7 @@ module CanvasSync
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(report_file_path, options)
|
15
|
-
|
16
|
-
report_file_path,
|
17
|
-
mapping[:assignment_groups][:report_columns],
|
18
|
-
AssignmentGroup,
|
19
|
-
mapping[:assignment_groups][:conflict_target].to_sym,
|
20
|
-
import_args: options
|
21
|
-
)
|
15
|
+
do_bulk_import(report_file_path, AssignmentGroup, options: options)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|
@@ -12,13 +12,7 @@ module CanvasSync
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(report_file_path, options)
|
15
|
-
|
16
|
-
report_file_path,
|
17
|
-
mapping[:assignments][:report_columns],
|
18
|
-
Assignment,
|
19
|
-
mapping[:assignments][:conflict_target].to_sym,
|
20
|
-
import_args: options
|
21
|
-
)
|
15
|
+
do_bulk_import(report_file_path, Assignment, options: options)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|
@@ -12,13 +12,7 @@ module CanvasSync
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(report_file_path, options)
|
15
|
-
|
16
|
-
report_file_path,
|
17
|
-
mapping[:context_module_items][:report_columns],
|
18
|
-
ContextModuleItem,
|
19
|
-
mapping[:context_module_items][:conflict_target].to_sym,
|
20
|
-
import_args: options
|
21
|
-
)
|
15
|
+
do_bulk_import(report_file_path, ContextModuleItem, options: options)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|
@@ -12,13 +12,7 @@ module CanvasSync
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def initialize(report_file_path, options)
|
15
|
-
|
16
|
-
report_file_path,
|
17
|
-
mapping[:context_modules][:report_columns],
|
18
|
-
ContextModule,
|
19
|
-
mapping[:context_modules][:conflict_target].to_sym,
|
20
|
-
import_args: options
|
21
|
-
)
|
15
|
+
do_bulk_import(report_file_path, ContextModule, options: options)
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|
@@ -407,3 +407,71 @@ group_memberships:
|
|
407
407
|
status:
|
408
408
|
database_column_name: workflow_state
|
409
409
|
type: string
|
410
|
+
|
411
|
+
user_observers:
|
412
|
+
conflict_target:
|
413
|
+
- canvas_observer_id
|
414
|
+
- canvas_student_id
|
415
|
+
report_columns:
|
416
|
+
canvas_observer_id:
|
417
|
+
database_column_name: observing_user_id
|
418
|
+
type: integer
|
419
|
+
canvas_student_id:
|
420
|
+
database_column_name: observed_user_id
|
421
|
+
type: integer
|
422
|
+
status:
|
423
|
+
database_column_name: workflow_state
|
424
|
+
type: string
|
425
|
+
|
426
|
+
grading_periods:
|
427
|
+
conflict_target: grading_period_id
|
428
|
+
report_columns:
|
429
|
+
grading_period_id:
|
430
|
+
database_column_name: canvas_id
|
431
|
+
type: integer
|
432
|
+
title:
|
433
|
+
database_column_name: title
|
434
|
+
type: string
|
435
|
+
weight:
|
436
|
+
database_column_name: weight
|
437
|
+
type: float
|
438
|
+
start_date:
|
439
|
+
database_column_name: start_date
|
440
|
+
type: datetime
|
441
|
+
end_date:
|
442
|
+
database_column_name: end_date
|
443
|
+
type: datetime
|
444
|
+
close_date:
|
445
|
+
database_column_name: close_date
|
446
|
+
type: datetime
|
447
|
+
grading_period_group_id:
|
448
|
+
database_column_name: canvas_grading_period_group_id
|
449
|
+
type: integer
|
450
|
+
status:
|
451
|
+
database_column_name: workflow_state
|
452
|
+
type: string
|
453
|
+
|
454
|
+
grading_period_groups:
|
455
|
+
conflict_target: grading_period_group_id
|
456
|
+
report_columns:
|
457
|
+
grading_period_group_id:
|
458
|
+
database_column_name: canvas_id
|
459
|
+
type: integer
|
460
|
+
canvas_course_id:
|
461
|
+
database_column_name: canvas_course_id
|
462
|
+
type: integer
|
463
|
+
canvas_account_id:
|
464
|
+
database_column_name: canvas_account_id
|
465
|
+
type: integer
|
466
|
+
title:
|
467
|
+
database_column_name: title
|
468
|
+
type: string
|
469
|
+
weighted:
|
470
|
+
database_column_name: weighted
|
471
|
+
type: boolean
|
472
|
+
display_totals_for_all_grading_periods:
|
473
|
+
database_column_name: display_totals_for_all_grading_periods
|
474
|
+
type: boolean
|
475
|
+
status:
|
476
|
+
database_column_name: workflow_state
|
477
|
+
type: string
|