canvas_sync 0.12.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/canvas_sync.rb +85 -30
  4. data/lib/canvas_sync/api_syncable.rb +4 -162
  5. data/lib/canvas_sync/class_callback_executor.rb +35 -0
  6. data/lib/canvas_sync/concerns/account/ancestry.rb +60 -0
  7. data/lib/canvas_sync/concerns/api_syncable.rb +189 -0
  8. data/lib/canvas_sync/concerns/legacy_columns.rb +34 -0
  9. data/lib/canvas_sync/generators/templates/migrations/create_group_memberships.rb +18 -0
  10. data/lib/canvas_sync/generators/templates/migrations/create_groups.rb +23 -0
  11. data/lib/canvas_sync/generators/templates/migrations/create_pseudonyms.rb +18 -0
  12. data/lib/canvas_sync/generators/templates/migrations/create_submissions.rb +1 -0
  13. data/lib/canvas_sync/generators/templates/models/account.rb +11 -1
  14. data/lib/canvas_sync/generators/templates/models/admin.rb +2 -1
  15. data/lib/canvas_sync/generators/templates/models/assignment.rb +2 -1
  16. data/lib/canvas_sync/generators/templates/models/assignment_group.rb +2 -1
  17. data/lib/canvas_sync/generators/templates/models/context_module.rb +2 -1
  18. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +2 -1
  19. data/lib/canvas_sync/generators/templates/models/course.rb +4 -2
  20. data/lib/canvas_sync/generators/templates/models/enrollment.rb +2 -1
  21. data/lib/canvas_sync/generators/templates/models/group.rb +19 -0
  22. data/lib/canvas_sync/generators/templates/models/group_membership.rb +17 -0
  23. data/lib/canvas_sync/generators/templates/models/pseudonym.rb +8 -0
  24. data/lib/canvas_sync/generators/templates/models/role.rb +2 -1
  25. data/lib/canvas_sync/generators/templates/models/section.rb +2 -1
  26. data/lib/canvas_sync/generators/templates/models/submission.rb +3 -1
  27. data/lib/canvas_sync/generators/templates/models/term.rb +2 -1
  28. data/lib/canvas_sync/generators/templates/models/user.rb +4 -1
  29. data/lib/canvas_sync/importers/bulk_importer.rb +7 -1
  30. data/lib/canvas_sync/importers/legacy_importer.rb +4 -2
  31. data/lib/canvas_sync/job.rb +3 -1
  32. data/lib/canvas_sync/job_chain.rb +57 -0
  33. data/lib/canvas_sync/jobs/{sync_users_job.rb → sync_accounts_job.rb} +11 -6
  34. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +2 -0
  35. data/lib/canvas_sync/processors/model_mappings.yml +81 -0
  36. data/lib/canvas_sync/processors/provisioning_report_processor.rb +28 -0
  37. data/lib/canvas_sync/record.rb +9 -0
  38. data/lib/canvas_sync/version.rb +1 -1
  39. data/spec/canvas_sync/canvas_sync_spec.rb +19 -16
  40. data/spec/canvas_sync/models/accounts_spec.rb +3 -0
  41. data/spec/canvas_sync/models/course_spec.rb +4 -0
  42. data/spec/canvas_sync/models/group_membership_spec.rb +26 -0
  43. data/spec/canvas_sync/models/group_spec.rb +26 -0
  44. data/spec/canvas_sync/models/user_spec.rb +2 -0
  45. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +20 -0
  46. data/spec/dummy/app/models/account.rb +8 -1
  47. data/spec/dummy/app/models/admin.rb +2 -1
  48. data/spec/dummy/app/models/assignment.rb +2 -1
  49. data/spec/dummy/app/models/assignment_group.rb +2 -1
  50. data/spec/dummy/app/models/context_module.rb +2 -1
  51. data/spec/dummy/app/models/context_module_item.rb +2 -1
  52. data/spec/dummy/app/models/course.rb +4 -2
  53. data/spec/dummy/app/models/enrollment.rb +2 -1
  54. data/spec/dummy/app/models/group.rb +25 -0
  55. data/spec/dummy/app/models/group_membership.rb +23 -0
  56. data/spec/dummy/app/models/role.rb +2 -1
  57. data/spec/dummy/app/models/section.rb +2 -1
  58. data/spec/dummy/app/models/submission.rb +2 -1
  59. data/spec/dummy/app/models/term.rb +2 -1
  60. data/spec/dummy/app/models/user.rb +3 -1
  61. data/spec/dummy/config/application.rb +12 -1
  62. data/spec/dummy/config/database.yml +11 -11
  63. data/spec/dummy/config/environments/development.rb +3 -3
  64. data/spec/dummy/config/initializers/assets.rb +1 -1
  65. data/spec/dummy/db/migrate/20190702203627_create_submissions.rb +1 -0
  66. data/spec/dummy/db/migrate/20200415171620_create_groups.rb +29 -0
  67. data/spec/dummy/db/migrate/20200416214248_create_group_memberships.rb +24 -0
  68. data/spec/dummy/db/schema.rb +31 -1
  69. data/spec/factories/group_factory.rb +8 -0
  70. data/spec/factories/group_membership_factory.rb +6 -0
  71. data/spec/support/fixtures/reports/group_memberships.csv +3 -0
  72. data/spec/support/fixtures/reports/groups.csv +3 -0
  73. data/spec/support/fixtures/reports/submissions.csv +3 -3
  74. metadata +36 -6
  75. data/spec/canvas_sync/jobs/sync_users_job_spec.rb +0 -15
@@ -8,4 +8,7 @@ RSpec.describe Account, type: :model do
8
8
  it { should validate_uniqueness_of(:canvas_id) }
9
9
  end
10
10
 
11
+ describe 'associations' do
12
+ it { should have_many(:groups) }
13
+ end
11
14
  end
@@ -41,5 +41,9 @@ RSpec.describe Course, type: :model do
41
41
  it do
42
42
  should have_many(:submissions)
43
43
  end
44
+
45
+ it do
46
+ should have_many(:groups)
47
+ end
44
48
  end
45
49
  end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe GroupMembership, type: :model do
4
+ let(:subject) {
5
+ FactoryGirl.create(:group_membership, group: FactoryGirl.create(:group), user: FactoryGirl.create(:user))
6
+ }
7
+
8
+ describe 'validations' do
9
+ it { should validate_presence_of(:canvas_id) }
10
+ it { should validate_uniqueness_of(:canvas_id) }
11
+ end
12
+
13
+ describe 'associations' do
14
+ it do
15
+ should belong_to(:group)
16
+ .with_primary_key(:canvas_id)
17
+ .with_foreign_key(:canvas_group_id)
18
+ end
19
+
20
+ it do
21
+ should belong_to(:user)
22
+ .with_primary_key(:canvas_id)
23
+ .with_foreign_key(:canvas_user_id)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Group, type: :model do
4
+ let(:subject) { FactoryGirl.create(:group) }
5
+
6
+ describe 'validations' do
7
+ it { should validate_presence_of(:canvas_id) }
8
+ it { should validate_uniqueness_of(:canvas_id) }
9
+ end
10
+
11
+ describe 'associations' do
12
+ it { should have_many(:group_memberships) }
13
+
14
+ it do
15
+ should belong_to(:course)
16
+ .with_primary_key(:canvas_id)
17
+ .with_foreign_key(:canvas_course_id)
18
+ end
19
+
20
+ it do
21
+ should belong_to(:account)
22
+ .with_primary_key(:canvas_id)
23
+ .with_foreign_key(:canvas_account_id)
24
+ end
25
+ end
26
+ end
@@ -9,6 +9,8 @@ RSpec.describe User, type: :model do
9
9
  end
10
10
 
11
11
  describe 'associations' do
12
+ it { should have_many(:group_memberships) }
13
+
12
14
  it do
13
15
  should have_many(:enrollments)
14
16
  .with_primary_key(:canvas_id)
@@ -42,6 +42,26 @@ RSpec.describe CanvasSync::Processors::ProvisioningReportProcessor do
42
42
  expect(cross_listed_section.canvas_nonxlist_course_id).to eq 2
43
43
  end
44
44
 
45
+ it 'processes groups' do
46
+ expect {
47
+ subject.process('spec/support/fixtures/reports/groups.csv', { models: ['groups'] }, 1)
48
+ }.to change { Group.count }.by(2)
49
+ group = Group.find_by_name('Group1')
50
+ expect(group.workflow_state).to eq 'available'
51
+ expect(group.canvas_account_id).to eq 1
52
+ expect(group.canvas_id).to eq 50
53
+ end
54
+
55
+ it 'processes group memberships' do
56
+ expect {
57
+ subject.process('spec/support/fixtures/reports/group_memberships.csv', { models: ['group_membership'] }, 1)
58
+ }.to change { GroupMembership.count }.by(2)
59
+ group = GroupMembership.find_by_canvas_id(50)
60
+ expect(group.workflow_state).to eq 'accepted'
61
+ expect(group.canvas_group_id).to eq 1
62
+ expect(group.canvas_user_id).to eq 10
63
+ end
64
+
45
65
  context 'options[:models] is multiple models' do
46
66
  it 'extracts the ZIP and processes each model' do
47
67
  user_count = User.count
@@ -7,11 +7,18 @@
7
7
 
8
8
 
9
9
  class Account < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
12
+ # include CanvasSync::Concerns::Account::Ancestry # Add support for the ancestry Gem
11
13
 
12
14
  validates :canvas_id, uniqueness: true, presence: true
13
15
 
14
16
  has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_account_id
17
+ belongs_to :canvas_parent, class_name: 'Account', optional: true,
18
+ primary_key: :canvas_id, foreign_key: :canvas_parent_account_id
19
+ has_many :sub_accounts, class_name: 'Account',
20
+ primary_key: :canvas_id, foreign_key: :canvas_parent_account_id
21
+ has_many :groups, primary_key: :canvas_id, foreign_key: :canvas_account_id
15
22
 
16
23
  api_syncable({
17
24
  name: :name,
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Admin < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Assignment < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class AssignmentGroup < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
@@ -10,7 +10,8 @@
10
10
  # 1 - Module is a reserved word in Rails and you can't call a model a Module
11
11
  # 2 - Canvas calls them ContextModules
12
12
  class ContextModule < ApplicationRecord
13
- include CanvasSync::ApiSyncable
13
+ include CanvasSync::Record
14
+ include CanvasSync::Concerns::ApiSyncable
14
15
 
15
16
  belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
16
17
  has_many :context_module_items, primary_key: :canvas_id, foreign_key: :canvas_context_module_id
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class ContextModuleItem < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  belongs_to :context_module, primary_key: :canvas_id, foreign_key: :canvas_context_module_id, optional: true
13
14
  belongs_to :content, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_content_id, foreign_type: :canvas_content_type
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Course < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_id, optional: true
@@ -16,7 +17,8 @@ class Course < ApplicationRecord
16
17
  has_many :assignments, as: :context, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
17
18
  has_many :submissions, primary_key: :canvas_id, foreign_key: :canvas_course_id
18
19
  has_many :assignment_groups, primary_key: :canvas_id, foreign_key: :canvas_course_id
19
-
20
+ has_many :groups, primary_key: :canvas_id, foreign_key: :canvas_course_id
21
+
20
22
  api_syncable({
21
23
  sis_id: :sis_course_id,
22
24
  course_code: :course_code,
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Enrollment < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
@@ -0,0 +1,25 @@
1
+ # #
2
+ # AUTO GENERATED MODEL
3
+ # This model was auto generated by the CanvasSync Gem.
4
+ # You can customize it as needed, but make sure you test
5
+ # any changes you make to the auto generated methods.
6
+ #
7
+
8
+
9
+ class Group < ApplicationRecord
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
12
+
13
+ validates :canvas_id, uniqueness: true, presence: true
14
+ belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
15
+ belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
16
+ has_many :group_memberships, primary_key: :canvas_id, foreign_key: :canvas_group_id
17
+
18
+ api_syncable({
19
+ canvas_id: :id,
20
+ name: :name,
21
+ canvas_course_id: :course_id,
22
+ sis_id: :sis_group_id,
23
+ workflow_state: ->(r){ 'available' },
24
+ }, -> (api) { api.group(canvas_id) })
25
+ end
@@ -0,0 +1,23 @@
1
+ # #
2
+ # AUTO GENERATED MODEL
3
+ # This model was auto generated by the CanvasSync Gem.
4
+ # You can customize it as needed, but make sure you test
5
+ # any changes you make to the auto generated methods.
6
+ #
7
+
8
+
9
+ class GroupMembership < ApplicationRecord
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
12
+
13
+ validates :canvas_id, uniqueness: true, presence: true
14
+ belongs_to :group, primary_key: :canvas_id, foreign_key: :canvas_group_id
15
+ belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
16
+
17
+ api_syncable({
18
+ canvas_id: :id,
19
+ canvas_group_id: :group_id,
20
+ canvas_user_id: :user_id,
21
+ workflow_state: :workflow_state,
22
+ }, -> (api) { api.group_membership(canvas_group_id, canvas_id) })
23
+ end
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Role < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  has_many :admins, foreign_key: :canvas_role_id, primary_key: :canvas_id
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Section < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Submission < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  belongs_to :assignment, primary_key: :canvas_id, foreign_key: :canvas_assignment_id, optional: true
@@ -7,7 +7,8 @@
7
7
 
8
8
 
9
9
  class Term < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
@@ -7,13 +7,15 @@
7
7
 
8
8
 
9
9
  class User < ApplicationRecord
10
- include CanvasSync::ApiSyncable
10
+ include CanvasSync::Record
11
+ include CanvasSync::Concerns::ApiSyncable
11
12
 
12
13
  validates :canvas_id, uniqueness: true, presence: true
13
14
  has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_user_id
14
15
  has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_user_id
15
16
  has_many :admin_roles, through: :admins, source: :role
16
17
  has_many :submissions, primary_key: :canvas_id, foreign_key: :canvas_user_id
18
+ has_many :group_memberships, primary_key: :canvas_id, foreign_key: :canvas_user_id
17
19
 
18
20
  api_syncable({
19
21
  sis_id: :sis_user_id,
@@ -1,6 +1,17 @@
1
1
  require File.expand_path('../boot', __FILE__)
2
2
 
3
- require 'rails/all'
3
+ require 'rails'
4
+ require 'active_record/railtie'
5
+ # require 'active_storage/engine'
6
+ require 'action_controller/railtie'
7
+ require 'action_view/railtie'
8
+ require 'action_mailer/railtie'
9
+ require 'active_job/railtie'
10
+ # require 'action_cable/engine'
11
+ # require 'action_mailbox/engine'
12
+ # require 'action_text/engine'
13
+ require 'rails/test_unit/railtie'
14
+ # require 'sprockets/railtie'
4
15
 
5
16
  Bundler.require(*Rails.groups)
6
17
  require "canvas_sync"
@@ -1,25 +1,25 @@
1
- # SQLite version 3.x
2
- # gem install sqlite3
3
- #
4
- # Ensure the SQLite 3 gem is defined in your Gemfile
5
- # gem 'sqlite3'
6
- #
1
+
7
2
  default: &default
8
3
  adapter: postgresql
9
- pool: 5
10
- timeout: 5000
4
+ encoding: unicode
5
+ # For details on connection pooling, see Rails configuration guide
6
+ # http://guides.rubyonrails.org/configuring.html#database-pooling
7
+ pool: <%= ENV.fetch("DB_POOL_SIZE", nil) || (ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i + 10) %>
8
+ username: <%= ENV.fetch("DB_USERNAME", "") %>
9
+ password: <%= ENV.fetch("DB_PASSWORD", "") %>
10
+ host: <%= ENV.fetch("DB_ADDRESS", "localhost") %>
11
11
 
12
12
  development:
13
13
  <<: *default
14
14
  database: canvas_sync_development
15
15
 
16
- # Warning: The database defined as "test" will be erased and
17
- # re-generated from your development database when you run "rake".
18
- # Do not set this db to the same as development or production.
19
16
  test:
20
17
  <<: *default
21
18
  database: canvas_sync_test
22
19
 
23
20
  production:
24
21
  <<: *default
22
+ host: <%= ENV.fetch('DB_ADDRESS', 'localhost') %>
25
23
  database: canvas_sync_production
24
+ username: <%= ENV.fetch("DB_USERNAME", "canvas_sync_specs_postgres_user") %>
25
+ password: <%= ENV.fetch("DB_PASSWORD", 'canvas_sync_specs_postgres_password') %>
@@ -25,16 +25,16 @@ Rails.application.configure do
25
25
  # Debug mode disables concatenation and preprocessing of assets.
26
26
  # This option may cause significant delays in view rendering with a large
27
27
  # number of complex assets.
28
- config.assets.debug = true
28
+ # config.assets.debug = true
29
29
 
30
30
  # Asset digests allow you to set far-future HTTP expiration dates on all assets,
31
31
  # yet still be able to expire them through the digest params.
32
- config.assets.digest = true
32
+ # config.assets.digest = true
33
33
 
34
34
  # Adds additional error checking when serving assets at runtime.
35
35
  # Checks for improperly declared sprockets dependencies.
36
36
  # Raises helpful error messages.
37
- config.assets.raise_runtime_errors = true
37
+ # config.assets.raise_runtime_errors = true
38
38
 
39
39
  # Raises error for missing translations
40
40
  # config.action_view.raise_on_missing_translations = true
@@ -1,7 +1,7 @@
1
1
  # Be sure to restart your server when you modify this file.
2
2
 
3
3
  # Version of your assets, change this if you want to expire all your assets.
4
- Rails.application.config.assets.version = '1.0'
4
+ # Rails.application.config.assets.version = '1.0'
5
5
 
6
6
  # Add additional assets to the asset load path
7
7
  # Rails.application.config.assets.paths << Emoji.images_path
@@ -14,6 +14,7 @@ class CreateSubmissions < ActiveRecord::Migration[5.1]
14
14
  t.bigint :canvas_assignment_id
15
15
  t.bigint :canvas_user_id
16
16
  t.datetime :submitted_at
17
+ t.datetime :due_at
17
18
  t.datetime :graded_at
18
19
  t.float :score
19
20
  t.float :points_possible
@@ -0,0 +1,29 @@
1
+ # #
2
+ # AUTO GENERATED MIGRATION
3
+ # This migration was auto generated by the CanvasSync Gem.
4
+ # You can add new columns to this table, but removing or
5
+ # re-naming ones created here may break Canvas Syncing.
6
+ #
7
+
8
+
9
+ class CreateGroups < ActiveRecord::Migration[5.1]
10
+ def change
11
+ create_table :groups do |t|
12
+ t.bigint :canvas_id, null: false
13
+ t.string :sis_id
14
+ t.bigint :canvas_group_category_id
15
+ t.string :group_category_sis_id
16
+ t.bigint :canvas_account_id
17
+ t.bigint :canvas_course_id
18
+ t.string :name
19
+ t.string :workflow_state
20
+ t.bigint :context_id
21
+ t.string :context_type
22
+ t.integer :max_membership
23
+
24
+ t.timestamps
25
+ end
26
+
27
+ add_index :groups, :canvas_id, unique: true
28
+ end
29
+ end