canvas_sync 0.17.20 → 0.17.23.beta5
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 +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
|