align question banks to outcomes
Question banks can be aligned to outcomes that are part of the same context. Whenever students take a quiz with questions from the bank they'll get evaluated for the linked outcomes based on the points they got for each bank question. Also fixed a bug which may or may not have existed before this commit that unexpectedly deleted quiz submissions when a user tried to re-take a quiz. refs #3317 Change-Id: I744af3915672a5e260b078503e0bc648c238eca9 Reviewed-on: https://gerrit.instructure.com/2641 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
parent
ec92342ae0
commit
b7f1d5ae18
|
@ -98,11 +98,7 @@ class OutcomesController < ApplicationController
|
|||
if authorized_action(@context, @current_user, :manage_outcomes)
|
||||
@account_contexts = @context.associated_accounts rescue []
|
||||
@current_outcomes = @context.learning_outcomes
|
||||
@outcomes = []
|
||||
([@context] + @account_contexts).uniq.each do |context|
|
||||
@outcomes += LearningOutcomeGroup.default_for(context).sorted_all_outcomes rescue []
|
||||
end
|
||||
@outcomes = @outcomes.uniq
|
||||
@outcomes = LearningOutcome.available_in_context(@context)
|
||||
if params[:unused]
|
||||
@outcomes -= @current_outcomes
|
||||
end
|
||||
|
@ -170,8 +166,19 @@ class OutcomesController < ApplicationController
|
|||
elsif @result.artifact.is_a?(RubricAssessment) && @result.artifact.artifact && @result.artifact.artifact.is_a?(Submission)
|
||||
@submission = @result.artifact.artifact
|
||||
redirect_to named_context_url(@result.context, :context_assignment_submission_url, @submission.assignment_id, @submission.user_id)
|
||||
elsif @result.artifact.is_a?(QuizSubmission) && @result.associated_asset
|
||||
@submission = @result.artifact
|
||||
@question = @result.associated_asset
|
||||
if @submission.attempt <= @result.attempt
|
||||
@submission_version = @submission
|
||||
else
|
||||
@submission_version = @submission.submitted_versions.detect{|s| s.attempt >= @result.attempt }
|
||||
end
|
||||
question = @submission.quiz_data.detect{|q| q['assessment_question_id'] == @question.data[:id] }
|
||||
question_id = (question && question['id']) || @question.data[:id]
|
||||
redirect_to named_context_url(@result.context, :context_quiz_history_url, @submission.quiz_id, :quiz_submission_id => @submission.id, :version => @submission_version.version_number, :anchor => "question_#{question_id}")
|
||||
else
|
||||
flash[:error] = "Unrecognized artifact type: #{@result.artifact_type rescue 'nil'}"
|
||||
flash[:error] = "Unrecognized artifact type: #{@result.try(:artifact_type) || 'nil'}"
|
||||
redirect_to named_context_url(@context, :context_outcome_url, @outcome.id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,6 +65,7 @@ class QuestionBanksController < ApplicationController
|
|||
@bank = @context.assessment_question_banks.find(params[:id])
|
||||
add_crumb(@bank.title)
|
||||
if authorized_action(@bank, @current_user, :read)
|
||||
@outcome_tags = @bank.learning_outcome_tags.sort_by{|t| t.learning_outcome.short_description.downcase }
|
||||
@questions = @bank.assessment_questions.active.paginate(:per_page => 50, :page => 1)
|
||||
end
|
||||
end
|
||||
|
@ -122,7 +123,8 @@ class QuestionBanksController < ApplicationController
|
|||
@bank = @context.assessment_question_banks.find(params[:id])
|
||||
if authorized_action(@bank, @current_user, :update)
|
||||
if @bank.update_attributes(params[:assessment_question_bank])
|
||||
render :json => @bank.to_json
|
||||
@bank.reload
|
||||
render :json => @bank.to_json(:include => {:learning_outcome_tags => {:include => :learning_outcome}})
|
||||
else
|
||||
render :json => @bank.errors.to_json, :status => :bad_request
|
||||
end
|
||||
|
|
|
@ -114,9 +114,9 @@ class QuizSubmissionsController < ApplicationController
|
|||
if authorized_action(@submission, @current_user, :update_scores)
|
||||
@submission.update_scores(params)
|
||||
if params[:headless]
|
||||
redirect_to named_context_url(@context, :context_quiz_history_url, @quiz, :user_id => @submission.user_id, :version => @submission.version_number, :headless => 1, :score_updated => 1)
|
||||
redirect_to named_context_url(@context, :context_quiz_history_url, @quiz, :user_id => @submission.user_id, :version => (params[:submission_version_number] || @submission.version_number), :headless => 1, :score_updated => 1)
|
||||
else
|
||||
redirect_to named_context_url(@context, :context_quiz_history_url, @quiz, :user_id => @submission.user_id, :version => @submission.version_number)
|
||||
redirect_to named_context_url(@context, :context_quiz_history_url, @quiz, :user_id => @submission.user_id, :version => (params[:submission_version_number] || @submission.version_number))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
|
||||
class AssessmentQuestionBank < ActiveRecord::Base
|
||||
include Workflow
|
||||
attr_accessible :context, :title, :user
|
||||
attr_accessible :context, :title, :user, :outcomes
|
||||
belongs_to :context, :polymorphic => true
|
||||
has_many :assessment_questions, :order => 'position, created_at'
|
||||
has_many :assessment_question_bank_users
|
||||
has_many :learning_outcome_tags, :as => :content, :class_name => 'ContentTag', :conditions => ['content_tags.tag_type = ? AND content_tags.workflow_state != ?', 'learning_outcome', 'deleted'], :include => :learning_outcome
|
||||
has_many :quiz_groups
|
||||
before_save :infer_defaults
|
||||
DEFAULT_IMPORTED_TITLE = 'Imported Questions'
|
||||
|
@ -82,6 +83,31 @@ class AssessmentQuestionBank < ActiveRecord::Base
|
|||
AssessmentQuestion.find_all_by_id(ids)
|
||||
end
|
||||
|
||||
def outcomes=(hash)
|
||||
raise "Can't set outcomes on unsaved bank" if new_record?
|
||||
hash = {} if hash.blank?
|
||||
ids = []
|
||||
hash.each do |key, val|
|
||||
ids.push(key) if !key.blank? && key.to_i != 0
|
||||
end
|
||||
ids.uniq!
|
||||
tags = self.learning_outcome_tags
|
||||
tag_outcome_ids = tags.map(&:learning_outcome_id).compact.uniq
|
||||
outcomes = LearningOutcome.available_in_context(self.context, tag_outcome_ids)
|
||||
missing_ids = ids.select{|id| !tag_outcome_ids.include?(id) }
|
||||
tags.each do |tag|
|
||||
if hash[tag.learning_outcome_id.to_s]
|
||||
tag.update_attribute(:mastery_score, hash[tag.learning_outcome_id.to_s].to_f)
|
||||
end
|
||||
end
|
||||
tags_to_delete = tags.select{|t| !ids.include?(t.learning_outcome_id) }
|
||||
missing_ids.each do |id|
|
||||
self.learning_outcome_tags.create!(:learning_outcome_id => id.to_i, :context => self.context, :tag_type => 'learning_outcome', :mastery_score => hash[id].to_f)
|
||||
end
|
||||
tags_to_delete.each{|t| t.destroy }
|
||||
true
|
||||
end
|
||||
|
||||
named_scope :active, lambda {
|
||||
{:conditions => ['assessment_question_banks.workflow_state != ?', 'deleted'] }
|
||||
}
|
||||
|
|
|
@ -215,18 +215,42 @@ class ContentTag < ActiveRecord::Base
|
|||
raise "Outcome association required" unless association
|
||||
raise "Cannot evaluate a rubric alignment for a non-rubric artifact" if self.rubric_association_id && !artifact.is_a?(RubricAssessment)
|
||||
raise "Cannot evaluate a non-rubric alignment for a rubric artifact" if !self.rubric_association_id && artifact.is_a?(RubricAssessment)
|
||||
assessment_question = opts[:assessment_question]
|
||||
raise "Assessment question required for quiz outcomes" if association.is_a?(Quiz) && !opts[:assessment_question]
|
||||
association_type = association.class.to_s
|
||||
result = nil
|
||||
attempts = 0
|
||||
begin
|
||||
result = LearningOutcomeResult.find_or_create_by_learning_outcome_id_and_user_id_and_association_id_and_association_type_and_content_tag_id(self.learning_outcome_id, user.id, association.id, association_type, self.id)
|
||||
if association.is_a?(Quiz)
|
||||
result = LearningOutcomeResult.find_or_create_by_learning_outcome_id_and_user_id_and_association_id_and_association_type_and_content_tag_id_and_associated_asset_type_and_associated_asset_id(self.learning_outcome_id, user.id, association.id, association_type, self.id, 'AssessmentQuestion', assessment_question.id)
|
||||
else
|
||||
result = LearningOutcomeResult.find_or_create_by_learning_outcome_id_and_user_id_and_association_id_and_association_type_and_content_tag_id(self.learning_outcome_id, user.id, association.id, association_type, self.id)
|
||||
end
|
||||
rescue => e
|
||||
attempts += 1
|
||||
retry if attempts < 3
|
||||
end
|
||||
result.context = self.context
|
||||
result.artifact = artifact
|
||||
if artifact
|
||||
result.artifact_id = artifact.id
|
||||
result.artifact_type = artifact.class.to_s
|
||||
end
|
||||
case association
|
||||
when Quiz
|
||||
question_index = nil
|
||||
cached_question = artifact.quiz_data.detect{|q| q[:assessment_question_id] == assessment_question.id }
|
||||
cached_answer = artifact.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
|
||||
|
||||
result.score = cached_answer[:points]
|
||||
result.context = association.context || result.context
|
||||
result.possible = cached_question['points_possible']
|
||||
if self.mastery_score && result.score && result.possible
|
||||
result.mastery = (result.score / result.possible) > self.mastery_score
|
||||
end
|
||||
result.attempt = artifact.attempt
|
||||
result.title = "#{user.name}, #{association.title}: #{cached_question[:name]}"
|
||||
when Assignment
|
||||
if self.rubric_association_id && artifact.is_a?(RubricAssessment)
|
||||
association = self.rubric_association
|
||||
|
@ -264,7 +288,7 @@ class ContentTag < ActiveRecord::Base
|
|||
result.mastery = !!artifact[:mastery] rescue nil
|
||||
end
|
||||
result.assessed_at = Time.now
|
||||
result.save
|
||||
result.save_to_version(result.attempt)
|
||||
result
|
||||
end
|
||||
|
||||
|
@ -323,4 +347,8 @@ class ContentTag < ActiveRecord::Base
|
|||
named_scope :include_outcome, lambda{
|
||||
{ :include => :learning_outcome }
|
||||
}
|
||||
named_scope :outcome_tags_for_banks, lambda{|bank_ids|
|
||||
raise "bank_ids required" if bank_ids.blank?
|
||||
{ :conditions => ["content_tags.tag_type = ? AND content_tags.workflow_state != ? AND content_tags.content_type = ? AND content_tags.content_id IN (#{bank_ids.join(',')})", 'learning_outcome', 'deleted', 'AssessmentQuestionBank'] }
|
||||
}
|
||||
end
|
||||
|
|
|
@ -138,6 +138,18 @@ class LearningOutcome < ActiveRecord::Base
|
|||
self.learning_outcome_results.for_context_codes(codes).count
|
||||
end
|
||||
|
||||
def self.available_in_context(context, ids=[])
|
||||
account_contexts = context.associated_accounts rescue []
|
||||
codes = account_contexts.map(&:asset_string)
|
||||
order = {}
|
||||
codes.each_with_index{|c, idx| order[c] = idx }
|
||||
outcomes = []
|
||||
([context] + account_contexts).uniq.each do |context|
|
||||
outcomes += LearningOutcomeGroup.default_for(context).try(:sorted_all_outcomes, ids) || []
|
||||
end
|
||||
outcomes.uniq
|
||||
end
|
||||
|
||||
def self.non_rubric_outcomes?
|
||||
false
|
||||
end
|
||||
|
|
|
@ -41,27 +41,31 @@ class LearningOutcomeGroup < ActiveRecord::Base
|
|||
state :deleted
|
||||
end
|
||||
|
||||
def sorted_content
|
||||
def sorted_content(outcome_ids=[])
|
||||
tags = self.content_tags.active
|
||||
positions = {}
|
||||
tags.each{|t| positions[t.content_asset_string] = t.position }
|
||||
objects = LearningOutcome.active.find_all_by_id(tags.select{|t| t.content_type == 'LearningOutcome'}.map(&:content_id)).compact
|
||||
ids_to_find = tags.select{|t| t.content_type == 'LearningOutcome'}.map(&:content_id)
|
||||
ids_to_find = (ids_to_find & outcome_ids) unless outcome_ids.empty?
|
||||
objects = LearningOutcome.active.find_all_by_id(ids_to_find).compact
|
||||
objects += LearningOutcomeGroup.active.find_all_by_id(tags.select{|t| t.content_type == 'LearningOutcomeGroup'}.map(&:content_id)).compact
|
||||
if self.learning_outcome_group_id == nil
|
||||
all_tags = all_tags_for_context
|
||||
codes = all_tags.map(&:content_asset_string).uniq
|
||||
objects += LearningOutcome.active.find_all_by_context_id_and_context_type(self.context_id, self.context_type).select{|o| !codes.include?(o.asset_string) }
|
||||
all_objects = LearningOutcome.active.find_all_by_id_and_context_id_and_context_type(outcome_ids, self.context_id, self.context_type).select{|o| !codes.include?(o.asset_string) } unless outcome_ids.empty?
|
||||
all_objects ||= LearningOutcome.active.find_all_by_context_id_and_context_type(self.context_id, self.context_type).select{|o| !codes.include?(o.asset_string) }
|
||||
objects += all_objects
|
||||
end
|
||||
sorted_objects = objects.uniq.sort_by{|o| positions[o.asset_string] || 999 }
|
||||
end
|
||||
|
||||
def sorted_all_outcomes
|
||||
def sorted_all_outcomes(ids=[])
|
||||
res = []
|
||||
self.sorted_content.each do |obj|
|
||||
self.sorted_content(ids).each do |obj|
|
||||
if obj.is_a?(LearningOutcome)
|
||||
res << obj
|
||||
else
|
||||
res += obj.sorted_all_outcomes
|
||||
res += obj.sorted_all_outcomes(ids)
|
||||
end
|
||||
end
|
||||
res.uniq.compact
|
||||
|
|
|
@ -22,6 +22,7 @@ class LearningOutcomeResult < ActiveRecord::Base
|
|||
belongs_to :content_tag
|
||||
belongs_to :association, :polymorphic => true
|
||||
belongs_to :artifact, :polymorphic => true
|
||||
belongs_to :associated_asset, :polymorphic => true
|
||||
belongs_to :context, :polymorphic => true
|
||||
simply_versioned
|
||||
before_save :infer_defaults
|
||||
|
@ -52,6 +53,29 @@ class LearningOutcomeResult < ActiveRecord::Base
|
|||
]).empty?
|
||||
end
|
||||
|
||||
def save_to_version(attempt)
|
||||
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}
|
||||
if !versions.empty?
|
||||
versions.each do |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
|
||||
else
|
||||
save
|
||||
end
|
||||
else
|
||||
save
|
||||
end
|
||||
end
|
||||
|
||||
named_scope :for_context_codes, lambda{|codes|
|
||||
if codes == 'all'
|
||||
{}
|
||||
|
|
|
@ -465,7 +465,7 @@ class Quiz < ActiveRecord::Base
|
|||
end
|
||||
submission.end_at += (submission.extra_time * 60.0) if submission.end_at && submission.extra_time
|
||||
submission.finished_at = nil
|
||||
submission.submission_data = nil
|
||||
submission.submission_data = {}
|
||||
submission.workflow_state = 'preview' if preview
|
||||
submission.save
|
||||
submission
|
||||
|
|
|
@ -28,7 +28,7 @@ class QuizSubmission < ActiveRecord::Base
|
|||
before_save :update_kept_score
|
||||
before_save :sanitize_responses
|
||||
after_save :update_assignment_submission
|
||||
|
||||
|
||||
serialize :quiz_data
|
||||
serialize :submission_data
|
||||
|
||||
|
@ -87,6 +87,34 @@ class QuizSubmission < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
|
||||
def track_outcomes(attempt)
|
||||
question_ids = (self.quiz_data || []).map{|q| q[:assessment_question_id] }.compact.uniq
|
||||
questions = AssessmentQuestion.find_all_by_id(question_ids).compact
|
||||
bank_ids = questions.map(&:assessment_question_bank_id).uniq
|
||||
tagged_bank_ids = (bank_ids.empty? ? [] : ContentTag.outcome_tags_for_banks(bank_ids).scoped(:select => 'content_id')).map(&:content_id).uniq
|
||||
if !tagged_bank_ids.empty?
|
||||
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, self.id, attempt) unless question_ids.empty?
|
||||
end
|
||||
end
|
||||
|
||||
def update_outcomes_for_assessment_questions(question_ids, submission_id, attempt)
|
||||
return if question_ids.empty?
|
||||
submission = 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 = AssessmentQuestion.find_all_by_id(question_ids).compact
|
||||
bank_ids = questions.map(&:assessment_question_bank_id).uniq
|
||||
return if bank_ids.empty?
|
||||
tags = ContentTag.outcome_tags_for_banks(bank_ids)
|
||||
questions.each do |question|
|
||||
question_tags = tags.select{|t| t.content_id == question.assessment_question_bank_id }
|
||||
question_tags.each do |tag|
|
||||
debugger unless versioned_submission
|
||||
tag.create_outcome_result(self.user, self.quiz, versioned_submission, {:assessment_question => question})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def temporary_data
|
||||
raise "Cannot view temporary data for completed quiz" unless !self.completed?
|
||||
raise "Cannot view temporary data for completed quiz" if self.submission_data && !self.submission_data.is_a?(Hash)
|
||||
|
@ -97,7 +125,7 @@ class QuizSubmission < ActiveRecord::Base
|
|||
def data
|
||||
raise "Cannot view data for uncompleted quiz" unless self.completed?
|
||||
raise "Cannot view data for uncompleted quiz" if self.submission_data && !self.submission_data.is_a?(Array)
|
||||
res = self.submission_data || "[]"
|
||||
res = self.submission_data || []
|
||||
res
|
||||
end
|
||||
|
||||
|
@ -227,6 +255,7 @@ class QuizSubmission < ActiveRecord::Base
|
|||
|
||||
protected :update_assignment_submission
|
||||
|
||||
# Returned in order oldest to newest
|
||||
def submitted_versions
|
||||
found_attempts = {}
|
||||
res = []
|
||||
|
@ -280,6 +309,7 @@ class QuizSubmission < ActiveRecord::Base
|
|||
self.with_versioning(true) do |s|
|
||||
s.save
|
||||
end
|
||||
track_outcomes(self.attempt)
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -300,6 +330,7 @@ class QuizSubmission < ActiveRecord::Base
|
|||
self.score = submission.score
|
||||
self.save
|
||||
end
|
||||
track_outcomes(self.attempt) if self.attempt
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -315,7 +346,8 @@ class QuizSubmission < ActiveRecord::Base
|
|||
version_data["fudge_points"] = self.fudge_points
|
||||
version_data["workflow_state"] = self.workflow_state
|
||||
version.yaml = version_data.to_yaml
|
||||
version.save
|
||||
res = version.save
|
||||
res
|
||||
end
|
||||
|
||||
def context_module_action
|
||||
|
@ -382,6 +414,8 @@ class QuizSubmission < ActiveRecord::Base
|
|||
s.save
|
||||
end
|
||||
end
|
||||
track_outcomes(version.model.attempt)
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
|
|
@ -791,3 +791,17 @@ ul#quiz_versions
|
|||
&:hover
|
||||
td
|
||||
background-color: #eee
|
||||
|
||||
#aligned_outcomes_list
|
||||
.outcome
|
||||
margin-bottom: 3px
|
||||
padding-bottom: 3px
|
||||
border-bottom: 1px dotted #ccc
|
||||
.short_description
|
||||
float: left
|
||||
font-weight: bold
|
||||
.delete_outcome_link
|
||||
float: right
|
||||
.content
|
||||
font-size: 0.8em
|
||||
padding-left: 20px
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#outcomes td {
|
||||
padding: 2px 5px;
|
||||
text-align: center;
|
||||
border-bottom: 1px dotted #ccc;
|
||||
}
|
||||
#outcomes .short_description {
|
||||
text-align: left;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<%
|
||||
jammit_js :quizzes_bundle
|
||||
jammit_js :quizzes_bundle, :question_bank
|
||||
jammit_css :quizzes
|
||||
content_for :page_title, @bank.title
|
||||
%>
|
||||
|
@ -18,6 +18,27 @@
|
|||
<button class="button button-sidebar-wide disabled" disabled="true"><%= image_tag "bookmark.png" %> Already Bookmarked</button>
|
||||
<% end %>
|
||||
</div>
|
||||
<h2 id="aligned_outcomes">Aligned Outcomes</h2>
|
||||
<div class="rs-margin-lr rs-margin-bottom">
|
||||
<ul class="unstyled_list" id="aligned_outcomes_list">
|
||||
<% @outcome_tags.each do |tag| %>
|
||||
<li class="outcome" data-id="<%= tag.learning_outcome_id %>">
|
||||
<span class="short_description"><%= tag.learning_outcome.short_description %></span>
|
||||
<a href="#" class="delete_outcome_link no-hover"><%= image_tag "delete_circle.png" %></a>
|
||||
<span class="clear"></span>
|
||||
<span class="content">mastery at <span class="mastery_threshold"><%= (tag.mastery_score || 0) * 100.0 %></span>%</span>
|
||||
</li>
|
||||
<% end %>
|
||||
<li class="blank outcome" style="display: none;">
|
||||
<span class="short_description"> </span>
|
||||
<a href="#" class="delete_outcome_link no-hover"><%= image_tag "delete_circle.png" %></a>
|
||||
<span class="clear"></span>
|
||||
<span class="content">mastery at <span class="mastery_threshold"> </span>%</span>
|
||||
</li>
|
||||
</ul>
|
||||
<a href="#" class="button button-sidebar-wide add_outcome_link"><%= image_tag "ball.png" %> <span class="add_outcome_text">Align Outcome</span></a>
|
||||
</div>
|
||||
<%= render :partial => 'shared/find_outcome', :locals => {:purpose => 'question_bank'} %>
|
||||
<% end %>
|
||||
|
||||
<% form_for @bank, :url => context_url(@context, :context_question_bank_url), :html => {:id => "edit_bank_form", :method => :put} do |f| %>
|
||||
|
|
|
@ -23,3 +23,4 @@
|
|||
<a href="#" class="button add_rubric_link" style="margin-top: 20px; <%= hidden if @assignment && @assignment.rubric_association %>"><%= image_tag "rubric.png" %> Add Rubric</a>
|
||||
</div>
|
||||
<%= javascript_include_tag "edit_rubric.js" %>
|
||||
<%= javascript_include_tag "find_outcome.js" %>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<% context ||= @context %>
|
||||
<% context ||= @context; purpose ||= 'rubric' %>
|
||||
<div id="find_outcome_criterion_dialog" style="display: none;" class="find_outcome">
|
||||
<div class="loading_message" style="margin: 10px; text-align: center;">
|
||||
</div>
|
||||
|
@ -20,26 +20,37 @@
|
|||
<div class="outcome blank" style="display: none; cursor: pointer;" title="Select and Add Criterion">
|
||||
<div class="button-container" style="margin-bottom: 0.5em;">
|
||||
<button type="button" class="button select_outcome_link">Add Outcome</button>
|
||||
<div>
|
||||
<input type="checkbox" class="criterion_for_scoring" checked /><label>use this criterion for scoring</label>
|
||||
</div>
|
||||
<% if purpose == 'rubric' %>
|
||||
<div>
|
||||
<input type="checkbox" class="criterion_for_scoring" checked /><label>use this criterion for scoring</label>
|
||||
</div>
|
||||
<% elsif purpose == 'question_bank' %>
|
||||
<div>
|
||||
<label for="outcome_question_bank_mastery" class="mastery_level_text">
|
||||
set mastery for any score at or above:
|
||||
</label>
|
||||
<input type="text" class="mastery_level" id="outcome_question_bank_mastery" title="percent above which to set mastery" style="width: 40px;"/>%
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="learning_outcome_id" style="display: none;"></div>
|
||||
<div class="header short_description" style="font-weight: bold; font-size: 1.2em;"></div>
|
||||
<div class="body description" style="margin: 10px 0; font-size: 0.8em;"></div>
|
||||
<div>Criterion Ratings:</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="rating blank" style="display: none; padding: 2px 5px;">
|
||||
<div class="description"></div>
|
||||
<div class="long_description" style="font-size: 0.8em;"></div>
|
||||
<div><span class="points"></span> pts</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="font-size: 0.8em; margin-bottom: 10px;">
|
||||
threshold: <span class="mastery_points"> </span> pts
|
||||
</div>
|
||||
<% if purpose == 'rubric' %>
|
||||
<div>Criterion Ratings:</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="rating blank" style="display: none; padding: 2px 5px;">
|
||||
<div class="description"></div>
|
||||
<div class="long_description" style="font-size: 0.8em;"></div>
|
||||
<div><span class="points"></span> pts</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="font-size: 0.8em; margin-bottom: 10px;">
|
||||
threshold: <span class="mastery_points"> </span> pts
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -76,6 +76,7 @@ javascripts:
|
|||
- public/javascripts/assignments.js
|
||||
edit_rubric:
|
||||
- public/javascripts/edit_rubric.js
|
||||
- public/javascripts/find_outcome.js
|
||||
syllabus:
|
||||
- public/javascripts/calendar_move.js
|
||||
- public/javascripts/syllabus.js
|
||||
|
@ -134,6 +135,9 @@ javascripts:
|
|||
- public/javascripts/quiz_show.js
|
||||
- public/javascripts/quiz_rubric.js
|
||||
- public/javascripts/message_students.js
|
||||
question_bank:
|
||||
- public/javascripts/question_bank.js
|
||||
- public/javascripts/find_outcome.js
|
||||
take_quiz:
|
||||
- public/javascripts/quiz_timing.js
|
||||
- public/javascripts/take_quiz.js
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
class AddAssociatedAssetToLearningOutcomeResults < ActiveRecord::Migration
|
||||
def self.up
|
||||
add_column :learning_outcome_results, :associated_asset_id, :integer, :limit => 8
|
||||
add_column :learning_outcome_results, :associated_asset_type, :string
|
||||
remove_index :learning_outcome_results, [:user_id, :content_tag_id]
|
||||
add_index :learning_outcome_results, [:user_id, :content_tag_id, :associated_asset_id, :associated_asset_type], :unique => true
|
||||
end
|
||||
|
||||
def self.down
|
||||
# Not possible to reliably revert to the old index,
|
||||
# which was only on user_id and content_tag_id
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
|
@ -104,6 +104,7 @@ $(document).ready(function() {
|
|||
}
|
||||
$.flashError(message);
|
||||
};
|
||||
window.ajaxErrorFlash = ajaxErrorFlash;
|
||||
var data = $.ajaxJSON.findRequest(request);
|
||||
data = data || {};
|
||||
if(data.data) {
|
||||
|
|
|
@ -35,62 +35,35 @@ var rubricEditing = {
|
|||
return $criterion;
|
||||
},
|
||||
findOutcomeCriterion: function($rubric) {
|
||||
var $dialog = $("#find_outcome_criterion_dialog");
|
||||
$dialog.data('current_rubric', $rubric);
|
||||
if(!$dialog.hasClass('loaded')) {
|
||||
$dialog.find(".loading_message").text("Loading Outcomes...");
|
||||
$.ajaxJSON($dialog.find(".outcomes_list_url").attr('href'), 'GET', {}, function(data) {
|
||||
valids = [];
|
||||
for(var idx in data) {
|
||||
var outcome = data[idx].learning_outcome;
|
||||
if(outcome.data && outcome.data.rubric_criterion) {
|
||||
valids.push(outcome);
|
||||
}
|
||||
}
|
||||
if(valids.length === 0) {
|
||||
$dialog.find(".loading_message").text("No Rubric-Configured Outcomes found");
|
||||
} else {
|
||||
$dialog.find(".loading_message").hide();
|
||||
$dialog.addClass('loaded');
|
||||
for(var idx in valids) {
|
||||
var outcome = valids[idx];
|
||||
outcome.name = outcome.short_description;
|
||||
outcome.mastery_points = outcome.data.rubric_criterion.mastery_points || outcome.data.rubric_criterion.points_possible;
|
||||
var $name = $dialog.find(".outcomes_select.blank:first").clone(true).removeClass('blank');
|
||||
outcome.title = outcome.short_description;
|
||||
var $text = $("<div/>");
|
||||
$text.text(outcome.short_description);
|
||||
outcome.title = $.truncateText($.trim($text.text()), 35);
|
||||
outcome.display_name = outcome.cached_context_short_name || "";
|
||||
$name.fillTemplateData({data: outcome});
|
||||
$dialog.find(".outcomes_selects").append($name.show());
|
||||
var $outcome = $dialog.find(".outcome.blank:first").clone(true).removeClass('blank');
|
||||
outcome.learning_outcome_id = outcome.id;
|
||||
$outcome.fillTemplateData({data: outcome, htmlValues: ['description']});
|
||||
$outcome.addClass('outcome_' + outcome.id);
|
||||
if(outcome.data && outcome.data.rubric_criterion) {
|
||||
for(var jdx in outcome.data.rubric_criterion.ratings) {
|
||||
var rating = outcome.data.rubric_criterion.ratings[jdx];
|
||||
var $rating = $outcome.find(".rating.blank").clone(true).removeClass('blank');
|
||||
$rating.fillTemplateData({data: rating});
|
||||
$outcome.find("tr").append($rating.show());
|
||||
}
|
||||
}
|
||||
$dialog.find(".outcomes_list").append($outcome);
|
||||
}
|
||||
$dialog.find(".outcomes_select:not(.blank):first").click();
|
||||
}
|
||||
}, function(data) {
|
||||
$dialog.find(".loading_message").text("Outcomes Retrieval failed unexpected. Please try again.");
|
||||
$("#find_outcome_criterion_dialog").data('current_rubric', $rubric);
|
||||
find_outcome.find(function($outcome) {
|
||||
if(!$("#find_outcome_criterion_dialog").data('current_rubric')) { return; }
|
||||
var $rubric = $("#find_outcome_criterion_dialog").data('current_rubric');
|
||||
var outcome_id = $outcome.find(".learning_outcome_id").text();
|
||||
$rubric.find(".criterion.learning_outcome_" + outcome_id).find(".delete_criterion_link").click();
|
||||
$rubric.find(".add_criterion_link").click();
|
||||
var $criterion = $rubric.find(".criterion:not(.blank):last");
|
||||
$criterion.toggleClass('ignore_criterion_for_scoring', !$outcome.find(".criterion_for_scoring").attr('checked'));
|
||||
$criterion.find(".mastery_points").val($outcome.find(".mastery_points").text());
|
||||
$criterion.addClass("learning_outcome_criterion");
|
||||
$criterion.find(".learning_outcome_id").text(outcome_id);
|
||||
$criterion.find(".criterion_points").val($outcome.find(".rating:not(.blank):first .points").text()).blur();
|
||||
for(var idx = 0; idx < $outcome.find(".rating:not(.blank)").length - 2; idx++) {
|
||||
$criterion.find(".rating:not(.blank):first").addClass('add_column').click();
|
||||
}
|
||||
$criterion.find(".rating:not(.blank)").each(function(i) {
|
||||
var data = $outcome.find(".rating:not(.blank)").eq(i).getTemplateData({textValues: ['description', 'points']});
|
||||
$(this).fillTemplateData({data: data});
|
||||
});
|
||||
}
|
||||
$dialog.dialog('close').dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
title: "Find Outcome Criterion",
|
||||
width: 700,
|
||||
height: 400
|
||||
}).dialog('open');
|
||||
var long_description = $outcome.find(".body.description").html();
|
||||
var mastery_points = $outcome.find(".mastery_points").text();
|
||||
$criterion.find(".cancel_button").click();
|
||||
$criterion.find(".long_description").val(long_description);
|
||||
$criterion.find(".long_description_holder").toggleClass('empty', !long_description);
|
||||
$criterion.find(".criterion_description_value").text($outcome.find(".short_description").text());
|
||||
$criterion.find(".criterion_description").val($outcome.find(".short_description").text()).focus().select();
|
||||
$criterion.find(".mastery_points").text(mastery_points);
|
||||
}, {for_rubric: true});
|
||||
},
|
||||
hideCriterionAdd: function($rubric) {
|
||||
$rubric.find('.add_right, .add_left, .add_column').removeClass('add_left add_right add_column');
|
||||
|
@ -673,46 +646,6 @@ $(document).ready(function() {
|
|||
$("#edit_rubric_form .cancel_button").click(function() {
|
||||
rubricEditing.hideEditRubric($(this).parents(".rubric"), true);
|
||||
});
|
||||
$("#find_outcome_criterion_dialog .outcomes_select").click(function(event) {
|
||||
event.preventDefault();
|
||||
$("#find_outcome_criterion_dialog .outcomes_select.selected_side_tab").removeClass('selected_side_tab');
|
||||
$(this).addClass('selected_side_tab');
|
||||
var id = $(this).getTemplateData({textValues: ['id']}).id;
|
||||
$("#find_outcome_criterion_dialog .outcomes_list .outcome").hide();
|
||||
$("#find_outcome_criterion_dialog .outcomes_list .outcome_" + id).show();
|
||||
});
|
||||
$("#find_outcome_criterion_dialog .select_outcome_link").click(function(event) {
|
||||
event.preventDefault();
|
||||
var $outcome = $(this).parents(".outcome");
|
||||
if(!$("#find_outcome_criterion_dialog").data('current_rubric')) { return; }
|
||||
var $rubric = $("#find_outcome_criterion_dialog").data('current_rubric');
|
||||
var outcome_id = $outcome.find(".learning_outcome_id").text();
|
||||
$rubric.find(".criterion.learning_outcome_" + outcome_id).find(".delete_criterion_link").click();
|
||||
$("#find_outcome_criterion_dialog").dialog('close');
|
||||
$rubric.find(".add_criterion_link").click();
|
||||
var $criterion = $rubric.find(".criterion:not(.blank):last");
|
||||
$criterion.toggleClass('ignore_criterion_for_scoring', !$outcome.find(".criterion_for_scoring").attr('checked'));
|
||||
$criterion.find(".mastery_points").val($outcome.find(".mastery_points").text());
|
||||
$criterion.addClass("learning_outcome_criterion");
|
||||
$criterion.find(".learning_outcome_id").text(outcome_id);
|
||||
$criterion.find(".criterion_points").val($outcome.find(".rating:not(.blank):first .points").text()).blur();
|
||||
for(var idx = 0; idx < $outcome.find(".rating:not(.blank)").length - 2; idx++) {
|
||||
$criterion.find(".rating:not(.blank):first").addClass('add_column').click();
|
||||
}
|
||||
$criterion.find(".rating:not(.blank)").each(function(i) {
|
||||
var data = $outcome.find(".rating:not(.blank)").eq(i).getTemplateData({textValues: ['description', 'points']});
|
||||
$(this).fillTemplateData({data: data});
|
||||
});
|
||||
var long_description = $outcome.find(".body.description").html();
|
||||
var mastery_points = $outcome.find(".mastery_points").text();
|
||||
$criterion.find(".cancel_button").click();
|
||||
$criterion.find(".long_description").val(long_description);
|
||||
$criterion.find(".long_description_holder").toggleClass('empty', !long_description);
|
||||
$criterion.find(".criterion_description_value").text($outcome.find(".short_description").text());
|
||||
$criterion.find(".criterion_description").val($outcome.find(".short_description").text()).focus().select();
|
||||
$criterion.find(".mastery_points").text(mastery_points);
|
||||
});
|
||||
|
||||
|
||||
$("#rubrics").delegate('.add_criterion_link', 'click', function(event) {
|
||||
var $criterion = rubricEditing.addCriterion($(this).parents(".rubric")); //"#default_rubric"));
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
var find_outcome = (function() {
|
||||
return {
|
||||
find: function(callback, options) {
|
||||
options = options || {};
|
||||
find_outcome.callback = callback;
|
||||
var $dialog = $("#find_outcome_criterion_dialog");
|
||||
if(!$dialog.hasClass('loaded')) {
|
||||
$dialog.find(".loading_message").text("Loading Outcomes...");
|
||||
$.ajaxJSON($dialog.find(".outcomes_list_url").attr('href'), 'GET', {}, function(data) {
|
||||
valids = [];
|
||||
for(var idx in data) {
|
||||
var outcome = data[idx].learning_outcome;
|
||||
if(!options.for_rubric || (outcome.data && outcome.data.rubric_criterion)) {
|
||||
valids.push(outcome);
|
||||
}
|
||||
}
|
||||
if(valids.length === 0) {
|
||||
$dialog.find(".loading_message").text("No" + (options.for_rubric ? " Rubric-Configured" : "") + " Outcomes found");
|
||||
} else {
|
||||
$dialog.find(".loading_message").hide();
|
||||
$dialog.addClass('loaded');
|
||||
for(var idx in valids) {
|
||||
var outcome = valids[idx];
|
||||
outcome.name = outcome.short_description;
|
||||
outcome.mastery_points = outcome.data.rubric_criterion.mastery_points || outcome.data.rubric_criterion.points_possible;
|
||||
var $name = $dialog.find(".outcomes_select.blank:first").clone(true).removeClass('blank');
|
||||
outcome.title = outcome.short_description;
|
||||
var $text = $("<div/>");
|
||||
$text.text(outcome.short_description);
|
||||
outcome.title = $.truncateText($.trim($text.text()), 35);
|
||||
outcome.display_name = outcome.cached_context_short_name || "";
|
||||
$name.fillTemplateData({data: outcome});
|
||||
$dialog.find(".outcomes_selects").append($name.show());
|
||||
var $outcome = $dialog.find(".outcome.blank:first").clone(true).removeClass('blank');
|
||||
$outcome
|
||||
.find(".mastery_level").attr('id', 'outcome_question_bank_mastery_' + outcome.id).end()
|
||||
.find(".mastery_level_text").attr('for', 'outcome_question_bank_mastery_' + outcome.id);
|
||||
outcome.learning_outcome_id = outcome.id;
|
||||
var criterion = outcome.data && outcome.data.rubric_criterion
|
||||
var pct = (criterion.points_possible && criterion.mastery_points != null && (criterion.mastery_points / criterion.points_possible)) || 0;
|
||||
pct = (Math.round(pct * 10000) / 100.0) || "";
|
||||
$outcome.find(".mastery_level").val(pct);
|
||||
$outcome.fillTemplateData({data: outcome, htmlValues: ['description']});
|
||||
$outcome.addClass('outcome_' + outcome.id);
|
||||
if(outcome.data && outcome.data.rubric_criterion) {
|
||||
for(var jdx in outcome.data.rubric_criterion.ratings) {
|
||||
var rating = outcome.data.rubric_criterion.ratings[jdx];
|
||||
var $rating = $outcome.find(".rating.blank").clone(true).removeClass('blank');
|
||||
$rating.fillTemplateData({data: rating});
|
||||
$outcome.find("tr").append($rating.show());
|
||||
}
|
||||
}
|
||||
$dialog.find(".outcomes_list").append($outcome);
|
||||
}
|
||||
$dialog.find(".outcomes_select:not(.blank):first").click();
|
||||
}
|
||||
}, function(data) {
|
||||
$dialog.find(".loading_message").text("Outcomes Retrieval failed unexpected. Please try again.");
|
||||
});
|
||||
}
|
||||
$dialog.dialog('close').dialog({
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
title: "Find Outcome" + (options.for_rubric ? " Criterion" : ""),
|
||||
width: 700,
|
||||
height: 400
|
||||
}).dialog('open');
|
||||
}
|
||||
}
|
||||
})();
|
||||
window.find_outcome = find_outcome;
|
||||
$(document).ready(function() {
|
||||
$("#find_outcome_criterion_dialog .outcomes_select").click(function(event) {
|
||||
event.preventDefault();
|
||||
$("#find_outcome_criterion_dialog .outcomes_select.selected_side_tab").removeClass('selected_side_tab');
|
||||
$(this).addClass('selected_side_tab');
|
||||
var id = $(this).getTemplateData({textValues: ['id']}).id;
|
||||
$("#find_outcome_criterion_dialog .outcomes_list .outcome").hide();
|
||||
$("#find_outcome_criterion_dialog .outcomes_list .outcome_" + id).show();
|
||||
});
|
||||
$("#find_outcome_criterion_dialog .select_outcome_link").click(function(event) {
|
||||
event.preventDefault();
|
||||
var $outcome = $(this).parents(".outcome");
|
||||
$("#find_outcome_criterion_dialog").dialog('close');
|
||||
if($.isFunction(find_outcome.callback)) {
|
||||
find_outcome.callback($outcome);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
$(document).ready(function() {
|
||||
function updateOutcomes(outcomes) {
|
||||
$(".add_outcome_text").text("Updating Outcomes...").attr('disabled', true);
|
||||
var params = {};
|
||||
for(var idx in outcomes) {
|
||||
var outcome = outcomes[idx];
|
||||
params['assessment_question_bank[outcomes][' + outcome[0] + ']'] = outcome[1];
|
||||
}
|
||||
if(outcomes.length == 0) {
|
||||
params['assessment_question_bank[outcomes]'] = '';
|
||||
}
|
||||
var url = $(".edit_bank_link").attr('href');
|
||||
$.ajaxJSON(url, 'PUT', params, function(data) {
|
||||
var tags = data.assessment_question_bank.learning_outcome_tags.sort(function(a, b) {
|
||||
var a_name = ((a.content_tag && a.content_tag.learning_outcome && a.content_tag.learning_outcome.short_description) || 'none').toLowerCase();
|
||||
var b_name = ((b.content_tag && b.content_tag.learning_outcome && b.content_tag.learning_outcome.short_description) || 'none').toLowerCase();
|
||||
if(a_name < b_name) { return -1; }
|
||||
else if(a_name > b_name) { return 1; }
|
||||
else { return 0; }
|
||||
});
|
||||
$(".add_outcome_text").text("Align Outcomes").attr('disabled', false);
|
||||
var $outcomes = $("#aligned_outcomes_list");
|
||||
$outcomes.find(".outcome:not(.blank)").remove();
|
||||
var $template = $outcomes.find(".blank:first").clone(true).removeClass('blank');
|
||||
for(var idx in tags) {
|
||||
var tag = tags[idx].content_tag;
|
||||
var outcome = {
|
||||
short_description: tag.learning_outcome.short_description,
|
||||
mastery_threshold: Math.round(tag.mastery_score * 10000) / 100.0
|
||||
};
|
||||
var $outcome = $template.clone(true);
|
||||
$outcome.attr('data-id', tag.learning_outcome_id);
|
||||
$outcome.fillTemplateData({
|
||||
data: outcome
|
||||
});
|
||||
$outcomes.append($outcome.show());
|
||||
}
|
||||
}, function(data) {
|
||||
$(".add_outcome_text").text("Updating Outcomes Failed").attr('disabled', false);
|
||||
});
|
||||
}
|
||||
$("#aligned_outcomes_list").delegate('.delete_outcome_link', 'click', function(event) {
|
||||
event.preventDefault();
|
||||
var result = confirm("Are you sure you want to remove this outcome from the bank?");
|
||||
var $outcome = $(this).parents(".outcome");
|
||||
var outcomes = [];
|
||||
var outcome_id = $outcome.attr('data-id');
|
||||
if(result) {
|
||||
$(this).parents(".outcome").dim();
|
||||
$("#aligned_outcomes_list .outcome:not(.blank)").each(function() {
|
||||
var id = $(this).attr('data-id');
|
||||
var pct = $(this).getTemplateData({textValues: ['mastery_threshold']}).mastery_threshold / 100;
|
||||
if(id != outcome_id) {
|
||||
outcomes.push([id, pct]);
|
||||
}
|
||||
});
|
||||
updateOutcomes(outcomes);
|
||||
}
|
||||
});
|
||||
$(".add_outcome_link").click(function(event) {
|
||||
event.preventDefault();
|
||||
find_outcome.find(function($outcome) {
|
||||
var outcome_id = $outcome.find(".learning_outcome_id").text();
|
||||
var mastery = (parseFloat($outcome.find(".mastery_level").val()) / 100.0) || 1.0;
|
||||
var outcomes = [];
|
||||
$("#aligned_outcomes_list .outcome:not(.blank)").each(function() {
|
||||
var id = $(this).attr('data-id');
|
||||
var pct = $(this).getTemplateData({textValues: ['mastery_threshold']}).mastery_threshold / 100.0;
|
||||
outcomes.push([id, pct]);
|
||||
});
|
||||
outcomes.push([outcome_id, mastery]);
|
||||
updateOutcomes(outcomes);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -334,7 +334,7 @@ describe Quiz do
|
|||
s.quiz_data.should_not be_nil
|
||||
s.quiz_version.should eql(q.version_number)
|
||||
s.finished_at.should be_nil
|
||||
s.submission_data.should be_nil
|
||||
s.submission_data.should eql({})
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -112,4 +112,90 @@ describe QuizSubmission do
|
|||
|
||||
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
|
||||
@bank.outcomes = {@outcome.id => 0.7}
|
||||
@bank.save!
|
||||
@bank.learning_outcome_tags.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
|
||||
@bank.outcomes = {@outcome.id => 0.7}
|
||||
@bank.save!
|
||||
@bank.learning_outcome_tags.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
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue