canvas-lms/spec/models/quiz_submission_spec.rb

1501 lines
59 KiB
Ruby

# encoding: UTF-8
#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe QuizSubmission do
before(:each) do
course
@quiz = @course.quizzes.create!
end
it "should copy the quiz's points_possible whenever it's saved" do
Quiz.where(:id => @quiz).update_all(:points_possible => 1.1)
q = @quiz.quiz_submissions.create!
q.reload.quiz_points_possible.should eql 1.1
Quiz.where(:id => @quiz).update_all(:points_possible => 1.9)
q.reload.quiz_points_possible.should eql 1.1
q.save!
q.reload.quiz_points_possible.should eql 1.9
end
it "should not lose time" do
@quiz.update_attribute(:time_limit, 10)
q = @quiz.quiz_submissions.create!
q.update_attribute(:started_at, Time.now)
original_end_at = q.end_at
@quiz.update_attribute(:time_limit, 5)
@quiz.update_quiz_submission_end_at_times
q.reload
q.end_at.should eql original_end_at
end
describe "#update_scores" do
before(:each) do
student_in_course
assignment_quiz([])
qd = multiple_choice_question_data
@quiz.quiz_data = [qd]
@quiz.points_possible = qd[:points_possible]
@quiz.save!
end
it "should update scores for a completed submission" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
# sanity check
qs.reload
qs.score.should == 50
qs.kept_score.should == 50
qs.update_scores({:fudge_points => -5, :question_score_1 => 50})
qs.score.should == 45
qs.fudge_points.should == -5
qs.kept_score.should == 45
v = qs.versions.current.model
v.score.should == 45
v.fudge_points.should == -5
end
it "should not allow updating scores on an uncompleted submission" do
qs = @quiz.generate_submission(@student)
qs.should be_untaken
lambda { qs.update_scores }.should raise_error
end
it "should update scores for a previous submission" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "8544" }
qs.grade_submission
# sanity check
qs.score.should == 0
qs.kept_score.should == 0
qs.versions.count.should == 2
qs.update_scores({:submission_version_number => 1, :fudge_points => 10, :question_score_1 => 0})
qs.score.should == 0
qs.kept_score.should == 10
qs.versions.get(1).model.score.should == 10
qs.versions.current.model.score.should == 0
end
it "should allow updating scores on a completed version of a submission while the current version is in progress" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.backup_submission_data({ "question_1" => "" }) # simulate k/v pairs we store for quizzes in progress
qs.reload.attempt.should == 2
lambda { qs.update_scores }.should raise_error
lambda { qs.update_scores(:submission_version_number => 1, :fudge_points => 1, :question_score_1 => 0) }.should_not raise_error
qs.should be_untaken
qs.score.should be_nil
qs.kept_score.should == 1
v = qs.versions.current.model
v.score.should == 1
v.fudge_points.should == 1
end
it "should keep kept_score up-to-date when score changes while quiz is being re-taken" do
qs = @quiz.generate_submission(@user)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs.kept_score.should == 0
qs = @quiz.generate_submission(@user)
qs.backup_submission_data({ "foo" => "bar2" }) # simulate k/v pairs we store for quizzes in progress
qs.reload
qs.update_scores(:submission_version_number => 1, :fudge_points => 3)
qs.reload
qs.should be_untaken
# score is nil because the current attempt is still in progress
# but kept_score is 3 because that's the higher score of the previous attempt
qs.score.should be_nil
qs.kept_score.should == 3
end
end
it "should not allowed grading on an already-graded submission" do
q = @quiz.quiz_submissions.create!
q.workflow_state = "complete"
q.save!
q.workflow_state.should eql("complete")
q.state.should eql(:complete)
q.write_attribute(:submission_data, [])
res = false
begin
res = q.grade_submission
0.should eql(1)
rescue => e
e.to_s.should match(Regexp.new("Can't grade an already-submitted submission"))
end
res.should eql(false)
end
context "explicitly setting grade" do
before(:each) do
course_with_student
@quiz = @course.quizzes.create!
@quiz.generate_quiz_data
@quiz.published_at = Time.now
@quiz.workflow_state = 'available'
@quiz.scoring_policy == "keep_highest"
@quiz.save!
@assignment = @quiz.assignment
@quiz_sub = @quiz.generate_submission @user, false
@quiz_sub.workflow_state = "complete"
@quiz_sub.save!
@quiz_sub.score = 5
@quiz_sub.fudge_points = 0
@quiz_sub.kept_score = 5
@quiz_sub.with_versioning(true, &:save!)
@submission = @quiz_sub.submission
end
it "it should adjust the fudge points" do
@assignment.grade_student(@user, {:grade => 3})
@quiz_sub.reload
@quiz_sub.score.should == 3
@quiz_sub.kept_score.should == 3
@quiz_sub.fudge_points.should == -2
@quiz_sub.manually_scored.should_not be_true
@submission.reload
@submission.score.should == 3
@submission.grade.should == "3"
end
it "should use the explicit grade even if it isn't the highest score" do
@quiz_sub.score = 4.0
@quiz_sub.attempt = 2
@quiz_sub.with_versioning(true, &:save!)
@quiz_sub.reload
@quiz_sub.score.should == 4
@quiz_sub.kept_score.should == 5
@quiz_sub.manually_scored.should_not be_true
@submission.reload
@submission.score.should == 5
@submission.grade.should == "5"
@assignment.grade_student(@user, {:grade => 3})
@quiz_sub.reload
@quiz_sub.score.should == 3
@quiz_sub.kept_score.should == 3
@quiz_sub.fudge_points.should == -1
@quiz_sub.manually_scored.should be_true
@submission.reload
@submission.score.should == 3
@submission.grade.should == "3"
end
it "should not have manually_scored set when updated normally" do
@quiz_sub.score = 4.0
@quiz_sub.attempt = 2
@quiz_sub.with_versioning(true, &:save!)
@assignment.grade_student(@user, {:grade => 3})
@quiz_sub.reload
@quiz_sub.manually_scored.should be_true
@quiz_sub.update_scores(:fudge_points => 2)
@quiz_sub.reload
@quiz_sub.score.should == 2
@quiz_sub.kept_score.should == 5
@quiz_sub.manually_scored.should_not be_true
@submission.reload
@submission.score.should == 5
@submission.grade.should == "5"
end
it "should add a version to the submission" do
@assignment.grade_student(@user, {:grade => 3})
@submission.reload
@submission.versions.count.should == 2
@submission.score.should == 3
@assignment.grade_student(@user, {:grade => 6})
@submission.reload
@submission.versions.count.should == 3
@submission.score.should == 6
end
it "should only update the last completed quiz submission" do
@quiz_sub.score = 4.0
@quiz_sub.attempt = 2
@quiz_sub.with_versioning(true, &:save!)
@quiz.generate_submission(@user)
@assignment.grade_student(@user, {:grade => 3})
@quiz_sub.reload.score.should be_nil
@quiz_sub.kept_score.should == 3
@quiz_sub.manually_scored.should be_false
last_version = @quiz_sub.versions.current.reload.model
last_version.score.should == 3
last_version.manually_scored.should be_true
end
end
it "should know if it is overdue" do
now = Time.now
q = @quiz.quiz_submissions.new
q.end_at = now
q.save!
q.overdue?.should eql(false)
q.end_at = now - (3 * 60)
q.save!
q.overdue?.should eql(false)
q.overdue?(true).should eql(true)
q.end_at = now - (6 * 60)
q.save!
q.overdue?.should eql(true)
q.overdue?(true).should eql(true)
end
it "should know if it is extendable" do
@quiz.update_attribute(:time_limit, 10)
now = Time.now.utc
q = @quiz.quiz_submissions.new
q.end_at = now
q.extendable?.should be_true
q.end_at = now - 1.minute
q.extendable?.should be_true
q.end_at = now - 30.minutes
q.extendable?.should be_true
q.end_at = now - 90.minutes
q.extendable?.should be_false
end
it "should calculate score based on quiz scoring policy" do
q = @course.quizzes.create!(:scoring_policy => "keep_latest")
s = q.quiz_submissions.new
s.workflow_state = "complete"
s.score = 5.0
s.attempt = 1
s.with_versioning(true, &:save!)
s.score.should eql(5.0)
s.kept_score.should eql(5.0)
s.score = 4.0
s.attempt = 2
s.with_versioning(true, &:save!)
s.version_number.should eql(2)
s.kept_score.should eql(4.0)
q.update_attributes!(:scoring_policy => "keep_highest")
s.reload
s.score = 3.0
s.attempt = 3
s.with_versioning(true, &:save!)
s.kept_score.should eql(5.0)
s.update_scores(:submission_version_number => 2, :fudge_points => 6.0)
s.kept_score.should eql(6.0)
end
describe "with an essay question" do
before(:each) do
quiz_with_graded_submission([{:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'essay_question'}}]) do
{
"text_after_answers" => "",
"question_#{@questions[0].id}" => "<p>Lorem ipsum answer.</p>",
"context_id" => "#{@course.id}",
"context_type" => "Course",
"user_id" => "#{@user.id}",
"quiz_id" => "#{@quiz.id}",
"course_id" => "#{@course.id}",
"question_text" => "Lorem ipsum question",
}
end
end
it "should leave a submission in pending_review state if there are essay questions" do
@quiz_submission.submission.workflow_state.should eql 'pending_review'
end
it "should mark a submission as complete once an essay question has been graded" do
@quiz_submission.update_scores({
'context_id' => @course.id,
'override_scores' => true,
'context_type' => 'Course',
'submission_version_number' => '1',
"question_score_#{@questions[0].id}" => '1'
})
@quiz_submission.submission.workflow_state.should eql 'graded'
end
it "should increment the assignment needs_grading_count for pending_review state" do
@quiz.assignment.reload.needs_grading_count.should == 1
end
it "should not increment the assignmet needs_grading_count if graded when a second attempt starts" do
@quiz_submission.update_scores({
'context_id' => @course.id,
'override_scores' => true,
'context_type' => 'Course',
'submission_version_number' => '1',
"question_score_#{@questions[0].id}" => '1'
})
@quiz.assignment.reload.needs_grading_count.should == 0
@quiz.generate_submission(@user)
@quiz_submission.reload.should be_untaken
@quiz_submission.submission.should be_graded
@quiz.assignment.reload.needs_grading_count.should == 0
end
it "should not decrement the assignmet needs_grading_count if pending_review when a second attempt starts" do
@quiz.assignment.reload.needs_grading_count.should == 1
@quiz.generate_submission(@user)
@quiz_submission.reload.should be_untaken
@quiz_submission.submission.should be_pending_review
@quiz.assignment.reload.needs_grading_count.should == 1
end
end
describe "with multiple essay questions" do
before(:each) do
quiz_with_graded_submission([{:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'essay_question'}},
{:question_data => {:name => 'question 2', :points_possible => 1, 'question_type' => 'essay_question'}}]) do
{
"text_after_answers" => "",
"question_#{@questions[0].id}" => "<p>Lorem ipsum answer 1.</p>",
"question_#{@questions[1].id}" => "<p>Lorem ipsum answer 2.</p>",
"context_id" => "#{@course.id}",
"context_type" => "Course",
"user_id" => "#{@user.id}",
"quiz_id" => "#{@quiz.id}",
"course_id" => "#{@course.id}",
"question_text" => "Lorem ipsum question",
}
end
end
it "should not mark a submission complete if there are essay questions without grades" do
@quiz_submission.update_scores({
'context_id' => @course.id,
'override_scores' => true,
'context_type' => 'Course',
'submission_version_number' => '1',
"question_score_#{@questions[0].id}" => '1',
"question_score_#{@questions[1].id}" => "--"
})
@quiz_submission.submission.workflow_state.should eql 'pending_review'
end
it "should mark a submission complete if all essay questions have been graded" do
@quiz_submission.update_scores({
'context_id' => @course.id,
'override_scores' => true,
'context_type' => 'Course',
'submission_version_number' => '1',
"question_score_#{@questions[0].id}" => '1',
"question_score_#{@questions[1].id}" => "0"
})
@quiz_submission.submission.workflow_state.should eql 'graded'
end
end
describe "formula questions" do
before do
@quiz = @course.quizzes.create!(:title => "formula quiz")
@quiz.quiz_questions.create! :question_data => {
:name => "Question",
:question_type => "calculated_question",
:answer_tolerance => 2.0,
:formulas => [[0, "2*z"]],
:variables => [["variable_0", {:scale => 0, :min => 1.0, :max => 10.0, :name => 'z'}]],
:answers => [["answer_0", {
:weight => 100,
:variables => [["variable_0", {:value => 2.0, :name => 'z'}]],
:answer_text => "4.0"
}]],
:question_text => "2 * [z] is ?"
}
@quiz.generate_quiz_data(:persist => true)
end
it "should respect the answer_tolerance" do
submission = @quiz.generate_submission(@user)
submission.submission_data = {
"question_#{@quiz.quiz_questions.first.id}" => 3.0, # off by 1
}
submission.grade_submission
submission.instance_variable_get(:@user_answers).first[:correct].should be_true
end
end
it "should update associated submission" do
c = factory_with_protected_attributes(Course, :workflow_state => "active")
a = c.assignments.new(:title => "some assignment")
a.workflow_state = "available"
a.save!
u = User.new
u.workflow_state = "registered"
u.save!
c.enroll_student(u)
s = a.submit_homework(u)
quiz = c.quizzes.create!
q = quiz.quiz_submissions.new
q.submission_id = s.id
q.user_id = u.id
q.workflow_state = "complete"
q.score = 5.0
q.save!
q.kept_score.should eql(5.0)
s.reload
s.score.should eql(5.0)
end
describe "learning outcomes" do
it "should create learning outcome results when aligned to assessment questions" do
course_with_student(:active_all => true)
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
@q1 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => {'answer_0' => {'answer_text' => '1', 'answer_weight' => '100'}, 'answer_1' => {'answer_text' => '2'}, 'answer_2' => {'answer_text' => '3'},'answer_3' => {'answer_text' => '4'}}})
@q2 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 2', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => {'answer_0' => {'answer_text' => '1', 'answer_weight' => '100'}, 'answer_1' => {'answer_text' => '2'}, 'answer_2' => {'answer_text' => '3'},'answer_3' => {'answer_text' => '4'}}})
@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)
@bank.learning_outcome_alignments.length.should eql(1)
@q2.assessment_question.assessment_question_bank.should eql(@bank)
answer_1 = @q1.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
answer_2 = @q2.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
@quiz.generate_quiz_data(:persist => true)
@sub = @quiz.generate_submission(@user)
@sub.submission_data = {}
question_1 = @q1.question_data[:id]
question_2 = @q2.question_data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1
@sub.grade_submission
@sub.score.should eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.find_all_by_user_id(@user.id)
@results.length.should eql(2)
@results = @results.sort_by(&:associated_asset_id)
@results.first.associated_asset.should eql(@q1.assessment_question)
@results.first.mastery.should eql(true)
@results.last.associated_asset.should eql(@q2.assessment_question)
@results.last.mastery.should eql(false)
end
it "should update learning outcome results when aligned to assessment questions" do
course_with_student(:active_all => true)
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
@q1 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => {'answer_0' => {'answer_text' => '1', 'answer_weight' => '100'}, 'answer_1' => {'answer_text' => '2'}, 'answer_2' => {'answer_text' => '3'},'answer_3' => {'answer_text' => '4'}}})
@q2 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 2', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => {'answer_0' => {'answer_text' => '1', 'answer_weight' => '100'}, 'answer_1' => {'answer_text' => '2'}, 'answer_2' => {'answer_text' => '3'},'answer_3' => {'answer_text' => '4'}}})
@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)
@bank.learning_outcome_alignments.length.should eql(1)
@q2.assessment_question.assessment_question_bank.should eql(@bank)
answer_1 = @q1.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
answer_2 = @q2.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
@quiz.generate_quiz_data(:persist => true)
@sub = @quiz.generate_submission(@user)
@sub.submission_data = {}
question_1 = @q1.question_data[:id]
question_2 = @q2.question_data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1
@sub.grade_submission
@sub.score.should eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.find_all_by_user_id(@user.id)
@results.length.should eql(2)
@results = @results.sort_by(&:associated_asset_id)
@results.first.associated_asset.should eql(@q1.assessment_question)
@results.first.mastery.should eql(true)
@results.last.associated_asset.should eql(@q2.assessment_question)
@results.last.mastery.should eql(false)
@sub = @quiz.generate_submission(@user)
@sub.attempt.should eql(2)
@sub.submission_data = {}
question_1 = @q1.question_data[:id]
question_2 = @q2.question_data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1 + 1
@sub.submission_data["question_#{question_2}"] = answer_2
@sub.grade_submission
@sub.score.should eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.find_all_by_user_id(@user.id)
@results.length.should eql(2)
@results = @results.sort_by(&:associated_asset_id)
@results.first.associated_asset.should eql(@q1.assessment_question)
@results.first.mastery.should eql(false)
@results.first.original_mastery.should eql(true)
@results.last.associated_asset.should eql(@q2.assessment_question)
@results.last.mastery.should eql(true)
@results.last.original_mastery.should eql(false)
end
end
describe ".score_question" do
it "should score a multiple_choice_question" do
qd = multiple_choice_question_data
QuizSubmission.score_question(qd, { "question_1" => "1658" }).should ==
{ :question_id => 1, :correct => true, :points => 50, :answer_id => 1658, :text => "1658" }
QuizSubmission.score_question(qd, { "question_1" => "8544" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :answer_id => 8544, :text => "8544" }
QuizSubmission.score_question(qd, { "question_1" => "5" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "5" }
QuizSubmission.score_question(qd, {}).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "" }
QuizSubmission.score_question(qd, { "undefined_if_blank" => "1" }).should ==
{ :question_id => 1, :correct => "undefined", :points => 0, :text => "" }
end
it "should score a true_false_question" do
qd = true_false_question_data
QuizSubmission.score_question(qd, { "question_1" => "8950" }).should ==
{ :question_id => 1, :correct => true, :points => 45, :answer_id => 8950, :text => "8950" }
QuizSubmission.score_question(qd, { "question_1" => "8403" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :answer_id => 8403, :text => "8403" }
QuizSubmission.score_question(qd, { "question_1" => "5" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "5" }
QuizSubmission.score_question(qd, {}).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "" }
QuizSubmission.score_question(qd, { "undefined_if_blank" => "1" }).should ==
{ :question_id => 1, :correct => "undefined", :points => 0, :text => "" }
end
it "should score a short_answer_question (Fill In The Blank)" do
qd = short_answer_question_data
QuizSubmission.score_question(qd, { "question_1" => "stupid" }).should ==
{ :question_id => 1, :correct => true, :points => 16.5, :answer_id => 7100, :text => "stupid" }
QuizSubmission.score_question(qd, { "question_1" => " DUmB\n " }).should ==
{ :question_id => 1, :correct => true, :points => 16.5, :answer_id => 2159, :text => " DUmB\n " }
QuizSubmission.score_question(qd, { "question_1" => "short" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "short" }
# Blank answer from quiz taker should not match even if blank answer is on quiz (blanks created by default)
qd_with_blank = short_answer_question_data_one_blank
QuizSubmission.score_question(qd_with_blank, { "question_1" => "" }).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "" }
QuizSubmission.score_question(qd, {}).should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "" }
# Preserve idea of "undefined" for when student wasn't asked the question (ie no response) as separate from
# student was asked but didn't answer. Can happen when instructor adds question to a quiz that a student has
# already started or completed.
QuizSubmission.score_question(qd, { "undefined_if_blank" => "1" }).should ==
{ :question_id => 1, :correct => 'undefined', :points => 0, :text => "" }
end
it "should score an essay_question" do
qd = essay_question_data
text = "that's too <b>dang</b> hard! <script>alert(1)</script>"
sanitized = "that's too <b>dang</b> hard! alert(1)"
QuizSubmission.score_question(qd, { "question_1" => text }).should ==
{ :question_id => 1, :correct => "undefined", :points => 0, :text => sanitized }
QuizSubmission.score_question(qd, {}).should ==
{ :question_id => 1, :correct => "undefined", :points => 0, :text => "" }
end
it "should score a text_only_question" do
QuizSubmission.score_question(text_only_question_data, {}).should ==
{ :question_id => 3, :correct => "no_score", :points => 0, :text => "" }
end
it "should score a matching_question" do
q = matching_question_data
# 1 wrong answer
user_answer = QuizSubmission.score_question(q, {
"question_1_answer_7396" => "3562",
"question_1_answer_6081" => "3855",
"question_1_answer_4224" => "1397",
"question_1_answer_7397" => "6067",
"question_1_answer_7398" => "6068",
"question_1_answer_7399" => "6069",
})
user_answer.delete(:points).should be_close(41.67, 0.01)
user_answer.should == {
:question_id => 1, :correct => "partial", :text => "",
:answer_7396 => "3562",
:answer_6081 => "3855",
:answer_4224 => "1397",
:answer_7397 => "6067",
:answer_7398 => "6068",
:answer_7399 => "6069",
}
# 1 wrong answer but no partial credit allowed
user_answer = QuizSubmission.score_question(q.merge(:allow_partial_credit => false), {
"question_1_answer_7396" => "3562",
"question_1_answer_6081" => "3855",
"question_1_answer_4224" => "1397",
"question_1_answer_7397" => "6067",
"question_1_answer_7398" => "6068",
"question_1_answer_7399" => "6069",
})
user_answer.should == {
:question_id => 1, :correct => false, :points => 0, :text => "",
:answer_7396 => "3562",
:answer_6081 => "3855",
:answer_4224 => "1397",
:answer_7397 => "6067",
:answer_7398 => "6068",
:answer_7399 => "6069",
}
# all wrong answers
user_answer = QuizSubmission.score_question(q, {
"question_1_answer_7396" => "3562",
"question_1_answer_6081" => "1500",
"question_1_answer_4224" => "8513",
})
user_answer.should == {
:question_id => 1, :correct => false, :points => 0, :text => "",
:answer_7396 => "3562",
:answer_6081 => "1500",
:answer_4224 => "8513",
:answer_7397 => "",
:answer_7398 => "",
:answer_7399 => "",
}
user_answer = QuizSubmission.score_question(q, {
"question_1_answer_7396" => "6061",
"question_1_answer_6081" => "3855",
"question_1_answer_4224" => "1397",
"question_1_answer_7397" => "6067",
"question_1_answer_7398" => "6068",
"question_1_answer_7399" => "6069",
})
user_answer.should == {
:question_id => 1, :correct => true, :points => 50, :text => "",
:answer_7396 => "6061",
:answer_6081 => "3855",
:answer_4224 => "1397",
:answer_7397 => "6067",
:answer_7398 => "6068",
:answer_7399 => "6069",
}
# selected a different answer but the text of that answer was the same
user_answer = QuizSubmission.score_question(q, {
"question_1_answer_7396" => "1397",
"question_1_answer_6081" => "3855",
"question_1_answer_4224" => "1397",
"question_1_answer_7397" => "6067",
"question_1_answer_7398" => "6068",
"question_1_answer_7399" => "6069",
})
user_answer.should == {
:question_id => 1, :correct => true, :points => 50, :text => "",
:answer_7396 => "6061",
:answer_6081 => "3855",
:answer_4224 => "1397",
:answer_7397 => "6067",
:answer_7398 => "6068",
:answer_7399 => "6069",
}
# no answer shouldn't be treated as a blank string, breaking undefined_if_blank
QuizSubmission.score_question(q, { "undefined_if_blank" => "1" }).should == {
:question_id => 1, :correct => "undefined", :points => 0, :text => "",
:answer_7396 => "",
:answer_6081 => "",
:answer_4224 => "",
:answer_7397 => "",
:answer_7398 => "",
:answer_7399 => "",
}
end
it "should score a numerical_question" do
qd = numerical_question_data
QuizSubmission.score_question(qd, { "question_1" => "3.2" }).should == {
:question_id => 1, :correct => false, :points => 0, :text => "3.2" }
QuizSubmission.score_question(qd, { "question_1" => "4" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "4", :answer_id => 9222 }
QuizSubmission.score_question(qd, { "question_1" => "-4" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-4", :answer_id => 997 }
QuizSubmission.score_question(qd, { "question_1" => "4.05" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "4.05", :answer_id => 9370 }
QuizSubmission.score_question(qd, { "question_1" => "4.10" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "4.10", :answer_id => 9370 }
QuizSubmission.score_question(qd, { "question_1" => "3.90" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "3.90", :answer_id => 9370 }
QuizSubmission.score_question(qd, { "question_1" => "-4.1" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-4.1", :answer_id => 5450 }
QuizSubmission.score_question(qd, { "question_1" => "-3.9" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-3.9", :answer_id => 5450 }
QuizSubmission.score_question(qd, { "question_1" => "-4.05" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-4.05", :answer_id => 5450 }
QuizSubmission.score_question(qd, { "question_1" => "" }).should == {
:question_id => 1, :correct => false, :points => 0, :text => "" }
QuizSubmission.score_question(qd, { :undefined_if_blank => "1" }).should == {
:question_id => 1, :correct => "undefined", :points => 0, :text => "" }
# blank answer should not be treated as 0.0
qd2 = qd.dup
qd2["answers"] << { "exact" => 0, "numerical_answer_type" => "exact_answer", "margin" => 0, "weight" => 100, "id" => 1234 }
QuizSubmission.score_question(qd2, { "question_1" => "" }).should == {
:question_id => 1, :correct => false, :points => 0, :text => "" }
end
it "should score a calculated_question" do
qd = calculated_question_data
QuizSubmission.score_question(qd, { "question_1" => "-11.7" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-11.7", :answer_id => 6396 }
QuizSubmission.score_question(qd, { "question_1" => "-11.68" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-11.68", :answer_id => 6396 }
QuizSubmission.score_question(qd, { "question_1" => "-11.675" }).should == {
:question_id => 1, :correct => false, :points => 0, :text => "-11.675" }
QuizSubmission.score_question(qd, {}).should == {
:question_id => 1, :correct => false, :points => 0, :text => "" }
QuizSubmission.score_question(qd, { "question_1" => "-11.72" }).should == {
:question_id => 1, :correct => true, :points => 26.2, :text => "-11.72", :answer_id => 6396 }
end
it "should score a multiple_answers_question" do
qd = multiple_answers_question_data
QuizSubmission.score_question(qd, {
"question_1_answer_9761" => "1",
"question_1_answer_3079" => "0",
"question_1_answer_5194" => "1",
"question_1_answer_166" => "1",
"question_1_answer_4739" => "0",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "1",
"question_1_answer_7381" => "0",
}).should == {
:question_id => 1, :correct => true, :points => 50, :text => "",
:answer_9761 => "1",
:answer_3079 => "0",
:answer_5194 => "1",
:answer_166 => "1",
:answer_4739 => "0",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "1",
:answer_7381 => "0",
}
# partial credit
user_answer = QuizSubmission.score_question(qd, {
"question_1_answer_9761" => "1",
"question_1_answer_3079" => "0",
"question_1_answer_5194" => "1",
"question_1_answer_166" => "1",
"question_1_answer_4739" => "1",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "1",
"question_1_answer_7381" => "0",
})
user_answer.delete(:points).should be_close(41.67, 0.01)
user_answer.should == {
:question_id => 1, :correct => "partial", :text => "",
:answer_9761 => "1",
:answer_3079 => "0",
:answer_5194 => "1",
:answer_166 => "1",
:answer_4739 => "1",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "1",
:answer_7381 => "0",
}
user_answer = QuizSubmission.score_question(qd.merge(:allow_partial_credit => false), {
"question_1_answer_9761" => "1",
"question_1_answer_3079" => "0",
"question_1_answer_5194" => "1",
"question_1_answer_166" => "1",
"question_1_answer_4739" => "1",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "1",
"question_1_answer_7381" => "0",
})
user_answer.should == {
:question_id => 1, :correct => false, :points => 0, :text => "",
:answer_9761 => "1",
:answer_3079 => "0",
:answer_5194 => "1",
:answer_166 => "1",
:answer_4739 => "1",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "1",
:answer_7381 => "0",
}
# checking one that shouldn't be checked, subtracts one correct answer's worth of points
user_answer = QuizSubmission.score_question(qd, {
"question_1_answer_9761" => "1",
"question_1_answer_3079" => "0",
"question_1_answer_5194" => "0",
"question_1_answer_166" => "1",
"question_1_answer_4739" => "1",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "0",
"question_1_answer_7381" => "0",
})
user_answer.delete(:points).should be_close(25.0, 0.01)
user_answer.should == {
:question_id => 1, :correct => "partial", :text => "",
:answer_9761 => "1",
:answer_3079 => "0",
:answer_5194 => "0",
:answer_166 => "1",
:answer_4739 => "1",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "0",
:answer_7381 => "0",
}
# can't get less than 0
user_answer = QuizSubmission.score_question(qd, {
"question_1_answer_9761" => "0",
"question_1_answer_3079" => "1",
"question_1_answer_5194" => "0",
"question_1_answer_166" => "0",
"question_1_answer_4739" => "1",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "0",
"question_1_answer_7381" => "1",
})
user_answer.should == {
:question_id => 1, :correct => false, :points => 0, :text => "",
:answer_9761 => "0",
:answer_3079 => "1",
:answer_5194 => "0",
:answer_166 => "0",
:answer_4739 => "1",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "0",
:answer_7381 => "1",
}
# incorrect_dock allows a different value to be subtracted on incorrect answer
# this isn't exposed in the UI anywhere yet, but the code supports it
user_answer = QuizSubmission.score_question(qd.merge(:incorrect_dock => 1.5), {
"question_1_answer_9761" => "1",
"question_1_answer_3079" => "0",
"question_1_answer_5194" => "0",
"question_1_answer_166" => "1",
"question_1_answer_4739" => "1",
"question_1_answer_2196" => "1",
"question_1_answer_8982" => "1",
"question_1_answer_9701" => "0",
"question_1_answer_7381" => "0",
})
user_answer.delete(:points).should be_close(31.83, 0.01)
user_answer.should == {
:question_id => 1, :correct => "partial", :text => "",
:answer_9761 => "1",
:answer_3079 => "0",
:answer_5194 => "0",
:answer_166 => "1",
:answer_4739 => "1",
:answer_2196 => "1",
:answer_8982 => "1",
:answer_9701 => "0",
:answer_7381 => "0",
}
QuizSubmission.score_question(qd, { "undefined_if_blank" => "1" }).should ==
{ :question_id => 1, :correct => "undefined", :points => 0, :text => "" }
end
it "should score a multiple_dropdowns_question" do
q = multiple_dropdowns_question_data
user_answer = QuizSubmission.score_question(q, { "question_1630873_4e6185159bea49c4d29047379b400ad5"=>"6994", "question_1630873_3f507e80e33ef092a02948a064433ec5"=>"5988", "question_1630873_78635a3709b540a59678c806b102d038"=>"9908", "question_1630873_657b11f1c17376f178c4d80c4c25d0ab"=>"1121", "question_1630873_02c8346333761ffe9bbddee7b1c5a537"=>"4390", "question_1630873_1865cbc77c83d7571ed8b3a108d11d3d"=>"7604", "question_1630873_94239fc44b4f8aaf36bd3596768f4816"=>"6955", "question_1630873_cd073d17d0d9558fb2be7d7bf9a1c840"=>"3353", "question_1630873_69d0969351d989767d7096f28daf7461"=>"3390"})
user_answer.delete(:points).should be_close(0.44, 0.01)
user_answer.should == {
:question_id => 1630873, :correct => "partial", :text => "",
:answer_for_structure1 => 4390,
:answer_id_for_structure1 => 4390,
:answer_for_event1 => 3390,
:answer_id_for_event1 => 3390,
:answer_for_structure2 => 6955,
:answer_id_for_structure2 => 6955,
:answer_for_structure3 => 5988,
:answer_id_for_structure3 => 5988,
:answer_for_structure4 => 7604,
:answer_id_for_structure4 => 7604,
:answer_for_event2 => 3353,
:answer_id_for_event2 => 3353,
:answer_for_structure5 => 9908,
:answer_id_for_structure5 => 9908,
:answer_for_structure6 => 6994,
:answer_id_for_structure6 => 6994,
:answer_for_structure7 => 1121,
:answer_id_for_structure7 => 1121,
}
user_answer = QuizSubmission.score_question(q, { "question_1630873_4e6185159bea49c4d29047379b400ad5"=>"1883", "question_1630873_3f507e80e33ef092a02948a064433ec5"=>"5988", "question_1630873_78635a3709b540a59678c806b102d038"=>"878", "question_1630873_657b11f1c17376f178c4d80c4c25d0ab"=>"9570", "question_1630873_02c8346333761ffe9bbddee7b1c5a537"=>"1522", "question_1630873_1865cbc77c83d7571ed8b3a108d11d3d"=>"9532", "question_1630873_94239fc44b4f8aaf36bd3596768f4816"=>"1228", "question_1630873_cd073d17d0d9558fb2be7d7bf9a1c840"=>"599", "question_1630873_69d0969351d989767d7096f28daf7461"=>"5498"})
user_answer.should == {
:question_id => 1630873, :correct => false, :points => 0, :text => "",
:answer_for_structure1 => 1522,
:answer_id_for_structure1 => 1522,
:answer_for_event1 => 5498,
:answer_id_for_event1 => 5498,
:answer_for_structure2 => 1228,
:answer_id_for_structure2 => 1228,
:answer_for_structure3 => 5988,
:answer_id_for_structure3 => 5988,
:answer_for_structure4 => 9532,
:answer_id_for_structure4 => 9532,
:answer_for_event2 => 599,
:answer_id_for_event2 => 599,
:answer_for_structure5 => 878,
:answer_id_for_structure5 => 878,
:answer_for_structure6 => 1883,
:answer_id_for_structure6 => 1883,
:answer_for_structure7 => 9570,
:answer_id_for_structure7 => 9570,
}
user_answer = QuizSubmission.score_question(q, { "question_1630873_4e6185159bea49c4d29047379b400ad5"=>"6994", "question_1630873_3f507e80e33ef092a02948a064433ec5"=>"7676", "question_1630873_78635a3709b540a59678c806b102d038"=>"9908", "question_1630873_657b11f1c17376f178c4d80c4c25d0ab"=>"1121", "question_1630873_02c8346333761ffe9bbddee7b1c5a537"=>"4390", "question_1630873_1865cbc77c83d7571ed8b3a108d11d3d"=>"7604", "question_1630873_94239fc44b4f8aaf36bd3596768f4816"=>"6955", "question_1630873_cd073d17d0d9558fb2be7d7bf9a1c840"=>"3353", "question_1630873_69d0969351d989767d7096f28daf7461"=>"3390"})
user_answer.should == {
:question_id => 1630873, :correct => true, :points => 0.5, :text => "",
:answer_for_structure1 => 4390,
:answer_id_for_structure1 => 4390,
:answer_for_event1 => 3390,
:answer_id_for_event1 => 3390,
:answer_for_structure2 => 6955,
:answer_id_for_structure2 => 6955,
:answer_for_structure3 => 7676,
:answer_id_for_structure3 => 7676,
:answer_for_structure4 => 7604,
:answer_id_for_structure4 => 7604,
:answer_for_event2 => 3353,
:answer_id_for_event2 => 3353,
:answer_for_structure5 => 9908,
:answer_id_for_structure5 => 9908,
:answer_for_structure6 => 6994,
:answer_id_for_structure6 => 6994,
:answer_for_structure7 => 1121,
:answer_id_for_structure7 => 1121,
}
end
it "should score a fill_in_multiple_blanks_question" do
q = fill_in_multiple_blanks_question_data
user_answer = QuizSubmission.score_question(q, {
"question_1_8238a0de6965e6b81a8b9bba5eacd3e2" => "control",
"question_1_a95fbffb573485f87b8c8aca541f5d4e" => "patrol",
"question_1_3112b644eec409c20c346d2a393bd45e" => "soul",
"question_1_fb1b03eb201132f7c1a5824cf9ebecb7" => "toll",
"question_1_90811a00aaf122ea20ab5c28be681ac9" => "assplode",
"question_1_ce36b05cfdedbc990a188907fc29d37b" => "old",
})
user_answer.should ==
{ :question_id => 1, :correct => true, :points => 50.0, :text => "",
:answer_for_answer1 => "control",
:answer_id_for_answer1 => 3950,
:answer_for_answer2 => "patrol",
:answer_id_for_answer2 => 9181,
:answer_for_answer3 => "soul",
:answer_id_for_answer3 => 3733,
:answer_for_answer4 => "toll",
:answer_id_for_answer4 => 7829,
:answer_for_answer5 => "assplode",
:answer_id_for_answer5 => 5301,
:answer_for_answer6 => "old",
:answer_id_for_answer6 => 3367,
}
user_answer = QuizSubmission.score_question(q, {
"question_1_8238a0de6965e6b81a8b9bba5eacd3e2" => "control",
"question_1_a95fbffb573485f87b8c8aca541f5d4e" => "patrol",
"question_1_3112b644eec409c20c346d2a393bd45e" => "soul",
"question_1_fb1b03eb201132f7c1a5824cf9ebecb7" => "toll",
"question_1_90811a00aaf122ea20ab5c28be681ac9" => "wut",
"question_1_ce36b05cfdedbc990a188907fc29d37b" => "old",
})
user_answer.delete(:points).should be_close(41.6, 0.1)
user_answer.should ==
{ :question_id => 1, :correct => "partial", :text => "",
:answer_for_answer1 => "control",
:answer_id_for_answer1 => 3950,
:answer_for_answer2 => "patrol",
:answer_id_for_answer2 => 9181,
:answer_for_answer3 => "soul",
:answer_id_for_answer3 => 3733,
:answer_for_answer4 => "toll",
:answer_id_for_answer4 => 7829,
:answer_for_answer5 => "wut",
:answer_id_for_answer5 => nil,
:answer_for_answer6 => "old",
:answer_id_for_answer6 => 3367,
}
user_answer = QuizSubmission.score_question(q, {
"question_1_a95fbffb573485f87b8c8aca541f5d4e" => "0",
"question_1_3112b644eec409c20c346d2a393bd45e" => "fail",
"question_1_fb1b03eb201132f7c1a5824cf9ebecb7" => "wrong",
"question_1_90811a00aaf122ea20ab5c28be681ac9" => "wut",
"question_1_ce36b05cfdedbc990a188907fc29d37b" => "oh well",
})
user_answer.should ==
{ :question_id => 1, :correct => false, :points => 0, :text => "",
:answer_for_answer1 => "",
:answer_id_for_answer1 => nil,
:answer_for_answer2 => "0",
:answer_id_for_answer2 => nil,
:answer_for_answer3 => "fail",
:answer_id_for_answer3 => nil,
:answer_for_answer4 => "wrong",
:answer_id_for_answer4 => nil,
:answer_for_answer5 => "wut",
:answer_id_for_answer5 => nil,
:answer_for_answer6 => "oh well",
:answer_id_for_answer6 => nil,
}
# one blank to fill in
user_answer = QuizSubmission.score_question(fill_in_multiple_blanks_question_one_blank_data, { "question_2_10ca8479f89652b254a5c6ec90ab9ab8" => " DUmB \n " })
user_answer.should ==
{ :question_id => 2, :correct => true, :points => 3.75, :text => "",
:answer_for_myblank => "dumb",
:answer_id_for_myblank => 1235, }
user_answer = QuizSubmission.score_question(fill_in_multiple_blanks_question_one_blank_data, { "question_2_10ca8479f89652b254a5c6ec90ab9ab8" => "wut" })
user_answer.should ==
{ :question_id => 2, :correct => false, :points => 0, :text => "",
:answer_for_myblank => "wut",
:answer_id_for_myblank => nil, }
end
it "should score an unknown question type" do
# if a question with an invalid type makes it into the quiz data, we
# score it as always 0 out of points_possible, rather than raise an error
qd = {"name"=>"Question 1", "question_type"=>"Error", "assessment_question_id"=>nil, "migration_id"=>"i1234", "id"=>2, "points_possible"=>5.35, "question_name"=>"Question 1", "qti_error"=>"There was an error exporting an assessment question - No question type used when trying to parse a qti question", "question_text"=>"test1", "answers"=>[], "assessment_question_migration_id"=>"i1234"}.with_indifferent_access
user_answer = QuizSubmission.score_question(qd, {})
user_answer.should ==
{ :question_id => 2, :correct => false, :points => 0, :text => "", }
end
it "should not escape user responses in fimb questions" do
course_with_student(:active_all => true)
q = {:neutral_comments=>"",
:position=>1,
:question_name=>"Question 1",
:correct_comments=>"",
:answers=>
[{:comments=>"",
:blank_id=>"answer1",
:weight=>100,
:text=>"control",
:id=>3950},
{:comments=>"",
:blank_id=>"answer1",
:weight=>100,
:text=>"controll",
:id=>9177}],
:points_possible=>50,
:question_type=>"fill_in_multiple_blanks_question",
:assessment_question_id=>7903,
:name=>"Question 1",
:question_text=>
"<p><span>Ayo my quality [answer1]</p>",
:id=>1,
:incorrect_comments=>""}
user_answer = QuizSubmission.score_question(q, {
"question_1_#{AssessmentQuestion.variable_id("answer1")}" => "<>&\""
})
user_answer[:answer_for_answer1].should == "<>&\""
end
it "should not fail if fimb question doesn't have any answers" do
course_with_student(:active_all => true)
# @quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
q = {:position=>1, :name=>"Question 1", :correct_comments=>"", :question_type=>"fill_in_multiple_blanks_question", :assessment_question_id=>7903, :incorrect_comments=>"", :neutral_comments=>"", :id=>1, :points_possible=>50, :question_name=>"Question 1", :answers=>[], :question_text=>"<p><span>Ayo my quality [answer1].</p>"}
lambda {
QuizSubmission.score_question(q, { "question_1_8238a0de6965e6b81a8b9bba5eacd3e2" => "bleh" })
}.should_not raise_error
end
end
describe "#score_to_keep" do
before(:each) do
student_in_course
assignment_quiz([])
qd = multiple_choice_question_data
@quiz.quiz_data = [qd]
@quiz.points_possible = qd[:points_possible]
@quiz.save!
end
context "keep_highest" do
before(:each) do
@quiz.scoring_policy = "keep_highest"
@quiz.save!
end
it "should be nil during first in-progress submission" do
qs = @quiz.generate_submission(@student)
qs.score_to_keep.should be_nil
end
it "should be the submission score for one complete submission" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs.score_to_keep.should == @quiz.points_possible
end
it "should be correct for multiple complete versions" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs.score_to_keep.should == @quiz.points_possible
end
it "should be correct for multiple versions, current version in progress" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.score_to_keep.should == @quiz.points_possible
end
end
context "keep_latest" do
before(:each) do
@quiz.scoring_policy = "keep_latest"
@quiz.save!
end
it "should be nil during first in-progress submission" do
qs = @quiz.generate_submission(@student)
qs.score_to_keep.should be_nil
end
it "should be the submission score for one complete submission" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs.score_to_keep.should == @quiz.points_possible
end
it "should be correct for multiple complete versions" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs.score_to_keep.should == 0
end
it "should be correct for multiple versions, current version in progress" do
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "1658" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.submission_data = { "question_1" => "2405" }
qs.grade_submission
qs = @quiz.generate_submission(@student)
qs.score_to_keep.should == 0
end
end
end
context "permissions" do
it "should allow read to observers" do
course_with_student(:active_all => true)
@observer = user
oe = @course.enroll_user(@observer, 'ObserverEnrollment', :enrollment_state => 'active')
oe.update_attribute(:associated_user, @user)
@quiz = @course.quizzes.create!
qs = @quiz.generate_submission(@user)
qs.grants_right?(@observer, nil, :read).should be_true
end
end
describe "question" do
let(:submission) { QuizSubmission.new }
let(:question1) { {:id => 1} }
let(:question2) { {:id => 2} }
let(:questions) { [question1, question2] }
before do
submission.stubs(:questions).returns(questions)
end
it "returns the question matching the passed in ID" do
submission.question(1).should == question1
end
it "casts the ID to an integer" do
submission.question('2').should == question2
end
it "returns nil when not found" do
submission.question(3).should be_nil
end
describe "has_question?" do
it "returns true when it has a question identified by the ID" do
submission.has_question?(1).should be_true
end
it "returns false when the question cannot be found" do
submission.has_question?(3).should be_false
end
end
end
describe "question_answered?" do
let(:submission) { QuizSubmission.new }
before do
submission.stubs(:temporary_data).returns \
'question_1' => 'A',
'question_2' => '',
'question_3_123456abcdefghijklmnopqrstuvwxyz' => 'A',
'question_3_654321abcdefghijklmnopqrstuvwxyz' => 'B',
'question_4_123456abcdefghijklmnopqrstuvwxyz' => 'A',
'question_4_654321abcdefghijklmnopqrstuvwxyz' => '',
'question_5_123456abcdefghijklmnopqrstuvwxyz' => '',
'question_5_654321abcdefghijklmnopqrstuvwxyz' => '',
'question_6_answer_5231'=>'7700',
'question_6_answer_3055'=>'3037',
'question_6_answer_7094'=>'9976',
'question_6_answer_6346'=>'6392',
'question_7_answer_5231'=>'7700',
'question_7_answer_3055'=>'',
'question_7_answer_7094'=>'9976',
'question_7_answer_6346'=>'',
'question_8_answer_123' => '0',
'question_8_answer_234' => '0',
'question_8_answer_345' => '0',
'question_9_answer_123' => '0',
'question_9_answer_234' => '1',
'question_9_answer_345' => '1'
end
context "on a single answer question" do
context "when answered" do
it "returns true" do
submission.question_answered?(1).should be_true
end
end
context "when not answered" do
it "returns false" do
submission.question_answered?(2).should be_false
end
end
end
context "on a fill in multiple blanks question" do
context "when all answered" do
it "returns true" do
submission.question_answered?(3).should be_true
end
end
context "when some answered" do
it "returns false" do
submission.question_answered?(4).should be_false
end
end
context "when none answered" do
it "returns false" do
submission.question_answered?(5).should be_false
end
end
end
context "on a matching question" do
context "when all answered" do
it "returns true" do
submission.question_answered?(6).should be_true
end
end
context "when some answered" do
it "returns false" do
submission.question_answered?(7).should be_false
end
end
end
context "on a multiple answers question" do
context "when none answered" do
it "returns false" do
submission.question_answered?(8).should be_false
end
end
context "when answers selected" do
it "returns true" do
submission.question_answered?(9).should be_true
end
end
end
context "with no response recorded yet" do
it "returns false" do
submission.question_answered?(100).should be_false
end
end
end
describe "update_submission_version" do
let(:submission) { QuizSubmission.new }
before do
submission.with_versioning(true) do |s|
s.score = 10
s.save_without_validation
end
submission.version_number.should == 1
submission.with_versioning(true) do |s|
s.score = 15
s.save_without_validation
end
submission.version_number.should == 2
end
it "updates a previous version given current attributes" do
vs = submission.versions
vs.size.should == 2
submission.score = 25
submission.update_submission_version(vs.last, [:score])
submission.versions.map{ |s| s.model.score }.should == [15, 25]
end
context "when loading UTF-8 data" do
it "should strip bad chars" do
vs = submission.versions
# inject bad byte into yaml
submission.submission_data = ["bad\x81byte"]
submission.update_submission_version(vs.last, [:submission_data])
# reload yaml by setting a different column
submission.score = 20
submission.update_submission_version(vs.last, [:score])
submission.versions.map{ |s| s.model.submission_data }.should == [nil, ["badbyte"]]
end
end
end
describe 'broadcast policy' do
before do
Notification.create(:name => 'Submission Graded')
Notification.create(:name => 'Submission Grade Changed')
student_in_course
assignment_quiz([])
@course.enroll_student(@student)
@assignment.publish!
@submission = @quiz.generate_submission(@student)
end
it 'sends a graded notification after grading the quiz submission' do
@submission.messages_sent.should be_empty
@submission.grade_submission
@submission.reload.messages_sent.keys.should == ['Submission Graded']
end
it 'sends a grade changed notification after re-grading the quiz submission' do
@submission.grade_submission
@submission.score = @submission.score + 5
@submission.save!
@submission.reload.messages_sent.keys.should include('Submission Grade Changed')
end
it 'does not send any notifications for a submission with essay questions before they have been graded' do
quiz_with_graded_submission([{:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'essay_question'}}])
@quiz_submission.reload.messages_sent.should be_empty
end
it 'sends a notifications for a submission with essay questions before they have been graded if manually graded' do
quiz_with_graded_submission([{:question_data => {:name => 'question 1', :points_possible => 1, 'question_type' => 'essay_question'}}])
@quiz_submission.set_final_score(2)
@quiz_submission.reload.messages_sent.keys.should == ['Submission Graded']
end
end
end