Fixing outcome scoring for quizzes

Only quiz questions were being scored.  This commit adds
LearningOutcomeResults for a quiz which determines mastery by
performance on the entire selection of questions.

This also records LearningOutcomeQuestionResults for each quiz
question outcome result.

Closes CNVS-13282

Test-Plan:
- Build out several question banks tied to learning outcomes
- Confirm nothing is broken.
- Confirm that outcomes are scored correctly

Change-Id: If2dd2c5f387071cec3c5634bcb74fc3f78283c64
Reviewed-on: https://gerrit.instructure.com/45381
Tested-by: Jenkins
Reviewed-by: John Corrigan <jcorrigan@instructure.com>
QA-Review: Michael Hargiss <mhargiss@instructure.com>
Product-Review: Ryan Taylor <rtaylor@instructure.com>
This commit is contained in:
Ryan Taylor 2015-07-14 10:24:21 -06:00
parent 8d8f26c42e
commit cd36ed7bc6
9 changed files with 451 additions and 137 deletions

View File

@ -0,0 +1,80 @@
#
# Copyright (C) 2015 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/>.
#
class LearningOutcomeQuestionResult < ActiveRecord::Base
include PolymorphicTypeOverride
override_polymorphic_types associated_asset_type: {'Quiz' => 'Quizzes::Quiz'}
belongs_to :learning_outcome_result
belongs_to :learning_outcome
belongs_to :associated_asset, :polymorphic => true
simply_versioned
scope :for_associated_asset, lambda {|associated_asset|
where(:associated_asset_type => associated_asset.class.to_s, :associated_asset_id => associated_asset.id)
}
before_save :infer_defaults
def infer_defaults
self.original_score ||= self.score
self.original_possible ||= self.possible
self.original_mastery = self.mastery if self.original_mastery.nil?
calculate_percent!
true
end
def calculate_percent!
if self.score && self.possible
self.percent = self.score.to_f / self.possible.to_f
end
self.percent = nil if self.percent && !self.percent.to_f.finite?
end
def save_to_version(attempt)
if self.versions.empty?
save
else
current_version = self.versions.current.model
if current_version.attempt && attempt < current_version.attempt
versions = self.versions.sort_by(&:created_at).reverse.select{|v| v.model.attempt == attempt}
unless versions.empty?
versions.all? do |version|
update_version_data(version)
end
end
else
save
end
end
end
private
def update_version_data(version)
version_data = YAML::load(version.yaml)
version_data["score"] = self.score
version_data["mastery"] = self.mastery
version_data["possible"] = self.possible
version_data["attempt"] = self.attempt
version_data["title"] = self.title
version.yaml = version_data.to_yaml
version.save
end
end

View File

@ -33,14 +33,15 @@ class LearningOutcomeResult < ActiveRecord::Base
validates_inclusion_of :associated_asset_type, :allow_nil => true, :in => ['AssessmentQuestion', 'Quizzes::Quiz', 'LiveAssessments::Assessment']
belongs_to :context, :polymorphic => true
validates_inclusion_of :context_type, :allow_nil => true, :in => ['Course']
has_many :learning_outcome_question_results
simply_versioned
EXPORTABLE_ATTRIBUTES = [
:id, :context_id, :context_type, :context_code, :association_id, :association_type, :content_tag_id, :learning_outcome_id, :mastery, :user_id, :score, :created_at, :updated_at,
:attempt, :possible, :comments, :original_score, :original_possible, :original_mastery, :artifact_id, :artifact_type, :assessed_at, :title, :percent, :associated_asset_id, :associated_asset_type
]
].freeze
EXPORTABLE_ASSOCIATIONS = [:user, :learning_outcome, :association_object, :artifact, :associated_asset, :context]
EXPORTABLE_ASSOCIATIONS = [:user, :learning_outcome, :association_object, :artifact, :associated_asset, :context].freeze
before_save :infer_defaults
attr_accessible :learning_outcome, :user, :association_object, :alignment, :associated_asset
@ -51,11 +52,17 @@ class LearningOutcomeResult < ActiveRecord::Base
self.original_score ||= self.score
self.original_possible ||= self.possible
self.original_mastery = self.mastery if self.original_mastery == nil
self.percent = self.score.to_f / self.possible.to_f rescue nil
self.percent = nil if self.percent && !self.percent.to_f.finite?
calculate_percent!
true
end
def calculate_percent!
if self.score && self.possible
self.percent = self.score.to_f / self.possible.to_f
end
self.percent = nil if self.percent && !self.percent.to_f.finite?
end
def assignment
if self.association_object.is_a?(Assignment)
self.association_object

View File

@ -0,0 +1,103 @@
module Quizzes
class QuizOutcomeResultBuilder
def initialize(quiz_submission)
@qs = quiz_submission
end
def build_outcome_results(questions, alignments)
create_quiz_outcome_results(questions, alignments)
questions.each do |question|
alignments.each do |alignment|
if alignment.content_id == question.assessment_question_bank_id
create_outcome_question_result(question, alignment)
end
end
end
end
private
def create_outcome_question_result(question, alignment)
# find or create the user's unique LearningOutcomeResult for this alignment
# of the quiz question.
quiz_result = alignment.learning_outcome_results.
for_association(@qs.quiz).
for_associated_asset(@qs.quiz).
where(user_id: @qs.user.id).
first_or_initialize
# Create a question scoped outcome result linked to the quiz_result.
question_result = quiz_result.learning_outcome_question_results.for_associated_asset(question).first_or_initialize
# update the result with stuff from the quiz submission's question result
cached_question = @qs.quiz_data.detect { |q| q[:assessment_question_id] == question.id }
cached_answer = @qs.submission_data.detect { |q| q[:question_id] == cached_question[:id] }
raise "Could not find valid question" unless cached_question
raise "Could not find valid answer" unless cached_answer
question_result.learning_outcome = quiz_result.learning_outcome
# mastery
question_result.score = cached_answer[:points]
question_result.possible = cached_question['points_possible']
question_result.calculate_percent!
question_result.mastery = determine_mastery(question_result, alignment)
# attempt
question_result.attempt = @qs.attempt
# title
question_result.title = "#{@qs.user.name}, #{@qs.quiz.title}: #{cached_question[:name]}"
question_result.submitted_at = @qs.finished_at
question_result.assessed_at = Time.zone.now
question_result.save_to_version(question_result.attempt)
question_result
end
def create_quiz_outcome_results(questions, alignments)
unique_alignments = alignments.uniq
unique_alignments.map do |alignment|
result = alignment.learning_outcome_results.
for_association(@qs.quiz).
for_associated_asset(@qs.quiz).
where(user_id: @qs.user.id).
first_or_initialize
result.artifact = @qs
result.context = @qs.quiz.context || alignment.context
cached_questions_and_answers = questions.select do |question|
question.assessment_question_bank_id == alignment.content_id
end.map do |question|
cached_question = @qs.quiz_data.detect { |q| q[:assessment_question_id] == question.id }
cached_answer = @qs.submission_data.detect { |q| q[:question_id] == cached_question[:id] }
raise "Could not find valid question" unless cached_question
raise "Could not find valid answer" unless cached_answer
[cached_question, cached_answer]
end
result.score = cached_questions_and_answers.map(&:last).
map { |h| h[:points] }.
inject(:+)
result.possible = cached_questions_and_answers.map(&:first).
map { |h| h['points_possible']}.
inject(:+)
result.calculate_percent!
result.mastery = determine_mastery(result, alignment)
result.attempt = @qs.attempt
result.title = "#{@qs.user.name}, #{@qs.quiz.title}"
result.assessed_at = Time.zone.now
result.submitted_at = @qs.finished_at
result.save_to_version(result.attempt)
result
end
end
def determine_mastery(result, alignment)
if alignment.mastery_score && result.percent
result.percent >= alignment.mastery_score
end
end
end
end

View File

@ -141,43 +141,6 @@ class Quizzes::QuizSubmission < ActiveRecord::Base
true
end
def create_outcome_result(question, alignment)
# find or create the user's unique LearningOutcomeResult for this alignment
# of the quiz question.
result = alignment.learning_outcome_results.
for_association(quiz).
for_associated_asset(question).
where(user_id: user.id).
first_or_initialize
# force the context and artifact
result.artifact = self
result.context = quiz.context || alignment.context
# update the result with stuff from the quiz submission's question result
cached_question = quiz_data.detect { |q| q[:assessment_question_id] == question.id }
cached_answer = submission_data.detect { |q| q[:question_id] == cached_question[:id] }
raise "Could not find valid question" unless cached_question
raise "Could not find valid answer" unless cached_answer
# mastery
result.score = cached_answer[:points]
result.possible = cached_question['points_possible']
result.mastery = alignment.mastery_score && result.score && result.possible && (result.score / result.possible) > alignment.mastery_score
# attempt
result.attempt = attempt
# title
result.title = "#{user.name}, #{quiz.title}: #{cached_question[:name]}"
result.assessed_at = Time.now
result.submitted_at = self.finished_at
result.save_to_version(result.attempt)
result
end
def question(id)
questions.detect { |q| q[:id].to_i == id.to_i }
end

View File

@ -89,23 +89,17 @@ module Quizzes
tagged_bank_ids = Set.new(alignments.map(&:content_id))
question_ids = questions.select { |q| tagged_bank_ids.include?(q.assessment_question_bank_id) }
send_later_if_production(:update_outcomes_for_assessment_questions, question_ids, @submission.id, attempt) unless question_ids.empty?
send_later_if_production(:update_outcomes, question_ids, @submission.id, attempt) unless question_ids.empty?
end
def update_outcomes_for_assessment_questions(question_ids, submission_id, attempt)
def update_outcomes(question_ids, submission_id, attempt)
questions, alignments = questions_and_alignments(question_ids)
return if questions.empty? || alignments.empty?
submission = Quizzes::QuizSubmission.find(submission_id)
versioned_submission = submission.attempt == attempt ? submission : submission.versions.sort_by(&:created_at).map(&:model).reverse.detect { |s| s.attempt == attempt }
questions.each do |question|
alignments.each do |alignment|
if alignment.content_id == question.assessment_question_bank_id
versioned_submission.create_outcome_result(question, alignment)
end
end
end
builder = Quizzes::QuizOutcomeResultBuilder.new(versioned_submission)
builder.build_outcome_results(questions, alignments)
end
private

View File

@ -0,0 +1,40 @@
class CreateLearningOutcomeQuestionResults < ActiveRecord::Migration
tag :predeploy
def up
create_table :learning_outcome_question_results do |t|
t.integer :learning_outcome_result_id, limit: 8
t.integer :learning_outcome_id, limit: 8
t.integer :context_id, limit: 8
t.integer :associated_asset_id, limit: 8
t.string :associated_asset_type
t.string :context_type
t.string :context_code
t.float :score
t.float :possible
t.boolean :mastery
t.float :percent
t.integer :attempt
t.text :title
t.float :original_score
t.float :original_possible
t.boolean :original_mastery
t.datetime :assessed_at
t.datetime :created_at
t.datetime :updated_at
t.datetime :submitted_at
end
add_index "learning_outcome_question_results", [:learning_outcome_id], name: "index_learning_outcome_question_results_on_learning_outcome_id"
add_index "learning_outcome_question_results", [:learning_outcome_result_id], name: "index_LOQR_on_learning_outcome_result_id"
end
def down
drop_table :learning_outcome_question_results
remove_index :learning_outcome_question_results, [:learning_outcome_id]
remove_index :learning_outcome_question_results, [:learning_outcome_result_id]
end
end

View File

@ -208,8 +208,8 @@ module AccountReports
aq.id AS "assessment question id",
learning_outcomes.short_description AS "learning outcome name",
learning_outcomes.id AS "learning outcome id",
r.attempt AS "attempt",
r.score AS "outcome score",
COALESCE(qr.attempt, r.attempt) AS "attempt",
COALESCE(qr.score, r.score) AS "outcome score",
c.name AS "course name",
c.id AS "course id",
c.sis_source_id AS "course sis id",
@ -219,6 +219,7 @@ module AccountReports
joins("INNER JOIN #{LearningOutcome.quoted_table_name} ON content_tags.content_id = learning_outcomes.id
AND content_tags.content_type = 'LearningOutcome'
INNER JOIN #{LearningOutcomeResult.quoted_table_name} r ON r.learning_outcome_id = learning_outcomes.id
FULL OUTER JOIN #{LearningOutcomeQuestionResult.quoted_table_name} qr on qr.learning_outcome_id = r.learning_outcome_id
INNER JOIN #{ContentTag.quoted_table_name} ct ON r.content_tag_id = ct.id
INNER JOIN #{User.quoted_table_name} u ON u.id = r.user_id
INNER JOIN #{Pseudonym.quoted_table_name} p on p.user_id = r.user_id
@ -231,8 +232,8 @@ module AccountReports
AND subs.user_id = u.id
LEFT OUTER JOIN #{Quizzes::QuizSubmission.quoted_table_name} qs ON r.artifact_id = qs.id
AND r.artifact_type IN ('QuizSubmission', 'Quizzes::QuizSubmission')
LEFT OUTER JOIN #{AssessmentQuestion.quoted_table_name} aq ON aq.id = r.associated_asset_id
AND r.associated_asset_type = 'AssessmentQuestion'").
LEFT OUTER JOIN #{AssessmentQuestion.quoted_table_name} aq ON aq.id = qr.associated_asset_id
AND qr.associated_asset_type = 'AssessmentQuestion'").
where("ct.workflow_state <> 'deleted'
AND (r.id IS NULL OR (r.artifact_type IS NOT NULL AND r.artifact_type <> 'Submission'))")

View File

@ -0,0 +1,208 @@
#
# 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 Quizzes::QuizOutcomeResultBuilder do
def question_data(reset=false)
@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'}]
}
end
def build_course_quiz_questions_and_a_bank
course_with_student(:active_all => true)
@quiz = @course.quizzes.create!(:title => "new quiz", :shuffle_answers => true)
@q1 = @quiz.quiz_questions.create!(:question_data => question_data(true))
@q2 = @quiz.quiz_questions.create!(:question_data => question_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)
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
@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
@outcome.reload
@quiz_results = @outcome.learning_outcome_results.where(user_id: @user).to_a
@quiz_result = @quiz_results.first
@question_results = @quiz_results.first.learning_outcome_question_results
end
it 'has valid bank data' do
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@q2.assessment_question.assessment_question_bank).to eql(@bank)
expect(@bank.assessment_question_count).to eql(2)
expect(@sub.score).to eql(1.0)
end
it "should create learning outcome results" do
expect(@quiz_results.size).to eql(1)
expect(@question_results.size).to eql(2)
end
it 'should consider scores in aggregate' do
expect(@quiz_result.possible).to eql(2.0)
expect(@quiz_result.score).to eql(1.0)
end
it "shouldn't declare mastery" do
expect(@quiz_result.mastery).to eql(false)
end
context 'with two outcomes' do
before :once do
course_with_student(active_all: true)
@quiz = @course.quizzes.create!(title: 'test quiz')
@outcome = @course.created_learning_outcomes.create!(:short_description => 'new outcome')
@outcome2 = @course.created_learning_outcomes.create!(:short_description => 'new outcome #2')
@bank = @course.assessment_question_banks.create!(:title => 'bank1')
@bank2 = @course.assessment_question_banks.create!(:title => 'bank2')
@outcome.align(@bank, @bank.context, :mastery_score => 0.7)
@outcome2.align(@bank2, @bank2.context, :mastery_score => 0.5)
@a1 = @bank.assessment_questions.create!(question_data: question_data(true))
@a3 = @bank.assessment_questions.create!(question_data: question_data)
@a2 = @bank2.assessment_questions.create!(question_data: question_data)
@a4 = @bank2.assessment_questions.create!(question_data: question_data)
@q1 = @quiz.quiz_questions.create!(assessment_question: @a1, question_data: @a1.question_data)
@q3 = @quiz.quiz_questions.create!(assessment_question: @a3, question_data: @a3.question_data)
@q2 = @quiz.quiz_questions.create!(assessment_question: @a2, question_data: @a2.question_data)
@q4 = @quiz.quiz_questions.create!(assessment_question: @a4, question_data: @a4.question_data)
@quiz.generate_quiz_data(:persist => true)
@sub = @quiz.generate_submission(@user)
@sub.submission_data = {}
answer_a_question(@q1, @sub)
answer_a_question(@q2, @sub)
answer_a_question(@q3, @sub, correct: false)
answer_a_question(@q4, @sub, correct: false)
Quizzes::SubmissionGrader.new(@sub).grade_submission
@outcome.reload
@outcome2.reload
@quiz_results = LearningOutcomeResult.where(user_id: @user).to_a
@question_results = @quiz_results.map(&:learning_outcome_question_results)
end
it "has valid bank data" do
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@bank2.learning_outcome_alignments.length).to eql(1)
expect(@q1.assessment_question.assessment_question_bank).to eql(@bank)
expect(@q3.assessment_question.assessment_question_bank).to eql(@bank)
expect(@q2.assessment_question.assessment_question_bank).to eql(@bank2)
expect(@q4.assessment_question.assessment_question_bank).to eql(@bank2)
expect(@bank.assessment_question_count).to eql(2)
expect(@bank2.assessment_question_count).to eql(2)
end
it "should create two learning outcome results" do
expect(@question_results.map(&:size)).to eql([2,2])
expect(@quiz_results.size).to eql(2)
end
it 'should consider scores in aggregate' do
expect(@quiz_results.map(&:possible)).to eql([2.0,2.0])
expect(@quiz_results.map(&:score)).to eql([1.0,1.0])
end
it "should declare mastery when equal" do
expect(@quiz_results.map(&:mastery)).to eql([false, true])
end
end
end
describe "question level learning outcomes" do
it "should create learning outcome results when aligned to assessment questions" do
build_course_quiz_questions_and_a_bank
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@q2.assessment_question.assessment_question_bank).to eql(@bank)
@q1.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
@q2.question_data[:answers].detect{|a| a[:weight] == 100 }[:id]
@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
expect(@sub.score).to eql(1.0)
@outcome.reload
@quiz_result = @outcome.learning_outcome_results.where(user_id: @user).first
@results = @quiz_result.learning_outcome_question_results
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to eql(false)
end
it "should update learning outcome results when aligned to assessment questions" do
build_course_quiz_questions_and_a_bank
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@q2.assessment_question.assessment_question_bank).to eql(@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
expect(@sub.score).to eql(1.0)
@outcome.reload
@quiz_result = @outcome.learning_outcome_results.where(user_id: @user).first
@results = @quiz_result.learning_outcome_question_results
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to eql(false)
@sub = @quiz.generate_submission(@user)
expect(@sub.attempt).to eql(2)
@sub.submission_data = {}
answer_a_question(@q1, @sub, correct: false)
answer_a_question(@q2, @sub)
Quizzes::SubmissionGrader.new(@sub).grade_submission
expect(@sub.score).to eql(1.0)
@outcome.reload
@quiz_result = @outcome.learning_outcome_results.where(user_id: @user).first
@results = @quiz_result.learning_outcome_question_results
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(false)
expect(@results.first.original_mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to eql(true)
expect(@results.last.original_mastery).to eql(false)
end
end
end

View File

@ -674,88 +674,6 @@ describe Quizzes::QuizSubmission do
end
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_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '3'}, {'answer_text' => '4'}]})
@q2 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 2', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => [{'answer_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '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)
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@q2.assessment_question.assessment_question_bank).to 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.data[:id]
question_2 = @q2.data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1
Quizzes::SubmissionGrader.new(@sub).grade_submission
expect(@sub.score).to eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.where(user_id: @user).to_a
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to 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_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '3'}, {'answer_text' => '4'}]})
@q2 = @quiz.quiz_questions.create!(:question_data => {:name => 'question 2', :points_possible => 1, 'question_type' => 'multiple_choice_question', 'answers' => [{'answer_text' => '1', 'answer_weight' => '100'}, {'answer_text' => '2'}, {'answer_text' => '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)
expect(@bank.learning_outcome_alignments.length).to eql(1)
expect(@q2.assessment_question.assessment_question_bank).to 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.data[:id]
question_2 = @q2.data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1
Quizzes::SubmissionGrader.new(@sub).grade_submission
expect(@sub.score).to eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.where(user_id: @user).to_a
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to eql(false)
@sub = @quiz.generate_submission(@user)
expect(@sub.attempt).to eql(2)
@sub.submission_data = {}
question_1 = @q1.data[:id]
question_2 = @q2.data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1 + 1
@sub.submission_data["question_#{question_2}"] = answer_2
Quizzes::SubmissionGrader.new(@sub).grade_submission
expect(@sub.score).to eql(1.0)
@outcome.reload
@results = @outcome.learning_outcome_results.where(user_id: @user).to_a
expect(@results.length).to eql(2)
@results = @results.sort_by(&:associated_asset_id)
expect(@results.first.associated_asset).to eql(@q1.assessment_question)
expect(@results.first.mastery).to eql(false)
expect(@results.first.original_mastery).to eql(true)
expect(@results.last.associated_asset).to eql(@q2.assessment_question)
expect(@results.last.mastery).to eql(true)
expect(@results.last.original_mastery).to eql(false)
end
end
describe "#score_to_keep" do