create results for manually graded LTI submissions

Thanks Xander for the rest of this commit message :D

refs INTEROP-7100
flag=none

why:
* all submissions with scores should have matching Lti::Results
* a previous commit covered the case where an existing assignment with
submissions is associated with an LTI tool
* this case is where an LTI assignment is manually graded by a teacher,
without the tool having sent scores back

test plan
* without this commit checked out:
* create an assignment associated with a LTI 1.3 tool
* manually grade a student for this assignment, without them launching
the tool or submitting anything from the tool
* check that an Lti::Result has been created following instructions
above
* without this commit, there should be no `Lti::Result` for this
submission
* check out this commit and repeat the above steps
* with this commit, the `Lti::Result` should have an accurate score,
user, and submitted_at timestamp
* manually grade the same student again, with a different score value
* the same Lti::Result
* test also on assignments created through AGS, both
  submission_type=none and submission_type=external_tool

Change-Id: I431159db38d960ab20a992250df652f3b8f559db
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/288127
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Xander Moffatt <xmoffatt@instructure.com>
QA-Review: Xander Moffatt <xmoffatt@instructure.com>
Product-Review: Evan Battaglia <ebattaglia@instructure.com>
This commit is contained in:
Evan Battaglia 2022-03-25 15:14:05 -06:00
parent 905f275547
commit 724c649fba
3 changed files with 50 additions and 22 deletions

View File

@ -2547,6 +2547,11 @@ class Submission < ActiveRecord::Base
return unless saved_change_to_score?
return if autograded? # Submission changed by LTI Tool, it will set result score directly
unless lti_result
assignment.line_items.first&.results&.create!(
submission: self, user: user, created_at: Time.zone.now, updated_at: Time.zone.now
)
end
Lti::Result.update_score_for_submission(self, score)
end

View File

@ -26,7 +26,21 @@ module Factories
)
user = line_item_user(li_result_overrides, course)
li = lti_result_line_item(li_result_overrides, course)
Lti::Result.create!(line_item_results_params(li_result_overrides, user, li))
time = Time.zone.now
submission = lti_result_submission(li_result_overrides, user, li)
# If submissionscore was updated, a Lti::Result will already exist
li = Lti::Result.find_or_initialize_by(line_item: li, submission: submission, user: user)
li.assign_attributes(
activity_progress: li_result_overrides.fetch(:activity_progress, "Completed"),
grading_progress: li_result_overrides.fetch(:grading_progress, "FullyGraded"),
result_score: li_result_overrides[:result_score],
result_maximum: li_result_overrides[:result_maximum],
updated_at: li_result_overrides.fetch(:updated_at, time),
created_at: time,
comment: li_result_overrides[:comment]
)
li.save!
li
end
private
@ -70,21 +84,4 @@ module Factories
submission.update(score: li_result_overrides[:result_score]) if li_result_overrides[:result_score]
submission
end
def line_item_results_params(li_result_overrides, user, li)
time = Time.zone.now
submission = lti_result_submission(li_result_overrides, user, li)
{
activity_progress: li_result_overrides.fetch(:activity_progress, "Completed"),
grading_progress: li_result_overrides.fetch(:grading_progress, "FullyGraded"),
line_item: li,
user: user,
submission: submission,
result_score: li_result_overrides[:result_score],
result_maximum: li_result_overrides[:result_maximum],
updated_at: li_result_overrides.fetch(:updated_at, time),
created_at: time,
comment: li_result_overrides[:comment]
}
end
end

View File

@ -7368,10 +7368,36 @@ describe Submission do
end
describe "#update_line_item_result" do
it "does nothing if lti_result does not exist" do
submission = submission_model assignment: @assignment
expect(submission).to receive(:update_line_item_result)
submission.save!
let(:submission) { submission_model(assignment: @assignment) }
context "when lti_result does not exist" do
it "does nothing when there is no line item" do
expect do
submission.update!(score: 1.3)
end.to_not change { submission.lti_result }.from(nil)
end
context "when there is a line item" do
before { line_item_model(assignment: @assignment) }
it "does nothing if score has not changed" do
expect do
submission.update!(body: "hello abc")
end.to_not change { submission.lti_result }.from(nil)
end
it "creates an the lti_result with the correct score_given if the score has changed" do
expect do
submission.update!(score: 1.3)
end.to change { submission.lti_result&.reload&.result_score }.from(nil).to(1.3)
end
it "does nothing if the lti_result was updated by a tool" do
expect do
submission.update!(score: 1.3, grader_id: -123)
end.to_not change { submission.lti_result }.from(nil)
end
end
end
context "with lti_result" do