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:
parent
8d8f26c42e
commit
cd36ed7bc6
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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'))")
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue