coalescing_panda 4.0.11 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/coalescing_panda/canvas_batch.js.coffee +7 -5
- data/app/assets/stylesheets/coalescing_panda/application.css.scss +1 -0
- data/app/controllers/coalescing_panda/canvas_batches_controller.rb +0 -10
- data/app/controllers/coalescing_panda/lti_controller.rb +2 -2
- data/app/controllers/coalescing_panda/oauth2_controller.rb +1 -3
- data/app/models/coalescing_panda/assignment.rb +0 -2
- data/app/models/coalescing_panda/canvas_batch.rb +0 -4
- data/app/models/coalescing_panda/course.rb +0 -2
- data/app/models/coalescing_panda/group.rb +2 -3
- data/app/models/coalescing_panda/lti_account.rb +0 -1
- data/app/models/coalescing_panda/user.rb +0 -1
- data/app/models/coalescing_panda/workers/course_miner.rb +58 -141
- data/app/models/concerns/single_table_polymorphic.rb +1 -1
- data/app/views/coalescing_panda/canvas_batches/_canvas_batch.html.haml +10 -22
- data/app/views/coalescing_panda/canvas_batches/_canvas_batch_flash.html.haml +1 -1
- data/config/routes.rb +1 -3
- data/lib/coalescing_panda/controller_helpers.rb +25 -22
- data/lib/coalescing_panda/version.rb +1 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +82 -120
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +229 -589
- data/spec/dummy/log/test.log +60790 -39979
- data/spec/models/coalescing_panda/assignment_spec.rb +0 -1
- data/spec/models/coalescing_panda/course_spec.rb +0 -5
- data/spec/models/coalescing_panda/workers/course_miner_spec.rb +10 -57
- metadata +57 -80
- data/app/assets/javascripts/bootstrap/bootstrap-datepicker.js +0 -1247
- data/app/assets/javascripts/bootstrap/bootstrap.js +0 -6
- data/app/assets/stylesheets/bootstrap/bootstrap-datepicker.css +0 -449
- data/app/assets/stylesheets/bootstrap/bootstrap-overrides.css.scss +0 -61
- data/app/assets/stylesheets/bootstrap/bootstrap-responsive.css +0 -9
- data/app/assets/stylesheets/bootstrap/bootstrap.css.scss +0 -9
- data/app/assets/stylesheets/coalescing_panda/application.css +0 -16
- data/app/assets/stylesheets/coalescing_panda/oauth2.css +0 -0
- data/app/assets/stylesheets/coalescing_panda/progress.css.scss +0 -97
- data/app/models/coalescing_panda/assignment_group.rb +0 -11
- data/app/models/coalescing_panda/group_category.rb +0 -11
- data/db/migrate/20150506183335_create_coalescing_panda_assignment_groups.rb +0 -18
- data/db/migrate/20150506192717_add_assignment_group_id_to_assignments.rb +0 -5
- data/db/migrate/20150526144713_add_account_to_canvas_batches.rb +0 -5
- data/db/migrate/20150602205257_add_option_to_canvas_batches.rb +0 -5
- data/db/migrate/20150708192717_add_group_moderator_to_group_memberships.rb +0 -5
- data/db/migrate/20150709192717_add_leader_id_to_groups.rb +0 -6
- data/db/migrate/20150714205405_create_coalescing_panda_group_categories.rb +0 -16
- data/db/migrate/20160521205405_add_graded_at_to_coalescing_panda_submissions.rb +0 -5
- data/lib/coalescing_panda/bearcat_uri.rb +0 -20
- data/spec/factories/assignment_groups.rb +0 -14
- data/spec/models/coalescing_panda/assignment_group_spec.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 049b4be87e2a94883db4321af5513b3de1292d95
|
4
|
+
data.tar.gz: 18cb2a2a9965e4d80e96e8bb5c2d342c4a1e7911
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a71dcd02c993571e306894b3ba2a19ece4877ef108fb7475811a113d7c1442d38f2dfea109c94fb17dd43f5a63e18300dcfd805fc5411cc302fc3118858ade05
|
7
|
+
data.tar.gz: 38e4fc6d47585987d5611fa1544ca6792d357de01c9c8b18d169cd9fca210635434c4f161410de7c04ef367454d3642e77f16667927550805e4c383e108c3f06
|
@@ -7,7 +7,7 @@ window.CoalescingPanda.CanvasBatchProgress = class CanvasBatchProgress
|
|
7
7
|
batch = $('#batch-progress').data('batch')
|
8
8
|
url = $('#batch-progress').data('url')
|
9
9
|
window.clearPath = $('#batch-progress').data('clear-path')
|
10
|
-
if batch &&
|
10
|
+
if batch && batch.status != "Completed" || batch.status != "Error"
|
11
11
|
window.batchInterval = setInterval(getBatchStatus, 3000, batch.id, url, successCallback, errorCallback)
|
12
12
|
|
13
13
|
getBatchStatus = (id, url, successCallback, errorCallback) ->
|
@@ -17,14 +17,12 @@ window.CoalescingPanda.CanvasBatchProgress = class CanvasBatchProgress
|
|
17
17
|
$('#batch-progress').html(data)
|
18
18
|
setFlashMessages()
|
19
19
|
batch = $('#batch-info').data('batch')
|
20
|
-
if batch
|
20
|
+
if batch.status == "Completed"
|
21
21
|
clearIntervalAndBatch(data, batch)
|
22
22
|
successCallback() if successCallback != undefined
|
23
|
-
else if batch
|
23
|
+
else if batch.status == 'Error'
|
24
24
|
clearIntervalAndBatch(data, batch)
|
25
25
|
errorCallback() if errorCallback != undefined
|
26
|
-
else if batch && batch.status == "Canceled"
|
27
|
-
clearIntervalAndBatch(data, batch)
|
28
26
|
|
29
27
|
error: (message) ->
|
30
28
|
$('#batch-progress').html('Batch status request failed')
|
@@ -47,3 +45,7 @@ window.CoalescingPanda.CanvasBatchProgress = class CanvasBatchProgress
|
|
47
45
|
if window.messages != undefined
|
48
46
|
for key of window.messages
|
49
47
|
$(".batch-message-#{key}").text("#{window.messages[key]}")
|
48
|
+
|
49
|
+
$ ->
|
50
|
+
$("#batch-container").unbind().bind "batchStarted", (event, data) ->
|
51
|
+
new CanvasBatchProgress()
|
@@ -0,0 +1 @@
|
|
1
|
+
@import "launch";
|
@@ -7,16 +7,6 @@ module CoalescingPanda
|
|
7
7
|
render @batch
|
8
8
|
end
|
9
9
|
|
10
|
-
def retrigger
|
11
|
-
@batch = CanvasBatch.find(params[:id])
|
12
|
-
@batch.status = 'Queued'
|
13
|
-
@batch.save
|
14
|
-
worker = CoalescingPanda::Workers::CourseMiner.new(@batch.context, @batch.options)
|
15
|
-
session[:canvas_batch_id] = worker.batch.id
|
16
|
-
worker.start(true)
|
17
|
-
redirect_to :back
|
18
|
-
end
|
19
|
-
|
20
10
|
def clear_batch_session
|
21
11
|
session[:canvas_batch_id] = nil
|
22
12
|
render nothing: true
|
@@ -51,12 +51,12 @@ module CoalescingPanda
|
|
51
51
|
if %w(course account user).include?(name)
|
52
52
|
tail = '_navigation' unless name.include? '_navigation'
|
53
53
|
end
|
54
|
-
(
|
54
|
+
(name+tail).to_sym
|
55
55
|
end
|
56
56
|
|
57
57
|
def ext_params(options)
|
58
58
|
url = options.delete(:url)
|
59
|
-
options[:url] = main_app.send(
|
59
|
+
options[:url] = main_app.send(url+'_url')
|
60
60
|
options
|
61
61
|
end
|
62
62
|
|
@@ -13,9 +13,7 @@ module CoalescingPanda
|
|
13
13
|
client_key = lti_account.oauth2_client_key
|
14
14
|
user_id = params[:user_id]
|
15
15
|
api_domain = params[:api_domain]
|
16
|
-
|
17
|
-
Rails.logger.info "Creating Bearcat client for auth token retrieval pointed to: #{prefix}"
|
18
|
-
client = Bearcat::Client.new(prefix: prefix)
|
16
|
+
client = Bearcat::Client.new(prefix: oauth2_protocol+'://'+api_domain)
|
19
17
|
token = client.retrieve_token(client_id, coalescing_panda.oauth2_redirect_url, client_key, params['code'])
|
20
18
|
CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).first_or_create do |auth|
|
21
19
|
auth.api_token = token
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module CoalescingPanda
|
2
2
|
class Assignment < ActiveRecord::Base
|
3
3
|
belongs_to :course, foreign_key: :coalescing_panda_course_id, class_name: 'CoalescingPanda::Course'
|
4
|
-
belongs_to :assignment_group, foreign_key: :coalescing_panda_assignment_group_id, class_name: 'CoalescingPanda::AssignmentGroup'
|
5
|
-
belongs_to :group_category, foreign_key: :coalescing_panda_group_category_id, class_name: 'CoalescingPanda::GroupCategory'
|
6
4
|
has_many :submissions, foreign_key: :coalescing_panda_assignment_id, class_name: 'CoalescingPanda::Submission', dependent: :destroy
|
7
5
|
|
8
6
|
delegate :account, to: :course
|
@@ -1,10 +1,6 @@
|
|
1
1
|
module CoalescingPanda
|
2
2
|
class CanvasBatch < ActiveRecord::Base
|
3
|
-
serialize :options
|
4
|
-
|
5
|
-
belongs_to :account, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::LtiAccount'
|
6
3
|
belongs_to :context, polymorphic: true
|
7
|
-
|
8
4
|
default_scope { order('created_at DESC') }
|
9
5
|
end
|
10
6
|
end
|
@@ -8,10 +8,8 @@ module CoalescingPanda
|
|
8
8
|
has_many :submissions, through: :assignments, dependent: :destroy
|
9
9
|
has_many :users, through: :sections, source: :users, class_name: 'CoalescingPanda::User'
|
10
10
|
has_many :groups, :as => :context, class_name: 'CoalescingPanda::Group', dependent: :destroy
|
11
|
-
has_many :group_categories, :as => :context, class_name: 'CoalescingPanda::GroupCategory', dependent: :destroy
|
12
11
|
has_many :group_memberships, through: :groups, source: :group_memberships, class_name: 'CoalescingPanda::GroupMembership', dependent: :destroy
|
13
12
|
has_many :canvas_batches, as: :context, dependent: :destroy
|
14
|
-
has_many :assignment_groups, foreign_key: :coalescing_panda_course_id, class_name: 'CoalescingPanda::AssignmentGroup', dependent: :destroy
|
15
13
|
|
16
14
|
validates :coalescing_panda_lti_account_id, presence: true
|
17
15
|
validates :canvas_course_id, presence: true
|
@@ -3,10 +3,9 @@ module CoalescingPanda
|
|
3
3
|
belongs_to :context, :polymorphic => true
|
4
4
|
include SingleTablePolymorphic
|
5
5
|
|
6
|
-
|
7
|
-
belongs_to :group_category, foreign_key: :coalescing_panda_group_category_id, class_name: 'CoalescingPanda::GroupCategory'
|
8
|
-
has_many :group_memberships, foreign_key: :coalescing_panda_group_id, class_name: 'CoalescingPanda::GroupMembership', dependent: :destroy
|
6
|
+
has_many :group_memberships, dependent: :destroy, foreign_key: :coalescing_panda_group_id, class_name: 'CoalescingPanda::GroupMembership', dependent: :destroy
|
9
7
|
validates :group_category_id, presence: true
|
10
8
|
validates :canvas_group_id, presence: true
|
9
|
+
validates :coalescing_panda_user_id, presence: true
|
11
10
|
end
|
12
11
|
end
|
@@ -6,7 +6,6 @@ module CoalescingPanda
|
|
6
6
|
has_many :terms, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::Term'
|
7
7
|
has_many :courses, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::Course'
|
8
8
|
has_many :users, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::User'
|
9
|
-
has_many :canvas_batches, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::CanvasBatch'
|
10
9
|
has_many :sections, through: :courses
|
11
10
|
has_many :enrollments, through: :sections
|
12
11
|
has_many :assignments, through: :courses
|
@@ -3,7 +3,6 @@ module CoalescingPanda
|
|
3
3
|
belongs_to :account, foreign_key: :coalescing_panda_lti_account_id, class_name: 'CoalescingPanda::LtiAccount'
|
4
4
|
has_many :enrollments, foreign_key: :coalescing_panda_user_id, class_name: 'CoalescingPanda::Enrollment', dependent: :destroy
|
5
5
|
has_many :submissions, foreign_key: :coalescing_panda_user_id, class_name: 'CoalescingPanda::Submission', dependent: :destroy
|
6
|
-
has_many :leader_groups, foreign_key: :leader_id, class_name: 'CoalescingPanda::Group'
|
7
6
|
has_many :sections, through: :enrollments
|
8
7
|
has_many :courses, through: :sections
|
9
8
|
|
@@ -1,54 +1,32 @@
|
|
1
1
|
class CoalescingPanda::Workers::CourseMiner
|
2
|
-
SUPPORTED_MODELS = [:sections, :users, :enrollments, :
|
3
|
-
COMPLETED_STATUSES = ['Completed', 'Error']
|
4
|
-
RUNNING_STATUSES = ['Queued', 'Started']
|
2
|
+
SUPPORTED_MODELS = [:sections, :users, :enrollments, :assignments, :submissions, :groups, :group_memberships] #ORDER MATTERS!!
|
5
3
|
|
6
|
-
attr_accessor :options, :account, :course, :batch, :course_section_ids, :enrollment_ids, :assignment_ids, :
|
4
|
+
attr_accessor :options, :account, :course, :batch, :course_section_ids, :enrollment_ids, :assignment_ids, :group_ids, :user_ids
|
7
5
|
|
8
6
|
def initialize(course, options = [])
|
9
7
|
@course = course
|
10
8
|
@account = course.account
|
11
9
|
@options = options
|
12
|
-
@batch =
|
10
|
+
@batch = CoalescingPanda::CanvasBatch.create(context: course, status: "Queued")
|
13
11
|
@course_section_ids = []
|
14
12
|
@enrollment_ids = []
|
15
13
|
@assignment_ids = []
|
16
|
-
@assignment_group_ids = []
|
17
14
|
@group_ids = []
|
18
|
-
@group_membership_ids = []
|
19
15
|
@user_ids = []
|
20
16
|
end
|
21
17
|
|
22
|
-
def setup_batch
|
23
|
-
batch = account.canvas_batches.where(context: course).first
|
24
|
-
if batch.present? and RUNNING_STATUSES.include?(batch.status)
|
25
|
-
batch
|
26
|
-
else
|
27
|
-
batch = account.canvas_batches.create(context: course, status: "Queued")
|
28
|
-
end
|
29
|
-
batch.update_attributes(options: options)
|
30
|
-
batch
|
31
|
-
end
|
32
|
-
|
33
18
|
def api_client
|
34
19
|
@api_client ||= Bearcat::Client.new(prefix: account.settings[:base_url], token: account.settings[:account_admin_api_token])
|
35
20
|
end
|
36
21
|
|
37
|
-
def start
|
38
|
-
unless forced
|
39
|
-
return unless batch.status == 'Queued' # don't start if there is already a running job
|
40
|
-
return unless should_download?
|
41
|
-
end
|
42
|
-
|
22
|
+
def start
|
43
23
|
begin
|
44
24
|
batch.update_attributes(status: "Started", percent_complete: 0)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
process_api_data(model_key.to_sym)
|
25
|
+
SUPPORTED_MODELS.each_with_index do |model_key, index|
|
26
|
+
index += 1
|
27
|
+
process_api_data(model_key.to_sym) if options.include?(model_key)
|
49
28
|
percent_complete = (index/(options.count.nonzero? || 1).to_f * 100).round(1)
|
50
29
|
batch.update_attributes(percent_complete: percent_complete)
|
51
|
-
index += 1
|
52
30
|
end
|
53
31
|
batch.update_attributes(status: "Completed", percent_complete: 100)
|
54
32
|
rescue => e
|
@@ -57,19 +35,8 @@ class CoalescingPanda::Workers::CourseMiner
|
|
57
35
|
end
|
58
36
|
handle_asynchronously :start
|
59
37
|
|
60
|
-
def should_download?
|
61
|
-
return true unless account.settings[:canvas_download_interval].present?
|
62
|
-
return true unless last_completed_batch = account.canvas_batches.where(context: course, status: 'Completed').order('updated_at ASC').first
|
63
|
-
should_download = last_completed_batch.updated_at < Time.zone.now - account.settings[:canvas_download_interval].minutes
|
64
|
-
batch.update_attributes(status: 'Canceled') unless should_download
|
65
|
-
should_download
|
66
|
-
end
|
67
|
-
|
68
38
|
def process_api_data(key)
|
69
39
|
case key
|
70
|
-
when :assignment_groups
|
71
|
-
collection = api_client.list_assignment_groups(course.canvas_course_id).all_pages!
|
72
|
-
sync_assignment_groups(collection)
|
73
40
|
when :sections
|
74
41
|
collection = api_client.course_sections(course.canvas_course_id).all_pages!
|
75
42
|
sync_sections(collection)
|
@@ -85,21 +52,14 @@ class CoalescingPanda::Workers::CourseMiner
|
|
85
52
|
when :submissions
|
86
53
|
collection = []
|
87
54
|
course.assignments.each do |assignment|
|
88
|
-
|
89
|
-
|
90
|
-
collection << submissions
|
91
|
-
end
|
92
|
-
rescue StandardError => e
|
93
|
-
Rails.logger.info("Failed to retrieve submissions for course #{course.id} and assignment #{assignment.id}: #{e}")
|
55
|
+
api_client.get_course_submissions(course.canvas_course_id, assignment.canvas_assignment_id).all_pages!.each do |submissions|
|
56
|
+
collection << submissions
|
94
57
|
end
|
95
58
|
end
|
96
59
|
sync_submissions(collection)
|
97
60
|
when :groups
|
98
61
|
collection = api_client.course_groups(course.canvas_course_id).all_pages!
|
99
62
|
sync_groups(collection)
|
100
|
-
when :group_categories
|
101
|
-
collection = api_client.list_group_categories('courses', course.canvas_course_id).all_pages!
|
102
|
-
sync_group_categories(collection)
|
103
63
|
when :group_memberships
|
104
64
|
collection = []
|
105
65
|
course.groups.each do |group|
|
@@ -113,40 +73,25 @@ class CoalescingPanda::Workers::CourseMiner
|
|
113
73
|
end
|
114
74
|
end
|
115
75
|
|
116
|
-
def sync_assignment_groups(collection)
|
117
|
-
collection.each do |values|
|
118
|
-
begin
|
119
|
-
values['canvas_assignment_group_id'] = values['id'].to_s
|
120
|
-
assignment_group = course.assignment_groups.where(canvas_assignment_group_id: values['canvas_assignment_group_id']).first_or_initialize
|
121
|
-
assignment_group.assign_attributes(standard_attributes(assignment_group, values))
|
122
|
-
assignment_group.save(validate: false)
|
123
|
-
assignment_group_ids << assignment_group.id
|
124
|
-
rescue => e
|
125
|
-
Rails.logger.error "Error syncing assignment group: #{values} Error: #{e}"
|
126
|
-
end
|
127
|
-
end
|
128
|
-
course.assignment_groups.where.not(id: assignment_group_ids).destroy_all
|
129
|
-
end
|
130
|
-
|
131
76
|
def sync_sections(collection)
|
132
|
-
|
133
|
-
|
77
|
+
begin
|
78
|
+
collection.each do |values|
|
134
79
|
values['course_section_id'] = values['id'].to_s
|
135
80
|
section = course.sections.where(canvas_section_id: values['course_section_id']).first_or_initialize
|
136
81
|
section.assign_attributes(standard_attributes(section, values))
|
137
82
|
section.sis_id = values['sis_section_id']
|
138
83
|
section.save(validate: false)
|
139
84
|
course_section_ids << section.id
|
140
|
-
rescue => e
|
141
|
-
Rails.logger.error "Error syncing section: #{values} Error: #{e}"
|
142
85
|
end
|
86
|
+
course.sections.where.not(id: course_section_ids).destroy_all
|
87
|
+
rescue => e
|
88
|
+
Rails.logger.error "Error syncing sections: #{e}"
|
143
89
|
end
|
144
|
-
course.sections.where.not(id: course_section_ids).destroy_all
|
145
90
|
end
|
146
91
|
|
147
92
|
def sync_users(collection)
|
148
|
-
|
149
|
-
|
93
|
+
begin
|
94
|
+
collection.each do |values|
|
150
95
|
values['canvas_user_id'] = values["id"].to_s
|
151
96
|
user = account.users.where(canvas_user_id: values['canvas_user_id']).first_or_initialize
|
152
97
|
user.coalescing_panda_lti_account_id = account.id
|
@@ -154,132 +99,104 @@ class CoalescingPanda::Workers::CourseMiner
|
|
154
99
|
user.sis_id = values['sis_user_id'].to_s
|
155
100
|
user_ids << user.id
|
156
101
|
user.save(validate: false)
|
157
|
-
rescue => e
|
158
|
-
Rails.logger.error "Error syncing user: #{values} Error: #{e}"
|
159
102
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
103
|
+
removed_users = course.users.where.not(id: user_ids)
|
104
|
+
removed_users.each do |user|
|
105
|
+
user.enrollments.each do |enrollment|
|
106
|
+
course.submissions.where(coalescing_panda_user_id: enrollment.user.id).destroy_all
|
107
|
+
enrollment.destroy
|
108
|
+
end
|
166
109
|
end
|
110
|
+
removed_users.destroy_all
|
111
|
+
rescue => e
|
112
|
+
Rails.logger.error "Error syncing users: #{e}"
|
167
113
|
end
|
168
|
-
removed_users.destroy_all
|
169
114
|
end
|
170
115
|
|
171
116
|
def sync_enrollments(collection)
|
172
|
-
|
173
|
-
|
117
|
+
begin
|
118
|
+
collection.each do |values|
|
174
119
|
values['canvas_enrollment_id'] = values['id'].to_s
|
175
|
-
|
176
|
-
enrollment = section.enrollments.where(canvas_enrollment_id: values['canvas_enrollment_id']).first_or_initialize
|
120
|
+
enrollment = course.enrollments.where(canvas_enrollment_id: values['canvas_enrollment_id']).first_or_initialize
|
177
121
|
enrollment.section = course.sections.find_by(canvas_section_id: values['course_section_id'].to_s)
|
178
122
|
enrollment.user = account.users.find_by(canvas_user_id: values['user_id'].to_s)
|
179
123
|
values['workflow_state'] = values["enrollment_state"]
|
180
124
|
values['enrollment_type'] = values['type']
|
181
125
|
enrollment.assign_attributes(standard_attributes(enrollment, values))
|
182
|
-
enrollment.save
|
126
|
+
enrollment.save(validate: false)
|
183
127
|
enrollment_ids << enrollment.id
|
184
|
-
rescue => e
|
185
|
-
Rails.logger.error "Error syncing enrollment: #{values} Error: #{e}"
|
186
128
|
end
|
129
|
+
removed_enrollments = course.enrollments.where.not(id: enrollment_ids)
|
130
|
+
removed_enrollments.each do |enrollment|
|
131
|
+
course.submissions.where(coalescing_panda_user_id: enrollment.user.id).destroy_all
|
132
|
+
end
|
133
|
+
removed_enrollments.destroy_all
|
134
|
+
rescue => e
|
135
|
+
Rails.logger.error "Error syncing enrollments: #{e}"
|
187
136
|
end
|
188
|
-
removed_enrollments = course.enrollments.where.not(id: enrollment_ids)
|
189
|
-
removed_enrollments.each do |enrollment|
|
190
|
-
course.submissions.where(coalescing_panda_user_id: enrollment.user.id).destroy_all
|
191
|
-
end
|
192
|
-
removed_enrollments.destroy_all
|
193
137
|
end
|
194
138
|
|
195
139
|
def sync_assignments(collection)
|
196
|
-
|
197
|
-
|
140
|
+
begin
|
141
|
+
collection.each do |values|
|
198
142
|
values['canvas_assignment_id'] = values['id'].to_s
|
199
143
|
assignment = course.assignments.where(canvas_assignment_id: values['canvas_assignment_id']).first_or_initialize
|
200
|
-
assignment_group = course.assignment_groups.find_by(canvas_assignment_group_id: values['assignment_group_id'].to_s)
|
201
|
-
group_category = course.group_categories.find_by(canvas_group_category_id: values['group_category_id'])
|
202
|
-
assignment.coalescing_panda_assignment_group_id = assignment_group.id if assignment_group
|
203
|
-
assignment.coalescing_panda_group_category_id = group_category.id if group_category
|
204
144
|
assignment.assign_attributes(standard_attributes(assignment, values))
|
205
145
|
assignment.save(validate: false)
|
206
146
|
assignment_ids << assignment.id
|
207
|
-
rescue => e
|
208
|
-
Rails.logger.error "Error syncing assignment: #{values} Error: #{e}"
|
209
147
|
end
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
148
|
+
course.assignments.where.not(id: assignment_ids).each do |assignment|
|
149
|
+
assignment.submissions.destroy_all
|
150
|
+
assignment.destroy!
|
151
|
+
end
|
152
|
+
rescue => e
|
153
|
+
Rails.logger.error "Error syncing assignments: #{e}"
|
214
154
|
end
|
215
155
|
end
|
216
156
|
|
217
157
|
def sync_submissions(collection)
|
218
|
-
|
219
|
-
|
158
|
+
begin
|
159
|
+
collection.each do |values|
|
220
160
|
values['canvas_submission_id'] = values['id'].to_s
|
221
161
|
submission = course.submissions.where(canvas_submission_id: values['canvas_submission_id']).first_or_initialize
|
222
162
|
submission.user = course.users.find_by(canvas_user_id: values['user_id'].to_s)
|
223
163
|
submission.assignment = course.assignments.find_by(canvas_assignment_id: values['assignment_id'].to_s)
|
224
164
|
submission.assign_attributes(standard_attributes(submission, values))
|
225
165
|
submission.save(validate: false)
|
226
|
-
rescue => e
|
227
|
-
Rails.logger.error "Error syncing submission: #{values} Error: #{e}"
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def sync_group_categories(collection)
|
233
|
-
collection.each do |values|
|
234
|
-
begin
|
235
|
-
values['canvas_group_category_id'] = values['id'].to_s
|
236
|
-
values.delete('context_type') #assume only course for now
|
237
|
-
category = course.group_categories.where(canvas_group_category_id: values['canvas_group_category_id']).first_or_initialize
|
238
|
-
category.assign_attributes(standard_attributes(category, values))
|
239
|
-
category.save(validate: false)
|
240
|
-
rescue => e
|
241
|
-
Rails.logger.error "Error syncing group categories: #{values} Error: #{e.message} - #{e.backtrace}"
|
242
166
|
end
|
167
|
+
rescue => e
|
168
|
+
Rails.logger.error "Error syncing submissions: #{e}"
|
243
169
|
end
|
244
170
|
end
|
245
171
|
|
246
172
|
def sync_groups(collection)
|
247
|
-
|
248
|
-
|
173
|
+
begin
|
174
|
+
collection.each do |values|
|
249
175
|
values['canvas_group_id'] = values['id'].to_s
|
250
176
|
group = course.groups.where(canvas_group_id: values['canvas_group_id']).first_or_initialize
|
251
|
-
group_category = course.group_categories.find_by(canvas_group_category_id: values['group_category_id'])
|
252
|
-
if values['leader']
|
253
|
-
group.leader = course.users.find_by(canvas_user_id: values['leader']['id'].to_s)
|
254
|
-
else
|
255
|
-
group.leader = nil
|
256
|
-
end
|
257
|
-
group.coalescing_panda_group_category_id = group_category.id if group_category
|
258
177
|
group.assign_attributes(standard_attributes(group, values))
|
259
178
|
group.save(validate: false)
|
260
179
|
group_ids << group.id
|
261
|
-
rescue => e
|
262
|
-
Rails.logger.error "Error syncing group: #{values} Error: #{e}"
|
263
180
|
end
|
181
|
+
course.groups.where.not(id: group_ids).destroy_all
|
182
|
+
rescue => e
|
183
|
+
Rails.logger.error "Error syncing groups: #{e}"
|
264
184
|
end
|
265
|
-
course.groups.where.not(id: group_ids).destroy_all
|
266
185
|
end
|
267
186
|
|
268
187
|
def sync_group_memberships(collection)
|
269
|
-
|
270
|
-
|
188
|
+
begin
|
189
|
+
collection.each do |values|
|
271
190
|
values['canvas_group_membership_id'] = values['id'].to_s
|
272
191
|
group_membership = course.group_memberships.where(canvas_group_membership_id: values['canvas_group_membership_id']).first_or_initialize
|
273
192
|
group_membership.group = course.groups.find_by(canvas_group_id: values['group_id'].to_s)
|
274
193
|
group_membership.user = course.users.find_by(canvas_user_id: values['user_id'].to_s)
|
275
194
|
group_membership.assign_attributes(standard_attributes(group_membership, values))
|
276
195
|
group_membership.save(validate: false)
|
277
|
-
group_membership_ids << group_membership.id
|
278
|
-
rescue => e
|
279
|
-
Rails.logger.error "Error syncing group memebership: #{values} Error: #{e}"
|
280
196
|
end
|
197
|
+
rescue => e
|
198
|
+
Rails.logger.error "Error syncing group memberships: #{e}"
|
281
199
|
end
|
282
|
-
course.group_memberships.where.not(id: group_membership_ids).destroy_all
|
283
200
|
end
|
284
201
|
|
285
202
|
def standard_attributes(record, attributes)
|
@@ -287,4 +204,4 @@ class CoalescingPanda::Workers::CourseMiner
|
|
287
204
|
new_attributes.delete('id')
|
288
205
|
new_attributes.delete_if { |key, value| !record.attributes.include?(key) }
|
289
206
|
end
|
290
|
-
end
|
207
|
+
end
|
@@ -1,32 +1,20 @@
|
|
1
1
|
#batch-info{data: {batch: @batch.to_json}}
|
2
|
-
-
|
3
|
-
%
|
2
|
+
-if @batch.status == "Queued"
|
3
|
+
%h6.batch-message-queued Data is queued for download from Canvas.
|
4
4
|
|
5
|
-
-
|
5
|
+
-if @batch.status == "Completed"
|
6
6
|
.alert.alert-success
|
7
7
|
%button.close{"data-dismiss" => "alert", :type => "button"} ×
|
8
8
|
%span.batch-message-completed
|
9
9
|
Data successfully downloaded from Canvas.
|
10
10
|
|
11
|
-
-
|
12
|
-
%h6.batch-message-started
|
13
|
-
|
14
|
-
.
|
15
|
-
.bar{:style => "width: #{@batch.percent_complete}%;"}
|
11
|
+
-if @batch.status == "Started"
|
12
|
+
%h6.batch-message-started Downloading data from Canvas
|
13
|
+
.progress.progress-striped.active
|
14
|
+
.bar{:style => "width: #{@batch.percent_complete}%;"}
|
16
15
|
|
17
|
-
|
18
|
-
- if @batch.status == "Error"
|
16
|
+
-if @batch.status == "Error"
|
19
17
|
.alert.alert-block.alert-error
|
20
18
|
%button.close{"data-dismiss" => "alert", :type => "button"} ×
|
21
|
-
%
|
22
|
-
= @batch.message
|
23
|
-
|
24
|
-
- if @batch.status == "Canceled"
|
25
|
-
.alert.alert-block.alert-info
|
26
|
-
%button.close{"data-dismiss" => "alert", :type => "button"} ×
|
27
|
-
%span.batch-message-retrigger
|
28
|
-
Canvas data last downloaded
|
29
|
-
= @batch.updated_at
|
30
|
-
.pull-right
|
31
|
-
= link_to "Retrigger", retrigger_canvas_batch_path(@batch), method: :post, class: "btn btn-default"
|
32
|
-
.clearfix
|
19
|
+
%h6.batch-message-error Data failed to download from Canvas
|
20
|
+
= @batch.message
|
@@ -1,4 +1,4 @@
|
|
1
1
|
- if current_batch.present?
|
2
2
|
- path = CoalescingPanda::Engine.routes.url_helpers.canvas_batch_path(current_batch)
|
3
|
-
-
|
3
|
+
-clear_path = CoalescingPanda::Engine.routes.url_helpers.clear_batch_session_path
|
4
4
|
#batch-progress{data: {batch: current_batch.try(:to_json), url: path, clear_path: clear_path} }
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
CoalescingPanda::Engine.routes.draw do
|
2
|
-
resources :canvas_batches, only: [:show
|
3
|
-
post :retrigger, on: :member
|
4
|
-
end
|
2
|
+
resources :canvas_batches, only: [:show]
|
5
3
|
post '/canvas_batches/clear_batch_session', as: :clear_batch_session
|
6
4
|
|
7
5
|
get '/oauth2/redirect' => 'oauth2#redirect'
|
@@ -7,21 +7,27 @@ module CoalescingPanda
|
|
7
7
|
if lti_authorize!(*roles)
|
8
8
|
user_id = params['user_id']
|
9
9
|
launch_presentation_return_url = @lti_account.settings[:launch_presentation_return_url] || params['launch_presentation_return_url']
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
uri = URI.parse(launch_presentation_return_url)
|
11
|
+
api_domain = uri.host
|
12
|
+
api_domain = "#{api_domain}:#{uri.port.to_s}" if uri.port
|
13
|
+
scheme = uri.scheme + '://'
|
14
|
+
@lti_params = params.to_hash
|
15
|
+
session['user_id'] = user_id
|
16
|
+
session['uri'] = launch_presentation_return_url
|
17
|
+
session['lis_person_sourcedid'] = params['lis_person_sourcedid']
|
18
|
+
session['oauth_consumer_key'] = params['oauth_consumer_key']
|
19
|
+
session['custom_canvas_account_id'] = params['custom_canvas_account_id']
|
13
20
|
|
14
|
-
if token = CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id,
|
15
|
-
@client = Bearcat::Client.new(token: token, prefix:
|
21
|
+
if token = CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).pluck(:api_token).first
|
22
|
+
@client = Bearcat::Client.new(token: token, prefix: scheme+api_domain)
|
16
23
|
elsif @lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key'])
|
17
24
|
client_id = @lti_account.oauth2_client_id
|
18
|
-
client = Bearcat::Client.new(prefix:
|
25
|
+
client = Bearcat::Client.new(prefix: scheme+api_domain)
|
19
26
|
session['state'] = SecureRandom.hex(32)
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
@canvas_url = client.auth_redirect_url(client_id,
|
28
|
+
coalescing_panda.oauth2_redirect_url({key: params['oauth_consumer_key'],
|
29
|
+
user_id: user_id, api_domain: api_domain, state: session['state']}))
|
23
30
|
#delete the added params so the original oauth sig still works
|
24
|
-
@lti_params = params.to_hash
|
25
31
|
@lti_params.delete('action')
|
26
32
|
@lti_params.delete('controller')
|
27
33
|
render 'coalescing_panda/oauth2/oauth2', layout: 'coalescing_panda/application'
|
@@ -29,14 +35,6 @@ module CoalescingPanda
|
|
29
35
|
end
|
30
36
|
end
|
31
37
|
|
32
|
-
def set_session(launch_presentation_return_url)
|
33
|
-
session['user_id'] = params['user_id']
|
34
|
-
session['uri'] = launch_presentation_return_url
|
35
|
-
session['lis_person_sourcedid'] = params['lis_person_sourcedid']
|
36
|
-
session['oauth_consumer_key'] = params['oauth_consumer_key']
|
37
|
-
session['custom_canvas_account_id'] = params['custom_canvas_account_id']
|
38
|
-
end
|
39
|
-
|
40
38
|
def have_session?
|
41
39
|
if params['tool_consumer_instance_guid'] && session['user_id'] != params['user_id']
|
42
40
|
reset_session
|
@@ -45,9 +43,12 @@ module CoalescingPanda
|
|
45
43
|
end
|
46
44
|
|
47
45
|
if (session['user_id'] && session['uri'])
|
48
|
-
uri =
|
49
|
-
|
50
|
-
|
46
|
+
uri = URI.parse(session['uri'])
|
47
|
+
api_domain = uri.host
|
48
|
+
api_domain = "#{api_domain}:#{uri.port.to_s}" if uri.port
|
49
|
+
scheme = uri.scheme + '://'
|
50
|
+
token = CanvasApiAuth.where('user_id = ? and api_domain = ?', session['user_id'], api_domain).pluck(:api_token).first
|
51
|
+
@client = Bearcat::Client.new(token: token, prefix: scheme+api_domain) if token
|
51
52
|
end
|
52
53
|
|
53
54
|
@lti_account = LtiAccount.find_by_key(session['oauth_consumer_key']) if session['oauth_consumer_key']
|
@@ -66,7 +67,9 @@ module CoalescingPanda
|
|
66
67
|
logger.info 'not authorized on roles' if !authorized
|
67
68
|
authorized = authorized && @lti_account.validate_nonce(params['oauth_nonce'], DateTime.strptime(params['oauth_timestamp'], '%s'))
|
68
69
|
logger.info 'not authorized on nonce' if !authorized
|
69
|
-
|
70
|
+
if !authorized
|
71
|
+
render :text => 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized
|
72
|
+
end
|
70
73
|
authorized
|
71
74
|
end
|
72
75
|
|