destroy LORs when a quiz changes from an assignment

closes OUT-4286
flag=none

test-plan:
- create a question & question bank with an outcome aligned to it
- align the question bank to a new quiz and publish the quiz
- take the quiz as a student
- verify you have a learning outcome result record for the student
- edit the quiz type to be a "practice quiz"
- verify the learning outcome result was soft deleted
- verify the result doesn't appear in the SLMGB
- change the quiz type back to assignment
- verify the result appears in the SLMGB

Change-Id: Ia17360a1ac4508590a19154e824f60194972eff6
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/263504
Product-Review: Augusto Callejas <acallejas@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
QA-Review: Brian Watson <bwatson@instructure.com>
Reviewed-by: Augusto Callejas <acallejas@instructure.com>
This commit is contained in:
Pat Renner 2021-04-22 16:53:55 -05:00
parent b4eede0022
commit 9ffa3b5452
4 changed files with 82 additions and 36 deletions

View File

@ -75,7 +75,10 @@ class Quizzes::Quiz < ActiveRecord::Base
after_save :clear_availability_cache
after_save :touch_context
after_save :regrade_if_published
# We currently only create LORs for quizzes that are assignments. If we change the quiz_type,
# we should destroy or restore the results appropriately
after_save :destroy_learning_outcome_results, if: -> { saved_change_to_quiz_type?(from: 'assignment') }
after_save :restore_learning_outcome_results, if: -> { saved_change_to_quiz_type?(to: 'assignment') }
serialize :quiz_data
simply_versioned
@ -407,6 +410,18 @@ class Quizzes::Quiz < ActiveRecord::Base
self.quiz_submissions.each { |s| s.save! }
end
def update_learning_outcome_results(state)
LearningOutcomeResult.for_associated_asset(self).update_all(workflow_state: state, updated_at: Time.zone.now)
end
def destroy_learning_outcome_results
delay_if_production.update_learning_outcome_results('deleted')
end
def restore_learning_outcome_results
delay_if_production.update_learning_outcome_results('active')
end
def destroy_related_submissions
self.quiz_submissions.each do |qs|
submission = qs.submission

View File

@ -200,7 +200,7 @@ module Factories
"id"=>2159
}
],
"question_text"=>"<p>there's no such thing as a _____ question</p>",
"question_text"=>"<p>there's no such thing as a _____ question</p>",
"id" => 1
}.with_indifferent_access
end
@ -472,4 +472,39 @@ module Factories
@quiz.save!
@quiz
end
def question_data(reset=false, data={})
@qdc = reset || !@qdc ? 1 : @qdc + 1
{
:name => "question #{@qdc}", :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' =>
[{'answer_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '3'}, {'answer_text' => '4'}]
}.merge(data)
end
def find_the_answer_from_a_question(question)
question.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
end
def answer_a_question(question, submission, correct: true)
return if question.question_data['answers'] == []
q_id = question.data[:id]
answer = if correct
find_the_answer_from_a_question(question)
else
find_the_answer_from_a_question(question) + 1
end
submission.submission_data["question_#{q_id}"] = answer
end
def build_course_quiz_questions_and_a_bank(data={}, opts={})
scoring_policy = opts[:scoring_policy] || "keep_highest"
course_with_student(:active_all => true)
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true, :quiz_type => "assignment", scoring_policy: scoring_policy)
@q1 = @quiz.quiz_questions.create!(:question_data => question_data(true, data[:q1] || data))
@q2 = @quiz.quiz_questions.create!(:question_data => question_data(false, data[:q2] || data))
@outcome = @course.created_learning_outcomes.create!(:short_description => 'new outcome')
@bank = @q1.assessment_question.assessment_question_bank
@outcome.align(@bank, @bank.context, :mastery_score => 0.7)
end
end

View File

@ -21,39 +21,6 @@
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
describe Quizzes::QuizOutcomeResultBuilder do
def question_data(reset=false, data={})
@qdc = (reset || !@qdc) ? 1 : @qdc + 1
{:name => "question #{@qdc}", :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' =>
[{'answer_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '3'}, {'answer_text' => '4'}]
}.merge(data)
end
def build_course_quiz_questions_and_a_bank(data={}, opts={})
scoring_policy = opts[:scoring_policy] || "keep_highest"
course_with_student(:active_all => true)
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true, :quiz_type => "assignment", scoring_policy: scoring_policy)
@q1 = @quiz.quiz_questions.create!(:question_data => question_data(true, data[:q1] || data))
@q2 = @quiz.quiz_questions.create!(:question_data => question_data(false, data[:q2] || data))
@outcome = @course.created_learning_outcomes.create!(:short_description => 'new outcome')
@bank = @q1.assessment_question.assessment_question_bank
@outcome.align(@bank, @bank.context, :mastery_score => 0.7)
end
def find_the_answer_from_a_question(question)
question.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
end
def answer_a_question(question, submission, correct: true)
return if question.question_data['answers'] == []
q_id = question.data[:id]
answer = if correct
find_the_answer_from_a_question(question)
else
find_the_answer_from_a_question(question) + 1
end
submission.submission_data["question_#{q_id}"] = answer
end
describe "quiz level learning outcome results" do
before :once do
build_course_quiz_questions_and_a_bank
@ -139,7 +106,7 @@ describe Quizzes::QuizOutcomeResultBuilder do
Quizzes::SubmissionGrader.new(@sub).grade_submission
@outcome.reload
@outcome2.reload
@quiz_results = LearningOutcomeResult.where(user_id: @user).to_a
@quiz_results = LearningOutcomeResult.where(user_id: @user).sort_by(&:learning_outcome_id).to_a
@question_results = @quiz_results.map(&:learning_outcome_question_results)
end
it "has valid bank data" do

View File

@ -715,6 +715,35 @@ describe Quizzes::Quiz do
expect(possible).to eq 9.9
end
describe "learning outcome results" do
before :once do
build_course_quiz_questions_and_a_bank
@quiz.generate_quiz_data(persist: true)
@sub = @quiz.generate_submission(@user)
@sub.submission_data = {}
answer_a_question(@q1, @sub)
answer_a_question(@q2, @sub, correct: false)
Quizzes::SubmissionGrader.new(@sub).grade_submission
@quiz.reload
end
it "should destroy results if the quiz_type becomes practice_quiz" do
@quiz.quiz_type = "practice_quiz"
expect {
@quiz.save!
}.to change { LearningOutcomeResult.active.count }.by(-1)
end
it "should restore results if the quiz_type changes from practice_quiz" do
@quiz.quiz_type = "practice_quiz"
@quiz.save!
@quiz.quiz_type = "assignment"
expect {
@quiz.save!
}.to change { LearningOutcomeResult.active.count }.by(1)
end
end
describe "#generate_submission" do
it "should generate a valid submission for a given user" do
u = User.create!(:name => "some user")