coalescing_panda 4.0.4 → 4.0.5
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/app/assets/javascripts/coalescing_panda/canvas_batch.js.coffee +5 -7
- data/app/assets/stylesheets/coalescing_panda/progress.css.scss +97 -0
- data/app/controllers/coalescing_panda/canvas_batches_controller.rb +10 -0
- data/app/controllers/coalescing_panda/lti_controller.rb +2 -2
- data/app/controllers/coalescing_panda/oauth2_controller.rb +3 -1
- data/app/models/coalescing_panda/assignment.rb +2 -0
- data/app/models/coalescing_panda/assignment_group.rb +11 -0
- data/app/models/coalescing_panda/canvas_batch.rb +4 -0
- data/app/models/coalescing_panda/course.rb +2 -0
- data/app/models/coalescing_panda/group.rb +3 -2
- data/app/models/coalescing_panda/group_category.rb +11 -0
- data/app/models/coalescing_panda/lti_account.rb +1 -0
- data/app/models/coalescing_panda/user.rb +1 -0
- data/app/models/coalescing_panda/workers/course_miner.rb +132 -56
- data/app/models/concerns/single_table_polymorphic.rb +1 -1
- data/app/views/coalescing_panda/canvas_batches/_canvas_batch.html.haml +22 -10
- data/app/views/coalescing_panda/canvas_batches/_canvas_batch_flash.html.haml +1 -1
- data/config/routes.rb +3 -1
- data/db/migrate/20150506183335_create_coalescing_panda_assignment_groups.rb +18 -0
- data/db/migrate/20150506192717_add_assignment_group_id_to_assignments.rb +5 -0
- data/db/migrate/20150526144713_add_account_to_canvas_batches.rb +5 -0
- data/db/migrate/20150602205257_add_option_to_canvas_batches.rb +5 -0
- data/db/migrate/20150708192717_add_group_moderator_to_group_memberships.rb +5 -0
- data/db/migrate/20150709192717_add_leader_id_to_groups.rb +6 -0
- data/db/migrate/20150714205405_create_coalescing_panda_group_categories.rb +16 -0
- data/lib/coalescing_panda/bearcat_uri.rb +20 -0
- data/lib/coalescing_panda/controller_helpers.rb +22 -25
- data/lib/coalescing_panda/version.rb +1 -1
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +120 -82
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +628 -0
- data/spec/dummy/log/test.log +42958 -0
- data/spec/factories/assignment_groups.rb +14 -0
- data/spec/models/coalescing_panda/assignment_group_spec.rb +32 -0
- data/spec/models/coalescing_panda/assignment_spec.rb +1 -0
- data/spec/models/coalescing_panda/course_spec.rb +5 -0
- data/spec/models/coalescing_panda/workers/course_miner_spec.rb +57 -10
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 426f1e67e2f5b5e67c512ddf08d4218e69195c58
|
4
|
+
data.tar.gz: 1fa762f16649d117921743c0f6d1ad08897a2a71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 008f2c9346cf73643171662a546e5f9749beb2b0215b297b253e3e130ef64b1014b69bd39d1222f711222e68ef712a0a9454d30437b7d7fbe7635f7e38c8f1a0
|
7
|
+
data.tar.gz: b9cf5e7c0907f2e0aff5dca5ead001ab65144977ddd06ab232dee32a5c12441d8f37f06b3767a5c1bd765177b8bc0f6969727282ef77bf2e8a7dc5d767b1d0f7
|
@@ -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 && batch.status != "Completed" || batch.status != "Error"
|
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,12 +17,14 @@ window.CoalescingPanda.CanvasBatchProgress = class CanvasBatchProgress
|
|
17
17
|
$('#batch-progress').html(data)
|
18
18
|
setFlashMessages()
|
19
19
|
batch = $('#batch-info').data('batch')
|
20
|
-
if batch.status == "Completed"
|
20
|
+
if batch && batch.status == "Completed"
|
21
21
|
clearIntervalAndBatch(data, batch)
|
22
22
|
successCallback() if successCallback != undefined
|
23
|
-
else if batch.status == 'Error'
|
23
|
+
else if batch && 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)
|
26
28
|
|
27
29
|
error: (message) ->
|
28
30
|
$('#batch-progress').html('Batch status request failed')
|
@@ -45,7 +47,3 @@ window.CoalescingPanda.CanvasBatchProgress = class CanvasBatchProgress
|
|
45
47
|
if window.messages != undefined
|
46
48
|
for key of window.messages
|
47
49
|
$(".batch-message-#{key}").text("#{window.messages[key]}")
|
48
|
-
|
49
|
-
$ ->
|
50
|
-
$("#batch-container").unbind().bind "batchStarted", (event, data) ->
|
51
|
-
new CanvasBatchProgress()
|
@@ -0,0 +1,97 @@
|
|
1
|
+
.progress {
|
2
|
+
overflow: hidden;
|
3
|
+
height: 20px;
|
4
|
+
margin-bottom: 20px;
|
5
|
+
background-color: #f5f5f5;
|
6
|
+
border-radius: 4px;
|
7
|
+
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
8
|
+
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
9
|
+
}
|
10
|
+
.progress-bar {
|
11
|
+
float: left;
|
12
|
+
width: 0%;
|
13
|
+
height: 100%;
|
14
|
+
font-size: 12px;
|
15
|
+
line-height: 20px;
|
16
|
+
color: #ffffff;
|
17
|
+
text-align: center;
|
18
|
+
background-color: #337ab7;
|
19
|
+
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
20
|
+
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
|
21
|
+
-webkit-transition: width 0.6s ease;
|
22
|
+
-o-transition: width 0.6s ease;
|
23
|
+
transition: width 0.6s ease;
|
24
|
+
}
|
25
|
+
|
26
|
+
.alert {
|
27
|
+
padding: 8px 35px 8px 14px;
|
28
|
+
margin-bottom: 20px;
|
29
|
+
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
30
|
+
background-color: #fcf8e3;
|
31
|
+
border: 1px solid #fbeed5;
|
32
|
+
-webkit-border-radius: 4px;
|
33
|
+
-moz-border-radius: 4px;
|
34
|
+
border-radius: 4px;
|
35
|
+
}
|
36
|
+
|
37
|
+
.alert,
|
38
|
+
.alert h4 {
|
39
|
+
color: #c09853;
|
40
|
+
}
|
41
|
+
|
42
|
+
.alert h4 {
|
43
|
+
margin: 0;
|
44
|
+
}
|
45
|
+
|
46
|
+
.alert .close {
|
47
|
+
position: relative;
|
48
|
+
top: -2px;
|
49
|
+
right: -21px;
|
50
|
+
line-height: 20px;
|
51
|
+
}
|
52
|
+
|
53
|
+
.alert-success {
|
54
|
+
color: #468847;
|
55
|
+
background-color: #dff0d8;
|
56
|
+
border-color: #d6e9c6;
|
57
|
+
}
|
58
|
+
|
59
|
+
.alert-success h4 {
|
60
|
+
color: #468847;
|
61
|
+
}
|
62
|
+
|
63
|
+
.alert-danger,
|
64
|
+
.alert-error {
|
65
|
+
color: #b94a48;
|
66
|
+
background-color: #f2dede;
|
67
|
+
border-color: #eed3d7;
|
68
|
+
}
|
69
|
+
|
70
|
+
.alert-danger h4,
|
71
|
+
.alert-error h4 {
|
72
|
+
color: #b94a48;
|
73
|
+
}
|
74
|
+
|
75
|
+
.alert-info {
|
76
|
+
color: #3a87ad;
|
77
|
+
background-color: #d9edf7;
|
78
|
+
border-color: #bce8f1;
|
79
|
+
}
|
80
|
+
|
81
|
+
.alert-info h4 {
|
82
|
+
color: #3a87ad;
|
83
|
+
}
|
84
|
+
|
85
|
+
.alert-block {
|
86
|
+
padding-top: 14px;
|
87
|
+
padding-bottom: 14px;
|
88
|
+
}
|
89
|
+
|
90
|
+
.alert-block > p,
|
91
|
+
.alert-block > ul {
|
92
|
+
margin-bottom: 0;
|
93
|
+
}
|
94
|
+
|
95
|
+
.alert-block p + p {
|
96
|
+
margin-top: 5px;
|
97
|
+
}
|
@@ -7,6 +7,16 @@ 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
|
+
|
10
20
|
def clear_batch_session
|
11
21
|
session[:canvas_batch_id] = nil
|
12
22
|
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
|
-
(name
|
54
|
+
([name, tail].join).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(url
|
59
|
+
options[:url] = main_app.send([url,'_url'].join)
|
60
60
|
options
|
61
61
|
end
|
62
62
|
|
@@ -13,7 +13,9 @@ 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
|
-
|
16
|
+
prefix = [oauth2_protocol, '://', api_domain].join
|
17
|
+
Rails.logger.info "Creating Bearcat client for auth token retrieval pointed to: #{prefix}"
|
18
|
+
client = Bearcat::Client.new(prefix: prefix)
|
17
19
|
token = client.retrieve_token(client_id, coalescing_panda.oauth2_redirect_url, client_key, params['code'])
|
18
20
|
CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).first_or_create do |auth|
|
19
21
|
auth.api_token = token
|
@@ -1,6 +1,8 @@
|
|
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'
|
4
6
|
has_many :submissions, foreign_key: :coalescing_panda_assignment_id, class_name: 'CoalescingPanda::Submission', dependent: :destroy
|
5
7
|
|
6
8
|
delegate :account, to: :course
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class AssignmentGroup < ActiveRecord::Base
|
3
|
+
belongs_to :course, foreign_key: :coalescing_panda_course_id, class_name: 'CoalescingPanda::Course'
|
4
|
+
has_many :assignments, foreign_key: :coalescing_panda_assignment_group_id, class_name: 'CoalescingPanda::Assignment', dependent: :destroy
|
5
|
+
|
6
|
+
delegate :account, to: :course
|
7
|
+
|
8
|
+
validates :coalescing_panda_course_id, presence: true
|
9
|
+
validates :canvas_assignment_group_id, presence: true
|
10
|
+
end
|
11
|
+
end
|
@@ -1,6 +1,10 @@
|
|
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'
|
3
6
|
belongs_to :context, polymorphic: true
|
7
|
+
|
4
8
|
default_scope { order('created_at DESC') }
|
5
9
|
end
|
6
10
|
end
|
@@ -8,8 +8,10 @@ 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
|
11
12
|
has_many :group_memberships, through: :groups, source: :group_memberships, class_name: 'CoalescingPanda::GroupMembership', dependent: :destroy
|
12
13
|
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
|
13
15
|
|
14
16
|
validates :coalescing_panda_lti_account_id, presence: true
|
15
17
|
validates :canvas_course_id, presence: true
|
@@ -3,9 +3,10 @@ module CoalescingPanda
|
|
3
3
|
belongs_to :context, :polymorphic => true
|
4
4
|
include SingleTablePolymorphic
|
5
5
|
|
6
|
-
|
6
|
+
belongs_to :leader, foreign_key: :leader_id, class_name: 'CoalescingPanda::User'
|
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
|
7
9
|
validates :group_category_id, presence: true
|
8
10
|
validates :canvas_group_id, presence: true
|
9
|
-
validates :coalescing_panda_user_id, presence: true
|
10
11
|
end
|
11
12
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class GroupCategory < ActiveRecord::Base
|
3
|
+
belongs_to :context, :polymorphic => true
|
4
|
+
include SingleTablePolymorphic
|
5
|
+
|
6
|
+
belongs_to :leader, foreign_key: :leader_id, class_name: 'CoalescingPanda::User'
|
7
|
+
has_many :groups, foreign_key: :coalescing_panda_group_category_id, class_name: 'CoalescingPanda::Group'
|
8
|
+
has_many :assignments, foreign_key: :coalescing_panda_group_category_id, class_name: 'CoalescingPanda::Assignment'
|
9
|
+
validates :canvas_group_category_id, presence: true
|
10
|
+
end
|
11
|
+
end
|
@@ -6,6 +6,7 @@ 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'
|
9
10
|
has_many :sections, through: :courses
|
10
11
|
has_many :enrollments, through: :sections
|
11
12
|
has_many :assignments, through: :courses
|
@@ -3,6 +3,7 @@ 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'
|
6
7
|
has_many :sections, through: :enrollments
|
7
8
|
has_many :courses, through: :sections
|
8
9
|
|
@@ -1,32 +1,53 @@
|
|
1
1
|
class CoalescingPanda::Workers::CourseMiner
|
2
|
-
SUPPORTED_MODELS = [:sections, :users, :enrollments, :assignments, :submissions, :groups, :group_memberships] #ORDER MATTERS!!
|
2
|
+
SUPPORTED_MODELS = [:sections, :users, :enrollments, :assignment_groups, :group_categories, :assignments, :submissions, :groups, :group_memberships] #ORDER MATTERS!!
|
3
|
+
COMPLETED_STATUSES = ['Completed', 'Error']
|
4
|
+
RUNNING_STATUSES = ['Queued', 'Started']
|
3
5
|
|
4
|
-
attr_accessor :options, :account, :course, :batch, :course_section_ids, :enrollment_ids, :assignment_ids, :group_ids, :user_ids
|
6
|
+
attr_accessor :options, :account, :course, :batch, :course_section_ids, :enrollment_ids, :assignment_ids, :assignment_group_ids, :group_ids, :user_ids
|
5
7
|
|
6
8
|
def initialize(course, options = [])
|
7
9
|
@course = course
|
8
10
|
@account = course.account
|
9
11
|
@options = options
|
10
|
-
@batch =
|
12
|
+
@batch = setup_batch
|
11
13
|
@course_section_ids = []
|
12
14
|
@enrollment_ids = []
|
13
15
|
@assignment_ids = []
|
16
|
+
@assignment_group_ids = []
|
14
17
|
@group_ids = []
|
15
18
|
@user_ids = []
|
16
19
|
end
|
17
20
|
|
21
|
+
def setup_batch
|
22
|
+
batch = account.canvas_batches.where(context: course).first
|
23
|
+
if batch.present? and RUNNING_STATUSES.include?(batch.status)
|
24
|
+
batch
|
25
|
+
else
|
26
|
+
batch = account.canvas_batches.create(context: course, status: "Queued")
|
27
|
+
end
|
28
|
+
batch.update_attributes(options: options)
|
29
|
+
batch
|
30
|
+
end
|
31
|
+
|
18
32
|
def api_client
|
19
33
|
@api_client ||= Bearcat::Client.new(prefix: account.settings[:base_url], token: account.settings[:account_admin_api_token])
|
20
34
|
end
|
21
35
|
|
22
|
-
def start
|
36
|
+
def start(forced = false)
|
37
|
+
unless forced
|
38
|
+
return unless batch.status == 'Queued' # don't start if there is already a running job
|
39
|
+
return unless should_download?
|
40
|
+
end
|
41
|
+
|
23
42
|
begin
|
24
43
|
batch.update_attributes(status: "Started", percent_complete: 0)
|
25
|
-
|
26
|
-
|
27
|
-
|
44
|
+
index = 1
|
45
|
+
SUPPORTED_MODELS.each do |model_key|
|
46
|
+
next unless options.include?(model_key)
|
47
|
+
process_api_data(model_key.to_sym)
|
28
48
|
percent_complete = (index/(options.count.nonzero? || 1).to_f * 100).round(1)
|
29
49
|
batch.update_attributes(percent_complete: percent_complete)
|
50
|
+
index += 1
|
30
51
|
end
|
31
52
|
batch.update_attributes(status: "Completed", percent_complete: 100)
|
32
53
|
rescue => e
|
@@ -35,8 +56,19 @@ class CoalescingPanda::Workers::CourseMiner
|
|
35
56
|
end
|
36
57
|
handle_asynchronously :start
|
37
58
|
|
59
|
+
def should_download?
|
60
|
+
return true unless account.settings[:canvas_download_interval].present?
|
61
|
+
return true unless last_completed_batch = account.canvas_batches.where(context: course, status: 'Completed').order('updated_at ASC').first
|
62
|
+
should_download = last_completed_batch.updated_at < Time.zone.now - account.settings[:canvas_download_interval].minutes
|
63
|
+
batch.update_attributes(status: 'Canceled') unless should_download
|
64
|
+
should_download
|
65
|
+
end
|
66
|
+
|
38
67
|
def process_api_data(key)
|
39
68
|
case key
|
69
|
+
when :assignment_groups
|
70
|
+
collection = api_client.list_assignment_groups(course.canvas_course_id).all_pages!
|
71
|
+
sync_assignment_groups(collection)
|
40
72
|
when :sections
|
41
73
|
collection = api_client.course_sections(course.canvas_course_id).all_pages!
|
42
74
|
sync_sections(collection)
|
@@ -60,6 +92,9 @@ class CoalescingPanda::Workers::CourseMiner
|
|
60
92
|
when :groups
|
61
93
|
collection = api_client.course_groups(course.canvas_course_id).all_pages!
|
62
94
|
sync_groups(collection)
|
95
|
+
when :group_categories
|
96
|
+
collection = api_client.list_group_categories('courses', course.canvas_course_id).all_pages!
|
97
|
+
sync_group_categories(collection)
|
63
98
|
when :group_memberships
|
64
99
|
collection = []
|
65
100
|
course.groups.each do |group|
|
@@ -73,25 +108,40 @@ class CoalescingPanda::Workers::CourseMiner
|
|
73
108
|
end
|
74
109
|
end
|
75
110
|
|
111
|
+
def sync_assignment_groups(collection)
|
112
|
+
collection.each do |values|
|
113
|
+
begin
|
114
|
+
values['canvas_assignment_group_id'] = values['id'].to_s
|
115
|
+
assignment_group = course.assignment_groups.where(canvas_assignment_group_id: values['canvas_assignment_group_id']).first_or_initialize
|
116
|
+
assignment_group.assign_attributes(standard_attributes(assignment_group, values))
|
117
|
+
assignment_group.save(validate: false)
|
118
|
+
assignment_group_ids << assignment_group.id
|
119
|
+
rescue => e
|
120
|
+
Rails.logger.error "Error syncing assignment group: #{values} Error: #{e}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
course.assignment_groups.where.not(id: assignment_group_ids).destroy_all
|
124
|
+
end
|
125
|
+
|
76
126
|
def sync_sections(collection)
|
77
|
-
|
78
|
-
|
127
|
+
collection.each do |values|
|
128
|
+
begin
|
79
129
|
values['course_section_id'] = values['id'].to_s
|
80
130
|
section = course.sections.where(canvas_section_id: values['course_section_id']).first_or_initialize
|
81
131
|
section.assign_attributes(standard_attributes(section, values))
|
82
132
|
section.sis_id = values['sis_section_id']
|
83
133
|
section.save(validate: false)
|
84
134
|
course_section_ids << section.id
|
135
|
+
rescue => e
|
136
|
+
Rails.logger.error "Error syncing section: #{values} Error: #{e}"
|
85
137
|
end
|
86
|
-
course.sections.where.not(id: course_section_ids).destroy_all
|
87
|
-
rescue => e
|
88
|
-
Rails.logger.error "Error syncing sections: #{e}"
|
89
138
|
end
|
139
|
+
course.sections.where.not(id: course_section_ids).destroy_all
|
90
140
|
end
|
91
141
|
|
92
142
|
def sync_users(collection)
|
93
|
-
|
94
|
-
|
143
|
+
collection.each do |values|
|
144
|
+
begin
|
95
145
|
values['canvas_user_id'] = values["id"].to_s
|
96
146
|
user = account.users.where(canvas_user_id: values['canvas_user_id']).first_or_initialize
|
97
147
|
user.coalescing_panda_lti_account_id = account.id
|
@@ -99,103 +149,129 @@ class CoalescingPanda::Workers::CourseMiner
|
|
99
149
|
user.sis_id = values['sis_user_id'].to_s
|
100
150
|
user_ids << user.id
|
101
151
|
user.save(validate: false)
|
152
|
+
rescue => e
|
153
|
+
Rails.logger.error "Error syncing user: #{values} Error: #{e}"
|
102
154
|
end
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
155
|
+
end
|
156
|
+
removed_users = course.users.where.not(id: user_ids)
|
157
|
+
removed_users.each do |user|
|
158
|
+
user.enrollments.each do |enrollment|
|
159
|
+
course.submissions.where(coalescing_panda_user_id: enrollment.user.id).destroy_all
|
160
|
+
enrollment.destroy
|
109
161
|
end
|
110
|
-
removed_users.destroy_all
|
111
|
-
rescue => e
|
112
|
-
Rails.logger.error "Error syncing users: #{e}"
|
113
162
|
end
|
163
|
+
removed_users.destroy_all
|
114
164
|
end
|
115
165
|
|
116
166
|
def sync_enrollments(collection)
|
117
|
-
|
118
|
-
|
167
|
+
collection.each do |values|
|
168
|
+
begin
|
119
169
|
values['canvas_enrollment_id'] = values['id'].to_s
|
120
|
-
|
170
|
+
section = course.sections.find_by(canvas_section_id: values['course_section_id'].to_s)
|
171
|
+
enrollment = section.enrollments.where(canvas_enrollment_id: values['canvas_enrollment_id']).first_or_initialize
|
121
172
|
enrollment.section = course.sections.find_by(canvas_section_id: values['course_section_id'].to_s)
|
122
173
|
enrollment.user = account.users.find_by(canvas_user_id: values['user_id'].to_s)
|
123
174
|
values['workflow_state'] = values["enrollment_state"]
|
124
175
|
values['enrollment_type'] = values['type']
|
125
176
|
enrollment.assign_attributes(standard_attributes(enrollment, values))
|
126
|
-
enrollment.save(validate: false)
|
177
|
+
enrollment.save!(validate: false)
|
127
178
|
enrollment_ids << enrollment.id
|
179
|
+
rescue => e
|
180
|
+
Rails.logger.error "Error syncing enrollment: #{values} Error: #{e}"
|
128
181
|
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}"
|
136
182
|
end
|
183
|
+
removed_enrollments = course.enrollments.where.not(id: enrollment_ids)
|
184
|
+
removed_enrollments.each do |enrollment|
|
185
|
+
course.submissions.where(coalescing_panda_user_id: enrollment.user.id).destroy_all
|
186
|
+
end
|
187
|
+
removed_enrollments.destroy_all
|
137
188
|
end
|
138
189
|
|
139
190
|
def sync_assignments(collection)
|
140
|
-
|
141
|
-
|
191
|
+
collection.each do |values|
|
192
|
+
begin
|
142
193
|
values['canvas_assignment_id'] = values['id'].to_s
|
143
194
|
assignment = course.assignments.where(canvas_assignment_id: values['canvas_assignment_id']).first_or_initialize
|
195
|
+
assignment_group = course.assignment_groups.find_by(canvas_assignment_group_id: values['assignment_group_id'].to_s)
|
196
|
+
group_category = course.group_categories.find_by(canvas_group_category_id: values['group_category_id'])
|
197
|
+
assignment.coalescing_panda_assignment_group_id = assignment_group.id if assignment_group
|
198
|
+
assignment.coalescing_panda_group_category_id = group_category.id if group_category
|
144
199
|
assignment.assign_attributes(standard_attributes(assignment, values))
|
145
200
|
assignment.save(validate: false)
|
146
201
|
assignment_ids << assignment.id
|
202
|
+
rescue => e
|
203
|
+
Rails.logger.error "Error syncing assignment: #{values} Error: #{e}"
|
147
204
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
rescue => e
|
153
|
-
Rails.logger.error "Error syncing assignments: #{e}"
|
205
|
+
end
|
206
|
+
course.assignments.where.not(id: assignment_ids).each do |assignment|
|
207
|
+
assignment.submissions.destroy_all
|
208
|
+
assignment.destroy!
|
154
209
|
end
|
155
210
|
end
|
156
211
|
|
157
212
|
def sync_submissions(collection)
|
158
|
-
|
159
|
-
|
213
|
+
collection.each do |values|
|
214
|
+
begin
|
160
215
|
values['canvas_submission_id'] = values['id'].to_s
|
161
216
|
submission = course.submissions.where(canvas_submission_id: values['canvas_submission_id']).first_or_initialize
|
162
217
|
submission.user = course.users.find_by(canvas_user_id: values['user_id'].to_s)
|
163
218
|
submission.assignment = course.assignments.find_by(canvas_assignment_id: values['assignment_id'].to_s)
|
164
219
|
submission.assign_attributes(standard_attributes(submission, values))
|
165
220
|
submission.save(validate: false)
|
221
|
+
rescue => e
|
222
|
+
Rails.logger.error "Error syncing submission: #{values} Error: #{e}"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def sync_group_categories(collection)
|
228
|
+
collection.each do |values|
|
229
|
+
begin
|
230
|
+
values['canvas_group_category_id'] = values['id'].to_s
|
231
|
+
values.delete('context_type') #assume only course for now
|
232
|
+
category = course.group_categories.where(canvas_group_category_id: values['canvas_group_category_id']).first_or_initialize
|
233
|
+
category.assign_attributes(standard_attributes(category, values))
|
234
|
+
category.save(validate: false)
|
235
|
+
rescue => e
|
236
|
+
Rails.logger.error "Error syncing group categories: #{values} Error: #{e.message} - #{e.backtrace}"
|
166
237
|
end
|
167
|
-
rescue => e
|
168
|
-
Rails.logger.error "Error syncing submissions: #{e}"
|
169
238
|
end
|
170
239
|
end
|
171
240
|
|
172
241
|
def sync_groups(collection)
|
173
|
-
|
174
|
-
|
242
|
+
collection.each do |values|
|
243
|
+
begin
|
175
244
|
values['canvas_group_id'] = values['id'].to_s
|
176
245
|
group = course.groups.where(canvas_group_id: values['canvas_group_id']).first_or_initialize
|
246
|
+
group_category = course.group_categories.find_by(canvas_group_category_id: values['group_category_id'])
|
247
|
+
if values['leader']
|
248
|
+
group.leader = course.users.find_by(canvas_user_id: values['leader']['id'].to_s)
|
249
|
+
else
|
250
|
+
group.leader = nil
|
251
|
+
end
|
252
|
+
group.coalescing_panda_group_category_id = group_category.id if group_category
|
177
253
|
group.assign_attributes(standard_attributes(group, values))
|
178
254
|
group.save(validate: false)
|
179
255
|
group_ids << group.id
|
256
|
+
rescue => e
|
257
|
+
Rails.logger.error "Error syncing group: #{values} Error: #{e}"
|
180
258
|
end
|
181
|
-
course.groups.where.not(id: group_ids).destroy_all
|
182
|
-
rescue => e
|
183
|
-
Rails.logger.error "Error syncing groups: #{e}"
|
184
259
|
end
|
260
|
+
course.groups.where.not(id: group_ids).destroy_all
|
185
261
|
end
|
186
262
|
|
187
263
|
def sync_group_memberships(collection)
|
188
|
-
|
189
|
-
|
264
|
+
collection.each do |values|
|
265
|
+
begin
|
190
266
|
values['canvas_group_membership_id'] = values['id'].to_s
|
191
267
|
group_membership = course.group_memberships.where(canvas_group_membership_id: values['canvas_group_membership_id']).first_or_initialize
|
192
268
|
group_membership.group = course.groups.find_by(canvas_group_id: values['group_id'].to_s)
|
193
269
|
group_membership.user = course.users.find_by(canvas_user_id: values['user_id'].to_s)
|
194
270
|
group_membership.assign_attributes(standard_attributes(group_membership, values))
|
195
271
|
group_membership.save(validate: false)
|
272
|
+
rescue => e
|
273
|
+
Rails.logger.error "Error syncing group memebership: #{values} Error: #{e}"
|
196
274
|
end
|
197
|
-
rescue => e
|
198
|
-
Rails.logger.error "Error syncing group memberships: #{e}"
|
199
275
|
end
|
200
276
|
end
|
201
277
|
|
@@ -204,4 +280,4 @@ class CoalescingPanda::Workers::CourseMiner
|
|
204
280
|
new_attributes.delete('id')
|
205
281
|
new_attributes.delete_if { |key, value| !record.attributes.include?(key) }
|
206
282
|
end
|
207
|
-
end
|
283
|
+
end
|
@@ -1,20 +1,32 @@
|
|
1
1
|
#batch-info{data: {batch: @batch.to_json}}
|
2
|
-
-if @batch.status == "Queued"
|
3
|
-
%
|
2
|
+
- if @batch.status == "Queued"
|
3
|
+
%span.batch-message-queued Data is queued for download from Canvas.
|
4
4
|
|
5
|
-
-if @batch.status == "Completed"
|
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
|
-
-if @batch.status == "Started"
|
12
|
-
%h6.batch-message-started
|
13
|
-
|
14
|
-
.
|
11
|
+
- if @batch.status == "Started"
|
12
|
+
%h6.batch-message-started
|
13
|
+
Downloading data from Canvas
|
14
|
+
.progress.progress-striped.active
|
15
|
+
.bar{:style => "width: #{@batch.percent_complete}%;"}
|
15
16
|
|
16
|
-
|
17
|
+
|
18
|
+
- if @batch.status == "Error"
|
17
19
|
.alert.alert-block.alert-error
|
18
20
|
%button.close{"data-dismiss" => "alert", :type => "button"} ×
|
19
|
-
%
|
20
|
-
= @batch.message
|
21
|
+
%span.batch-message-error Data failed to download from Canvas
|
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
|
@@ -1,4 +1,4 @@
|
|
1
1
|
- if current_batch.present?
|
2
2
|
- path = CoalescingPanda::Engine.routes.url_helpers.canvas_batch_path(current_batch)
|
3
|
-
-clear_path = CoalescingPanda::Engine.routes.url_helpers.clear_batch_session_path
|
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,5 +1,7 @@
|
|
1
1
|
CoalescingPanda::Engine.routes.draw do
|
2
|
-
resources :canvas_batches, only: [:show]
|
2
|
+
resources :canvas_batches, only: [:show, :update] do
|
3
|
+
post :retrigger, on: :member
|
4
|
+
end
|
3
5
|
post '/canvas_batches/clear_batch_session', as: :clear_batch_session
|
4
6
|
|
5
7
|
get '/oauth2/redirect' => 'oauth2#redirect'
|