Skip to content

Commit a419a64

Browse files
committed
allow teachers to resubmit to turnitin; closes #7981
add more structure to the turnitin_data hash on a submission to keep track of what state things are in, so that we can present errors to the user as they happen and provide a way to resubmit. there are three states: 'pending', 'error', and 'scored'. submissions start out as 'pending', and can move to 'error' if anything goes wrong in the submission process (either in creating the assignment, enrolling the user, or actually submitting the paper). there are built in retrys, and the error state only gets set if there is still an error after the retry limit. if this succeeds, we stay pending until the score is successfully retrieved from turnitin, at which point it is marked 'scored'. this change is primarily in speed grader, although icons have been updated in the gradebooks and submission pages as well. in the 'scored' state, (or without a state) things should work exactly as they have previously - display a score, which can be clicked to see the full report. in the 'error' state (indicated with an exclaimation point icon), clicking the icon gives you more details about the error and the option to resubmit. in the 'pending' state (indicated by a clock icon), clicking the icon just explains that the submission is pending. test-plan: - locate some submissions that were scored with turnitin before this change was applied and verify that they still work as expected - submit something new that will get sent to turnitin. verify that it initially gets marked as pending, and then after 5-10 minutes has a score. - submit something that will result in a turnitin error (eg: < 20 words, password protected pdf). verify that it initially gets marked as pending, then after ~30 minutes (to account for retries) is marked as an error. - click the icon, and click the resubmit button. verify that it goes back to pending. - verify that the new turnitin icons for these submission in gradebooks 1 and 2 look correct and don't mess up the layout. Change-Id: Ia5e1b04842de16aaa44d498eefe59ddb716dc014 Reviewed-on: https://gerrit.instructure.com/10125 Reviewed-by: Simon Williams <simon@instructure.com> Tested-by: Hudson <hudson@instructure.com>
1 parent f1505f0 commit a419a64

27 files changed

Lines changed: 522 additions & 134 deletions

app/coffeescripts/SubmissionDetailsDialog.coffee

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
define [
2+
'i18n!submission_details_dialog'
23
'jquery'
34
'jst/SubmissionDetailsDialog'
45
'jst/_submission_detail' # a partial needed by the SubmissionDetailsDialog template
6+
'jst/_turnitinScore' # a partial needed by the submission_detail partial
57
'jquery.ajaxJSON'
68
'jquery.disableWhileLoading'
79
'jquery.instructure_forms'
810
'jqueryui/dialog'
911
'jquery.instructure_misc_plugins'
1012
'vendor/jquery.scrollTo'
11-
], ($, submissionDetailsDialog) ->
13+
], (I18n, $, submissionDetailsDialog) ->
1214

1315
class SubmissionDetailsDialog
1416
constructor: (@assignment, @student, @options) ->
@@ -59,8 +61,11 @@ define [
5961
for attachment in submission.attachments || []
6062
if turnitinDataForThisAttachment = submission.turnitin_data?["attachment_#{attachment.id}"]
6163
if turnitinDataForThisAttachment["similarity_score"]
62-
attachment.turnitinUrl = "#{@options.context_url}/assignments/#{@assignment.id}/submissions/#{@student.id}/turnitin/attachment_#{attachment.id}"
6364
attachment.turnitin_data = turnitinDataForThisAttachment
65+
attachment.turnitin_data.state = "#{turnitinDataForThisAttachment.state || 'no'}_score"
66+
attachment.turnitin_data.score = "#{turnitinDataForThisAttachment.similarity_score}%"
67+
attachment.turnitin_data.reportUrl = "#{@options.context_url}/assignments/#{@assignment.id}/submissions/#{@student.id}/turnitin/attachment_#{attachment.id}"
68+
attachment.turnitin_data.tooltip = I18n.t('turnitin.tooltip.score', 'Turnitin Similarity Score - See detailed report')
6469
@dialog.html(submissionDetailsDialog(@submission))
6570
@dialog.find('select').trigger('change')
6671
@scrollCommentsToBottom()

app/controllers/submissions_controller.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,21 @@ def turnitin_report
334334
end
335335
end
336336
end
337+
338+
def resubmit_to_turnitin
339+
if authorized_action(@context, @current_user, [:manage_grades, :view_all_grades])
340+
@assignment = @context.assignments.active.find(params[:assignment_id])
341+
@submission = @assignment.submissions.find_by_user_id(params[:submission_id])
342+
@submission.resubmit_to_turnitin
343+
respond_to do |format|
344+
format.html {
345+
flash[:notice] = t('resubmitted_to_turnitin', "Successfully resubmitted to turnitin.")
346+
redirect_to named_context_url(@context, :context_assignment_submission_url, @assignment.id, @submission.user_id)
347+
}
348+
format.json { render :nothing => true, :status => :no_content }
349+
end
350+
end
351+
end
337352

338353
def update
339354
@assignment = @context.assignments.active.find(params[:assignment_id])

app/models/submission.rb

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -205,24 +205,29 @@ def check_turnitin_status(asset_string, attempt=1)
205205
self.turnitin_data ||= {}
206206
data = self.turnitin_data[asset_string]
207207
return unless data && data[:object_id]
208-
if !data[:similarity_score] && attempt < 10
209-
turnitin = Turnitin::Client.new(*self.context.turnitin_settings)
210-
res = turnitin.generateReport(self, asset_string)
211-
if res[:similarity_score]
212-
data[:similarity_score] = res[:similarity_score].to_f
213-
data[:web_overlap] = res[:web_overlap].to_f
214-
data[:publication_overlap] = res[:publication_overlap].to_f
215-
data[:student_overlap] = res[:student_overlap].to_f
216-
data[:state] = 'failure'
217-
data[:state] = 'problem' if data[:similarity_score] < 75
218-
data[:state] = 'warning' if data[:similarity_score] < 50
219-
data[:state] = 'acceptable' if data[:similarity_score] < 25
220-
data[:state] = 'none' if data[:similarity_score] == 0
208+
if data[:similarity_score].blank?
209+
if attempt < TURNITIN_RETRY
210+
turnitin = Turnitin::Client.new(*self.context.turnitin_settings)
211+
res = turnitin.generateReport(self, asset_string)
212+
if res[:similarity_score]
213+
data[:similarity_score] = res[:similarity_score].to_f
214+
data[:web_overlap] = res[:web_overlap].to_f
215+
data[:publication_overlap] = res[:publication_overlap].to_f
216+
data[:student_overlap] = res[:student_overlap].to_f
217+
data[:state] = 'failure'
218+
data[:state] = 'problem' if data[:similarity_score] < 75
219+
data[:state] = 'warning' if data[:similarity_score] < 50
220+
data[:state] = 'acceptable' if data[:similarity_score] < 25
221+
data[:state] = 'none' if data[:similarity_score] == 0
222+
data[:status] = 'scored'
223+
else
224+
send_at((5 * attempt).minutes.from_now, :check_turnitin_status, asset_string, attempt + 1)
225+
end
221226
else
222-
send_at((5 * attempt).minutes.from_now, :check_turnitin_status, asset_string, attempt + 1)
227+
data[:status] = 'error'
223228
end
224229
else
225-
data[:error] = true
230+
data[:status] = 'scored'
226231
end
227232
self.turnitin_data[asset_string] = data
228233
self.turnitin_data_changed!
@@ -263,44 +268,79 @@ def submit_to_turnitin_later
263268
end
264269
end
265270

271+
TURNITIN_RETRY = 5
266272
def submit_to_turnitin(attempt=0)
267273
return unless self.context.turnitin_settings
268274
turnitin = Turnitin::Client.new(*self.context.turnitin_settings)
269-
self.turnitin_data ||= {}
275+
reset_turnitin_assets
270276

271-
# 1. Make sure the assignment exists
277+
# 1. Make sure the assignment exists and user is enrolled
272278
assign_status = self.assignment.create_in_turnitin
273-
unless assign_status
274-
send_at(5.minutes.from_now, :submit_to_turnitin, attempt + 1) if attempt < 5
275-
return false
276-
end
277-
278-
# 2. Make sure the user is enrolled
279-
# TODO: track this elsewhere so we don't have to do the API call on every submission
280279
enroll_status = turnitin.enrollStudent(self.context, self.user)
281-
unless enroll_status
282-
send_at(5.minutes.from_now, :submit_to_turnitin, attempt + 1) if attempt < 5
280+
unless assign_status && enroll_status
281+
if attempt < TURNITIN_RETRY
282+
send_at(5.minutes.from_now, :submit_to_turnitin, attempt + 1)
283+
else
284+
assign_error = self.assignment.turnitin_settings[:error]
285+
turnitin_assets.each do |a|
286+
self.turnitin_data[a.asset_string][:status] = 'error'
287+
self.turnitin_data[a.asset_string].merge!(assign_error) if assign_error.present?
288+
end
289+
self.turnitin_data_changed!
290+
self.save
291+
end
283292
return false
284293
end
285294

286-
# 3. Submit the file(s)
295+
# 2. Submit the file(s)
287296
submission_response = turnitin.submitPaper(self)
288-
submission_response.each do |asset_string, response|
289-
if self.turnitin_data[asset_string] != response
290-
self.turnitin_data[asset_string] = response
291-
self.turnitin_data_changed!
292-
self.send_at(5.minutes.from_now, :check_turnitin_status, asset_string) if response[:object_id]
297+
submission_response.each do |res_asset_string, response|
298+
self.turnitin_data[res_asset_string].merge!(response)
299+
self.turnitin_data_changed!
300+
if response[:object_id]
301+
self.send_at(5.minutes.from_now, :check_turnitin_status, res_asset_string)
302+
elsif !(attempt < TURNITIN_RETRY)
303+
self.turnitin_data[res_asset_string][:status] = 'error'
293304
end
294305
end
295306
self.save
296307
submit_status = submission_response.present? && submission_response.values.all?{ |v| v[:object_id] }
297308
unless submit_status
298-
send_at(5.minutes.from_now, :submit_to_turnitin, attempt + 1) if attempt < 5
309+
send_at(5.minutes.from_now, :submit_to_turnitin, attempt + 1) if attempt < TURNITIN_RETRY
299310
return false
300311
end
301312

302313
true
303314
end
315+
316+
def turnitin_assets
317+
if self.submission_type == 'online_upload'
318+
self.attachments.select{ |a| a.turnitinable? }
319+
elsif self.submission_type == 'online_text_entry'
320+
[self]
321+
end
322+
end
323+
324+
def reset_turnitin_assets
325+
self.turnitin_data ||= {}
326+
turnitin_assets.each do |a|
327+
asset_data = self.turnitin_data[a.asset_string] || {}
328+
asset_data[:status] = 'pending'
329+
[:error_code, :error_message, :public_error_message].each do |key|
330+
asset_data.delete(key)
331+
end
332+
self.turnitin_data[a.asset_string] = asset_data
333+
self.turnitin_data_changed!
334+
end
335+
end
336+
337+
def resubmit_to_turnitin
338+
reset_turnitin_assets
339+
self.save
340+
341+
@submit_to_turnitin = true
342+
submit_to_turnitin_later
343+
end
304344

305345
def turnitinable?
306346
if self.submission_type == 'online_upload' || self.submission_type == 'online_text_entry'

app/stylesheets/g_instructure.sass

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import environment.sass
2+
@import turnitin.sass
23

34
#instructure_ajax_error_box
45
:display none
@@ -1379,23 +1380,6 @@ fieldset
13791380
legend
13801381
font-weight: bold
13811382
margin-left: 20px
1382-
1383-
.turnitin_similarity_score
1384-
white-space: nowrap
1385-
border: 1px solid #bbb
1386-
border-top: 5px solid #bbb
1387-
padding: 1px 2px 2px
1388-
font-size: 0.8em
1389-
&.none_score
1390-
border-color: #4987EC
1391-
&.acceptable_score
1392-
border-color: #52E252
1393-
&.warning_score
1394-
border-color: #ECEC49
1395-
&.problem_score
1396-
border-color: #FFB420
1397-
&.failure_score
1398-
border-color: #FF3F3F
13991383
h2.h-margin-top,h3.h-margin-top
14001384
margin-top: 20px
14011385

app/stylesheets/speed_grader.sass

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -428,20 +428,14 @@ body .ui-selectmenu-dropdown:hover .ui-selectmenu-icon, body .ui-state-active .u
428428
background-position: 0 -12px
429429
.resubmitted &
430430
background-position: 0 -175px
431-
#grade_container
432-
.turnitin_similarity_score
433-
background-color: #fff
434-
font-size: 0.8em
435-
padding-bottom: 0
436-
color: #444
437-
text-decoration: none
438-
margin-top: -2px
439-
margin-left: 10px
440-
&:hover
441-
text-decoration: none
431+
.turnitin_info
432+
margin-top: 3px
433+
border-top: 1px solid #999999
434+
padding: 3px 0
442435
#submission_details
443436
border-bottom: 1px solid #999999
444-
padding-bottom: 3px
437+
&.content_box
438+
padding-top: 0
445439
a
446440
font-size: 12px
447441
.on_time
@@ -453,33 +447,28 @@ body .ui-selectmenu-dropdown:hover .ui-selectmenu-icon, body .ui-state-active .u
453447
#submission_late_notice
454448
color: #d12f19
455449
font-weight: bold
456-
margin-left: 3px
457450
#submission_not_newest_notice
458451
color: #EB9A00
459452
font-weight: bold
460-
margin-left: 3px
461453
select
462454
width: 99%
463455
label
464456
font-weight: bold
465-
margin-left: 3px
466457
#submission_files_list
467458
overflow: auto
468459
.submission-file
469460
padding: 1px 16px 1px 19px
470461
position: relative
471-
.turnitin_similarity_score
462+
.turnitin_score_container
472463
position: absolute
473464
top: 0
474465
right: 20px
475-
background-color: #fff
476-
font-size: 0.7em
477-
padding-bottom: 0
478-
color: #444
479-
text-decoration: none
480-
margin-top: -2px
466+
.turnitin_similarity_score
467+
font-size: 9px
468+
&.submission_error, &.submission_pending
469+
padding: 1px
481470
&:hover
482-
text-decoration: none
471+
font-size: 0.7em
483472
.submission-file-download
484473
display: block
485474
+opacity(0.5)
@@ -492,8 +481,6 @@ body .ui-selectmenu-dropdown:hover .ui-selectmenu-icon, body .ui-state-active .u
492481
text-indent: -9999px
493482
&:hover
494483
background-color: #DCECFB
495-
.turnitin_similarity_score
496-
display: block
497484
.submission-file-download
498485
display: block
499486
+opacity(1.0)

app/stylesheets/submissionDetailsDialog.sass

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
padding: 3px 0
2424
a
2525
font-size: 11px
26+
a.turnitin_similarity_score
27+
font-size: 10px
2628
.submission_details_comments
2729
overflow: auto
2830
max-height: 200px
@@ -75,4 +77,4 @@
7577
font-weight: normal
7678
.button
7779
float: right
78-
80+

app/stylesheets/turnitin.sass

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
@import environment.sass
2+
3+
// #4985f5
4+
$turnitin_blue: rgba(73, 133, 245, 0.5)
5+
// #4ff057
6+
$turnitin_green: rgba(79, 240, 87, 0.5)
7+
// #f5f549
8+
$turnitin_yellow: rgba(245, 245, 73, 0.5)
9+
// #ffb433
10+
$turnitin_orange: rgba(255, 180, 51, 0.5)
11+
// #ff383f
12+
$turnitin_red: rgba(255, 56, 63, 0.5)
13+
// #b8b8b8
14+
$turnitin_gray: rgba(184, 184, 184, 0.5)
15+
16+
.turnitin_score_container_caret
17+
position: absolute
18+
width: 0
19+
height: 0
20+
top: 50%
21+
content: ""
22+
border-width: 4px
23+
border-color: transparent
24+
border-style: solid
25+
left: -8px
26+
margin-top: -4px
27+
&.none_score
28+
border-right-color: $turnitin_blue
29+
&.acceptable_score
30+
border-right-color: $turnitin_green
31+
&.warning_score
32+
border-right-color: $turnitin_yellow
33+
&.problem_score
34+
border-right-color: $turnitin_orange
35+
&.failure_score
36+
border-right-color: $turnitin_red
37+
&.submission_error, &.submission_pending
38+
border-right-color: $turnitin_gray
39+
.turnitin_score_container
40+
position: relative
41+
margin-left: 10px
42+
.turnitin_similarity_score,
43+
.turnitin_similarity_score:link,
44+
.turnitin_similarity_score:visited,
45+
.turnitin_similarity_score:active,
46+
.turnitin_similarity_score:hover,
47+
.turnitin_similarity_score:focus
48+
display: inline-block
49+
white-space: nowrap
50+
+border-radius(3px)
51+
padding: 2px 4px
52+
font-weight: bold
53+
text-shadow: rgba(255, 255, 255, 0.5) 1px 0 1px
54+
color: #444
55+
font-size: 10px
56+
text-decoration: none
57+
&.none_score
58+
background-color: $turnitin_blue
59+
&.acceptable_score
60+
background-color: $turnitin_green
61+
&.warning_score
62+
background-color: $turnitin_yellow
63+
&.problem_score
64+
background-color: $turnitin_orange
65+
&.failure_score
66+
background-color: $turnitin_red
67+
&.submission_error, &.submission_pending
68+
background-color: $turnitin_gray
69+
.turnitin_resubmit_container
70+
text-align: right

0 commit comments

Comments
 (0)