canvas_sync 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +178 -0
  3. data/Rakefile +23 -0
  4. data/lib/canvas_sync.rb +96 -0
  5. data/lib/canvas_sync/generators/install_generator.rb +54 -0
  6. data/lib/canvas_sync/generators/templates/course.rb +8 -0
  7. data/lib/canvas_sync/generators/templates/create_courses.rb +21 -0
  8. data/lib/canvas_sync/generators/templates/create_enrollments.rb +26 -0
  9. data/lib/canvas_sync/generators/templates/create_sections.rb +20 -0
  10. data/lib/canvas_sync/generators/templates/create_terms.rb +18 -0
  11. data/lib/canvas_sync/generators/templates/create_users.rb +18 -0
  12. data/lib/canvas_sync/generators/templates/enrollment.rb +8 -0
  13. data/lib/canvas_sync/generators/templates/section.rb +7 -0
  14. data/lib/canvas_sync/generators/templates/term.rb +28 -0
  15. data/lib/canvas_sync/generators/templates/user.rb +6 -0
  16. data/lib/canvas_sync/importers/bulk_importer.rb +54 -0
  17. data/lib/canvas_sync/jobs/application_job.rb +25 -0
  18. data/lib/canvas_sync/jobs/report_checker.rb +48 -0
  19. data/lib/canvas_sync/jobs/report_processor_job.rb +36 -0
  20. data/lib/canvas_sync/jobs/report_starter.rb +29 -0
  21. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +58 -0
  22. data/lib/canvas_sync/jobs/sync_terms_job.rb +23 -0
  23. data/lib/canvas_sync/jobs/sync_users_job.rb +32 -0
  24. data/lib/canvas_sync/processors/provisioning_report_processor.rb +118 -0
  25. data/lib/canvas_sync/version.rb +3 -0
  26. data/spec/canvas_sync/canvas_sync_spec.rb +60 -0
  27. data/spec/canvas_sync/jobs/report_checker_spec.rb +62 -0
  28. data/spec/canvas_sync/jobs/report_processor_job_spec.rb +30 -0
  29. data/spec/canvas_sync/jobs/report_starter_spec.rb +27 -0
  30. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +81 -0
  31. data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +18 -0
  32. data/spec/canvas_sync/jobs/sync_users_job_spec.rb +18 -0
  33. data/spec/canvas_sync/models/course_spec.rb +30 -0
  34. data/spec/canvas_sync/models/enrollment_spec.rb +30 -0
  35. data/spec/canvas_sync/models/section_spec.rb +24 -0
  36. data/spec/canvas_sync/models/term_spec.rb +71 -0
  37. data/spec/canvas_sync/models/user_spec.rb +18 -0
  38. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +41 -0
  39. data/spec/dummy/README.rdoc +1 -0
  40. data/spec/dummy/Rakefile +6 -0
  41. data/spec/dummy/app/models/application_record.rb +3 -0
  42. data/spec/dummy/app/models/course.rb +14 -0
  43. data/spec/dummy/app/models/enrollment.rb +14 -0
  44. data/spec/dummy/app/models/section.rb +13 -0
  45. data/spec/dummy/app/models/term.rb +34 -0
  46. data/spec/dummy/app/models/user.rb +12 -0
  47. data/spec/dummy/bin/rails +4 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/spec/dummy/config/application.rb +26 -0
  50. data/spec/dummy/config/boot.rb +5 -0
  51. data/spec/dummy/config/database.yml +25 -0
  52. data/spec/dummy/config/environment.rb +5 -0
  53. data/spec/dummy/config/environments/development.rb +41 -0
  54. data/spec/dummy/config/environments/test.rb +42 -0
  55. data/spec/dummy/config/initializers/assets.rb +11 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/routes.rb +2 -0
  59. data/spec/dummy/config/secrets.yml +22 -0
  60. data/spec/dummy/db/development.sqlite3 +0 -0
  61. data/spec/dummy/db/migrate/20170831220702_create_courses.rb +27 -0
  62. data/spec/dummy/db/migrate/20170831221129_create_users.rb +24 -0
  63. data/spec/dummy/db/migrate/20170905192509_create_enrollments.rb +32 -0
  64. data/spec/dummy/db/migrate/20170906193506_create_terms.rb +24 -0
  65. data/spec/dummy/db/migrate/20170906203438_create_sections.rb +26 -0
  66. data/spec/dummy/db/schema.rb +88 -0
  67. data/spec/dummy/db/test.sqlite3 +0 -0
  68. data/spec/dummy/log/development.log +828 -0
  69. data/spec/dummy/log/test.log +14582 -0
  70. data/spec/factories/course_factory.rb +10 -0
  71. data/spec/factories/enrollment_factory.rb +5 -0
  72. data/spec/factories/section_factory.rb +5 -0
  73. data/spec/factories/term_factory.rb +10 -0
  74. data/spec/factories/user_factory.rb +9 -0
  75. data/spec/spec_helper.rb +46 -0
  76. data/spec/support/fake_canvas.rb +22 -0
  77. data/spec/support/fixtures/canvas_responses/terms.json +64 -0
  78. data/spec/support/fixtures/reports/courses.csv +3 -0
  79. data/spec/support/fixtures/reports/enrollments.csv +3 -0
  80. data/spec/support/fixtures/reports/provisioning_csv +0 -0
  81. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +3 -0
  82. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +4 -0
  83. data/spec/support/fixtures/reports/sections.csv +3 -0
  84. data/spec/support/fixtures/reports/users.csv +4 -0
  85. metadata +423 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d6178b9db26d809e2e35b5c4d38187d2ceb14018
4
+ data.tar.gz: ec84b34894e5b40e27666cdb0946053a355cf422
5
+ SHA512:
6
+ metadata.gz: 4ee7ea65f6c785d05559f52b841483ea2a316509373feedc7c29a35599bfe99d20214ca3a2b2116d1ef503d366a03ec6382d59eb0636e361138cb6378ef6afe1
7
+ data.tar.gz: aaf71eaaf57f0ff80e7c5e92a22f3a3340e57358ab9a1042f1e821ad15dacbd0b401fd93199bbd0c0e9bbb3ff5540c223ce512be15315b5ad71dd43591e92875
@@ -0,0 +1,178 @@
1
+ # CanvasSync
2
+
3
+ This gem is intended to facilitate fast and easy syncing of Canvas data.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'canvas_sync'
11
+ ```
12
+
13
+ Models and migrations can be installed using the following generator:
14
+
15
+ ```
16
+ bin/rails generate canvas_sync:install --models users,terms,courses
17
+ ```
18
+
19
+ Use the `--models` option to specify what models you would like installed. This will add both the model files and their corresponding migrations. If you'd like to install all the models that `CanvasSync` supports then specify `--models all`.
20
+
21
+ For a list of currently supported models, see `CanvasSync::SUPPORTED_MODELS`.
22
+
23
+ Additionally, your Canvas instance must have the "Proserv Provisioning Report" enabled.
24
+
25
+ ## Docs
26
+
27
+ Docs can be generated using [yard](https://yardoc.org/). To view the docs:
28
+
29
+ - Clone this gem's repository
30
+ - `bundle install`
31
+ - `yard server --reload`
32
+
33
+ The yard server will give you a URL you can visit to view the docs.
34
+
35
+ ## Basic Usage
36
+
37
+ Your tool must have an `ActiveJob` compatible job queue adapter configured, such as DelayedJob or Sidekiq. Once that's done and you've used the generator to create your models and migrations you can run the standard provisioning sync:
38
+
39
+ ```ruby
40
+ CanvasSync.provisioning_sync(<canvas base url>, <canvas api token>, <array of models to sync>, <optional term scope>)
41
+ ```
42
+
43
+ Example:
44
+
45
+ ```ruby
46
+ CanvasSync.provisioning_sync("https://pseng.instructure.com", "12345", ['users', 'courses'], :active)
47
+ ```
48
+
49
+ This will kick off a string of jobs to sync your specified models.
50
+
51
+ If you pass in the optional `term_scope` the provisioning reports will be run for only the terms returned by that scope. The scope must be defined on your `Term` model. (A sample one is provided in the generated `Term`.)
52
+
53
+ Imports are inserted in bulk with [https://github.com/zdennis/activerecord-import](activerecord-import) so they should be very fast.
54
+
55
+ ## Advanced Usage
56
+
57
+ This gem also helps with syncing and processing other reports if needed. In order to do so, you must:
58
+
59
+ - Define a `Processor` class that implements a `process` method for handling the results of the report
60
+ - Integrate your reports with the `ReportStarter`
61
+ - Tell the gem what jobs to run
62
+
63
+ ### Processor
64
+
65
+ Your processor class must implement a `process` class method that receives a `report_file_path` and a hash of `options`. (See the `CanvasSync::Processors::ProvisioningReportProcessor` for an example.) The gem handles the work of enqueueing and downloading the report and then passes the file path to your class to process as needed. A simple example might be:
66
+
67
+ ```ruby
68
+ class MyCoolProcessor
69
+ def self.process(report_file_path, options)
70
+ puts "I downloaded a report to #{report_file_path}! Isn't that neat!"
71
+ end
72
+ end
73
+ ```
74
+
75
+ ### Report starter
76
+
77
+ You must implement a job that will enqueue a report starter for your report. (TODO: would be nice to make some sort of builder for this, so you just define the report and its params and then the gem runs it in a pre-defined job.)
78
+
79
+ Let's say we have a custom Canvas report called "my_really_cool_report_csv". First, we would need to create a job class that will enqueue a report starter. To work with the `CanvasSync` interface, your class must accept 4 parameters: `canvas_base_url`, `canvas_api_token`, `job_chain`, and `options`.
80
+
81
+ ```ruby
82
+ class MyReallyCoolReportJob < CanvasSync::Jobs::ReportStarter
83
+ def perform(canvas_base_url, canvas_api_token, job_chain, options)
84
+ super(
85
+ canvas_base_url,
86
+ canvas_api_token,
87
+ job_chain,
88
+ 'my_really_cool_report_csv', # Report name
89
+ { "parameters[param1]" => true }, # Report parameters
90
+ MyCoolProcessor, # Your processor class
91
+ options
92
+ )
93
+ end
94
+ end
95
+ ```
96
+
97
+ You can also see examples in `lib/canvas_sync/jobs/sync_users_job.rb` and `lib/canvas_sync/jobs/sync_provisioning_report.rb`.
98
+
99
+ ### Start the jobs
100
+
101
+ The `CanvasSync.process_jobs` method allows you to pass in a chain of jobs to run. The job chain must be formatted like:
102
+
103
+ ```ruby
104
+ {
105
+ jobs: [
106
+ { job: JobClass, options: {} },
107
+ { job: JobClass2, options: {} }
108
+ ],
109
+ options: {}
110
+ }
111
+ ```
112
+
113
+ Here is an example that runs our new report job first followed by the builtin provisioning job:
114
+
115
+ ```
116
+ job_chain = {
117
+ jobs: [
118
+ { job: MyReallyCoolReportJob, options: {} },
119
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob, options: { models: ['users', 'courses'] } }
120
+ ],
121
+ options: {}
122
+ }
123
+
124
+ CanvasSync.process_jobs("https://pseng.instructure.com", "my-api-token", job_chain)
125
+ ```
126
+
127
+ What if you've got some other job that you want run that doesn't deal with a report? No problem! Just make sure you call `CanvasSync.invoke_next` at the end of your job. Example:
128
+
129
+
130
+ ```
131
+ class SomeRandomJob < CanvasSync::Jobs::ApplicationJob
132
+ def perform(canvas_base_url, canvas_api_token, job_chain, options)
133
+ i_dunno_do_something!
134
+
135
+ CanvasSync.invoke_next(canvas_base_url, canvas_api_token, job_chain)
136
+ end
137
+ end
138
+
139
+ job_chain = {
140
+ jobs: [
141
+ { job: SomeRandomJob, options: {} },
142
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob, options: { models: ['users', 'courses'] } }
143
+ ],
144
+ options: {}
145
+ }
146
+
147
+ CanvasSync.process_jobs("https://pseng.instructure.com", "my-api-token", job_chain)
148
+ ```
149
+
150
+ ### Batching
151
+
152
+ The provisioning report uses the `CanvasSync::Importers::BulkImporter` class to bulk import rows with the activerecord-import gem. It inserts rows in batches of 10,000 by default. This can be customized by setting the `BULK_IMPORTER_BATCH_SIZE` environment variable if needed.
153
+
154
+ ## Upgrading
155
+
156
+ Re-running the generator when there's been a gem change will give you several choices if it detects conflicts between your local files and the updated generators. You can either view a diff or allow the generator to overwrite your local file. In most cases you may just want to add the code from the diff yourself so as not to break any of your customizations.
157
+
158
+ TODO - if you make any breaking changes make sure you include upgrade instructions here.
159
+
160
+ ## Integrating with existing applications
161
+
162
+ In order for this to work properly your database tables will need to have at least the columns defined in this gem. (Adding additional columns is fine.) As such, you may need to run some migrations to rename existing columns or add missing ones. The generator only works well in a situation where that table does not already exist. Take a look at the migration templates in `lib/canvas_sync/generators/templates` to see what you need.
163
+
164
+ ## Development
165
+
166
+ When adding to or updating this gem, make sure you do the following:
167
+
168
+ - If you modify a table that's already in the released version of the gem please write *new migrations* rather than modifying the existing ones. This will allow people to more easily upgrade.
169
+ - Update the yardoc comments where necessary, and confirm the changes by running `yardoc --server`
170
+ - Write specs
171
+ - If you modify the model or migration templates, run `bundle exec rake update_test_schema` to update them in the Rails Dummy application (and commit those changes)
172
+
173
+ ## TODO
174
+
175
+ Currently in a pre-alpha like state.
176
+
177
+ - Add better support for parallel job execution with job dependencies
178
+ - Add some sort of `JobLog` model for tracking parallel execution and job metadata
@@ -0,0 +1,23 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "open3"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task default: :spec
8
+
9
+ desc 'This updates the models and migrations used by the testing Rails Dummy app and should be run whenever those are updated.'
10
+ task :update_test_schema do
11
+ puts "Updating test models and migration files..."
12
+ stream_command("spec/dummy/bin/rails generate canvas_sync:install --models all --force")
13
+ puts "Updating the test database and schema..."
14
+ stream_command("cd spec/dummy; bundle exec rake db:drop; bundle exec rake db:migrate")
15
+ end
16
+
17
+ def stream_command(cmd)
18
+ Open3.popen2e(cmd) do |stdin, stdout_stderr, wait_thr|
19
+ while line = stdout_stderr.gets
20
+ puts line
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,96 @@
1
+ require "bearcat"
2
+ require "canvas_sync/version"
3
+
4
+ require "canvas_sync/jobs/application_job"
5
+ require "canvas_sync/jobs/report_starter"
6
+ require "canvas_sync/jobs/report_checker"
7
+ require "canvas_sync/jobs/report_processor_job"
8
+ require "canvas_sync/jobs/sync_provisioning_report_job"
9
+ require "canvas_sync/jobs/sync_terms_job"
10
+ require "canvas_sync/jobs/sync_users_job"
11
+
12
+ Dir[File.dirname(__FILE__) + "/canvas_sync/processors/*.rb"].each {|file| require file }
13
+ Dir[File.dirname(__FILE__) + "/canvas_sync/importers/*.rb"].each {|file| require file }
14
+ Dir[File.dirname(__FILE__) + "/canvas_sync/generators/*.rb"].each {|file| require file }
15
+
16
+ module CanvasSync
17
+ SUPPORTED_MODELS = %w(users courses terms enrollments sections)
18
+
19
+ # Runs a standard provisioning sync job with no extra report types.
20
+ # Terms will be synced first using the API. If you are syncing users
21
+ # and have also specified a Term scope, Users will by synced first, before
22
+ # every other model (as Users are never scoped to Term).
23
+ #
24
+ # @param canvas_base_url [String] e.g., "https://pseng.instructure.com"
25
+ # @param canvas_api_token [String] A Canvas API token for the specified Canvas instance
26
+ # @param models [Array<String>] A list of models to sync. e.g., ['users', 'courses'].
27
+ # must be one of SUPPORTED_MODELS
28
+ # @param term_scope [Symbol, nil] An optional symbol representing a scope that exists on the Term model.
29
+ # The provisioning report will be run for each of the terms contained in that scope.
30
+ def self.provisioning_sync(canvas_base_url, canvas_api_token, models, term_scope=nil)
31
+ validate_models!(models)
32
+ invoke_next(canvas_base_url, canvas_api_token, default_provisioning_report_chain(models, term_scope))
33
+ end
34
+
35
+ # Runs a chain of ordered jobs
36
+ #
37
+ # See the README for usage and examples
38
+ #
39
+ # @param canvas_base_url [String]
40
+ # @param canvas_api_token [String]
41
+ # @param job_chain [Hash]
42
+ def self.process_jobs(canvas_base_url, canvas_api_token, job_chain)
43
+ invoke_next(canvas_base_url, canvas_api_token, job_chain)
44
+ end
45
+
46
+ # Invokes the next job in a chain of jobs.
47
+ #
48
+ # This should typically be called automatically by the gem where necessary.
49
+ #
50
+ # @param canvas_base_url [String] e.g., "https://pseng.instructure.com"
51
+ # @param canvas_api_token [String] A Canvas API token for the specified Canvas instance
52
+ # @param job_chain [Hash] A chain of jobs to execute
53
+ def self.invoke_next(canvas_base_url, canvas_api_token, job_chain)
54
+ return if job_chain[:jobs].empty?
55
+
56
+ # Make sure all job classes are serialized as strings
57
+ job_chain[:jobs].each { |job| job[:job] = job[:job].to_s }
58
+
59
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
60
+ jobs = duped_job_chain[:jobs]
61
+ next_job = jobs.shift
62
+ next_job_class = next_job[:job].constantize
63
+ next_job_class.perform_later(canvas_base_url, canvas_api_token, duped_job_chain, next_job[:options])
64
+ end
65
+
66
+ # Syncs terms, users if necessary, then the rest of the specified models.
67
+ #
68
+ # @param models [Array<String>]
69
+ # @param term_scope [String]
70
+ # @return [Hash]
71
+ def self.default_provisioning_report_chain(models, term_scope=nil)
72
+ term_scope = term_scope.to_s if term_scope
73
+
74
+ # Always sync Terms first
75
+ jobs = [{ job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} }]
76
+
77
+ if models.include?('users') && term_scope.present?
78
+ # Sync all users first when scoping by term, because users cannot be scoped to term
79
+ jobs.push({ job: CanvasSync::Jobs::SyncUsersJob.to_s, options: {} })
80
+ models = models - ['users']
81
+ end
82
+
83
+ jobs.push({ job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: term_scope, models: models } })
84
+
85
+ { jobs: jobs, options: {} }
86
+ end
87
+
88
+ private
89
+
90
+ def self.validate_models!(models)
91
+ invalid = models - SUPPORTED_MODELS
92
+ if invalid.length > 0
93
+ raise "Invalid model(s) specified: #{invalid.join(', ')}. Only #{SUPPORTED_MODELS.join(', ')} are supported."
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,54 @@
1
+ require "rails/generators"
2
+ require "rails/generators/migration"
3
+
4
+ module CanvasSync
5
+ class InstallGenerator < Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+ source_root File.expand_path('../templates', __FILE__)
8
+ class_option :models, type: :string, required: true
9
+
10
+ def self.next_migration_number(path)
11
+ next_migration_number = current_migration_number(path) + 1
12
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
13
+ end
14
+
15
+ def autogenerated_model_warning
16
+ <<-HERE.strip_heredoc
17
+ #
18
+ # AUTO GENERATED MODEL
19
+ # This model was auto generated by the CanvasSync Gem.
20
+ # You can customize it as needed, but make sure you test
21
+ # any changes you make to the auto generated methods.
22
+ #
23
+ HERE
24
+ end
25
+
26
+ def autogenerated_migration_warning
27
+ <<-HERE.strip_heredoc
28
+ #
29
+ # AUTO GENERATED MIGRATION
30
+ # This migration was auto generated by the CanvasSync Gem.
31
+ # You can add new columns to this table, but removing or
32
+ # re-naming ones created here may break Canvas Syncing.
33
+ #
34
+ HERE
35
+ end
36
+
37
+ # Generates the specified models and migrations. Invoke with:
38
+ #
39
+ # bin/rails generate canvas_sync:install --models users,courses
40
+ #
41
+ # Install all models and migrations with:
42
+ #
43
+ # bin/rails generate canvas_sync:install --models all
44
+ def generate_migrations_and_models
45
+ models = options['models'] == 'all' ? CanvasSync::SUPPORTED_MODELS : options['models'].split(',')
46
+ CanvasSync.validate_models!(models)
47
+
48
+ models.each do |model|
49
+ migration_template "create_#{model}.rb", "db/migrate/create_#{model}.rb"
50
+ template "#{model.singularize}.rb", "app/models/#{model.singularize}.rb"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ <%= autogenerated_model_warning %>
2
+
3
+ class Course < ApplicationRecord
4
+ validates :canvas_course_id, uniqueness: true, presence: true
5
+ belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_term_id, optional: true
6
+ has_many :enrollments, primary_key: :canvas_course_id, foreign_key: :canvas_course_id
7
+ has_many :sections, primary_key: :canvas_course_id, foreign_key: :canvas_course_id
8
+ end
@@ -0,0 +1,21 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateCourses < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :courses do |t|
6
+ t.bigint :canvas_course_id, null: false
7
+ t.string :sis_id
8
+ t.string :short_name
9
+ t.string :long_name
10
+ t.integer :canvas_account_id
11
+ t.integer :canvas_term_id
12
+ t.integer :term_sis_id
13
+ t.datetime :start_date
14
+ t.datetime :end_date
15
+
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :courses, :canvas_course_id, unique: true
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateEnrollments < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :enrollments do |t|
6
+ t.bigint :canvas_enrollment_id, null: false
7
+ t.bigint :canvas_course_id
8
+ t.string :course_sis_id
9
+ t.bigint :canvas_user_id
10
+ t.string :user_sis_id
11
+ t.string :role
12
+ t.integer :role_id
13
+ t.bigint :canvas_section_id
14
+ t.string :section_sis_id
15
+ t.string :status
16
+ t.string :base_role_type
17
+
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :enrollments, :canvas_enrollment_id, unique: true
23
+ add_index :enrollments, :canvas_course_id
24
+ add_index :enrollments, :canvas_user_id
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateSections < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :sections do |t|
6
+ t.bigint :canvas_section_id, null: false
7
+ t.string :sis_id
8
+ t.bigint :canvas_course_id
9
+ t.string :name
10
+ t.string :status
11
+ t.datetime :start_date
12
+ t.datetime :end_date
13
+
14
+ t.timestamps
15
+ end
16
+
17
+ add_index :sections, :canvas_section_id, unique: true
18
+ add_index :sections, :canvas_course_id
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateTerms < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :terms do |t|
6
+ t.integer :canvas_term_id, null: false
7
+ t.string :name
8
+ t.datetime :start_at
9
+ t.datetime :end_at
10
+ t.string :workflow_state
11
+ t.integer :grading_period_group_id
12
+ t.string :sis_id
13
+
14
+ t.timestamps
15
+ end
16
+ add_index :terms, :canvas_term_id, unique: true
17
+ end
18
+ end