quiz questions now soft-delete

Adds workflow state to QuizQuestion models.

Test plan:
  - Make a quiz, have a student take the quiz.
  - Set some regrade options on the quiz and republish the quiz.
  - Go back to the edit page for the quiz and delete a quiz question.
    You should not get a error when you do this.
  - Make sure the quiz question does not show up by refreshing the
    quiz's edit page.

Change-Id: If4bcb0ad28217a527080da9aa32ce6bf7921bfdb
Reviewed-on: https://gerrit.instructure.com/24111
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jacob Fugal <jacob@instructure.com>
QA-Review: Amber Taniuchi <amber@instructure.com>
Product-Review: Stanley Stuart <stanley@instructure.com>
This commit is contained in:
Stanley Stuart 2013-09-05 14:47:29 -05:00
parent 1d0d6249e6
commit 0209b5a4c4
23 changed files with 159 additions and 54 deletions

View File

@ -62,8 +62,8 @@ class QuizGroupsController < ApplicationController
if authorized_action(@quiz, @current_user, :update) if authorized_action(@quiz, @current_user, :update)
@group = @quiz.quiz_groups.find(params[:quiz_group_id]) @group = @quiz.quiz_groups.find(params[:quiz_group_id])
items = [] items = []
group_questions = @group.quiz_questions group_questions = @group.quiz_questions.active
questions = @quiz.quiz_questions questions = @quiz.quiz_questions.active
order = params[:order].split(",") order = params[:order].split(",")
order.each do |name| order.each do |name|
id = name.gsub(/\Aquestion_/, "").to_i id = name.gsub(/\Aquestion_/, "").to_i

View File

@ -21,7 +21,7 @@ class QuizQuestionsController < ApplicationController
def show def show
if authorized_action(@quiz, @current_user, :update) if authorized_action(@quiz, @current_user, :update)
@question = @quiz.quiz_questions.find(params[:id]) @question = @quiz.quiz_questions.active.find(params[:id])
render :json => @question.to_json(:include => :assessment_question) render :json => @question.to_json(:include => :assessment_question)
end end
end end
@ -55,7 +55,7 @@ class QuizQuestionsController < ApplicationController
def update def update
if authorized_action(@quiz, @current_user, :update) if authorized_action(@quiz, @current_user, :update)
@question = @quiz.quiz_questions.find(params[:id]) @question = @quiz.quiz_questions.active.find(params[:id])
question_data = params[:question] question_data = params[:question]
question_data[:regrade_user] = @current_user question_data[:regrade_user] = @current_user
question_data ||= {} question_data ||= {}
@ -64,7 +64,7 @@ class QuizQuestionsController < ApplicationController
@group = @quiz.quiz_groups.find(question_data[:quiz_group_id]) @group = @quiz.quiz_groups.find(question_data[:quiz_group_id])
if question_data[:quiz_group_id] != @question.quiz_group_id if question_data[:quiz_group_id] != @question.quiz_group_id
@question.quiz_group_id = question_data[:quiz_group_id] @question.quiz_group_id = question_data[:quiz_group_id]
@question.position = @group.quiz_questions.length @question.position = @group.quiz_questions.active.length
end end
end end
@ -78,7 +78,7 @@ class QuizQuestionsController < ApplicationController
def destroy def destroy
if authorized_action(@quiz, @current_user, :update) if authorized_action(@quiz, @current_user, :update)
@question = @quiz.quiz_questions.find(params[:id]) @question = @quiz.quiz_questions.active.find(params[:id])
@question.destroy @question.destroy
render :json => @question.to_json render :json => @question.to_json
end end

View File

@ -376,7 +376,7 @@ class QuizzesController < ApplicationController
if authorized_action(@quiz, @current_user, :update) if authorized_action(@quiz, @current_user, :update)
items = [] items = []
groups = @quiz.quiz_groups groups = @quiz.quiz_groups
questions = @quiz.quiz_questions questions = @quiz.quiz_questions.active
order = params[:order].split(",") order = params[:order].split(",")
order.each_index do |idx| order.each_index do |idx|
name = order[idx] name = order[idx]
@ -387,7 +387,7 @@ class QuizzesController < ApplicationController
obj = groups.detect{|g| g.id == id.to_i} if id != 0 && name.match(/\Agroup/) obj = groups.detect{|g| g.id == id.to_i} if id != 0 && name.match(/\Agroup/)
items << obj if obj items << obj if obj
end end
root_questions = @quiz.quiz_questions.where("quiz_group_id IS NULL").all root_questions = @quiz.quiz_questions.active.where("quiz_group_id IS NULL").all
items += root_questions items += root_questions
items.uniq! items.uniq!
question_updates = [] question_updates = []

View File

@ -368,19 +368,34 @@ class Quiz < ActiveRecord::Base
end end
def root_entries_max_position def root_entries_max_position
question_max = self.quiz_questions.maximum(:position, :conditions => 'quiz_group_id is null') question_max = self.active_quiz_questions.maximum(:position, :conditions => 'quiz_group_id is null')
group_max = self.quiz_groups.maximum(:position) group_max = self.quiz_groups.maximum(:position)
[question_max, group_max, 0].compact.max [question_max, group_max, 0].compact.max
end end
def active_quiz_questions_without_group
if self.quiz_questions.loaded?
active_quiz_questions.select { |q| !q.quiz_group_id }
else
active_quiz_questions.where(quiz_group_id: nil).all
end
end
def active_quiz_questions
if self.quiz_questions.loaded?
quiz_questions.select(&:active?)
else
quiz_questions.active
end
end
# Returns the list of all "root" entries, either questions or question # Returns the list of all "root" entries, either questions or question
# groups for this quiz. This is PRE-SAVED data. Once the quiz has # groups for this quiz. This is PRE-SAVED data. Once the quiz has
# been saved, all the data can be found in Quiz.quiz_data # been saved, all the data can be found in Quiz.quiz_data
def root_entries(force_check=false) def root_entries(force_check=false)
return @root_entries if @root_entries && !force_check return @root_entries if @root_entries && !force_check
result = [] result = []
all_questions = self.quiz_questions result.concat self.active_quiz_questions_without_group
result.concat all_questions.select{|q| !q.quiz_group_id }
result.concat self.quiz_groups result.concat self.quiz_groups
result = result.sort_by{|e| e.position || 99999}.map do |e| result = result.sort_by{|e| e.position || 99999}.map do |e|
res = nil res = nil
@ -393,7 +408,7 @@ class Quiz < ActiveRecord::Base
data[:assessment_question_bank_id] = e.assessment_question_bank_id data[:assessment_question_bank_id] = e.assessment_question_bank_id
data[:questions] = [] data[:questions] = []
else else
data[:questions] = e.quiz_questions.sort_by{|q| q.position || 99999}.map(&:data) data[:questions] = e.quiz_questions.active.sort_by{|q| q.position || 99999}.map(&:data)
end end
data[:actual_pick_count] = e.actual_pick_count data[:actual_pick_count] = e.actual_pick_count
res = data res = data
@ -756,7 +771,7 @@ class Quiz < ActiveRecord::Base
end end
def migrate_content_links_by_hand(user) def migrate_content_links_by_hand(user)
self.quiz_questions.each do |question| self.quiz_questions.active.each do |question|
data = QuizQuestion.migrate_question_hash(question.question_data, :context => self.context, :user => user) data = QuizQuestion.migrate_question_hash(question.question_data, :context => self.context, :user => user)
question.write_attribute(:question_data, data) question.write_attribute(:question_data, data)
question.save question.save
@ -866,7 +881,7 @@ class Quiz < ActiveRecord::Base
raise e if retrying raise e if retrying
return self.clone_for(context, original_dup, options, true) return self.clone_for(context, original_dup, options, true)
end end
entities = self.quiz_groups + self.quiz_questions entities = self.quiz_groups + self.active_quiz_questions
entities.each do |entity| entities.each do |entity|
entity_dup = entity.clone_for(dup, nil, :old_context => self.context, :new_context => context) entity_dup = entity.clone_for(dup, nil, :old_context => self.context, :new_context => context)
entity_dup.quiz_id = dup.id entity_dup.quiz_id = dup.id
@ -951,7 +966,7 @@ class Quiz < ActiveRecord::Base
return false if has_student_submissions? return false if has_student_submissions?
self.question_count = 0 self.question_count = 0
self.quiz_questions.destroy_all self.quiz_questions.active.map(&:destroy)
self.quiz_groups.destroy_all self.quiz_groups.destroy_all
self.quiz_data = nil self.quiz_data = nil
true true
@ -1031,7 +1046,7 @@ class Quiz < ActiveRecord::Base
hash[:questions] ||= [] hash[:questions] ||= []
if question_data[:qq_data] || question_data[:aq_data] if question_data[:qq_data] || question_data[:aq_data]
existing_questions = item.quiz_questions.where("migration_id IS NOT NULL").select([:id, :migration_id]).index_by(&:migration_id) existing_questions = item.quiz_questions.active.where("migration_id IS NOT NULL").select([:id, :migration_id]).index_by(&:migration_id)
end end
if question_data[:qq_data] if question_data[:qq_data]

View File

@ -40,7 +40,7 @@ class QuizGroup < ActiveRecord::Base
# don't do a valid question check because we don't want to instantiate all the bank's questions # don't do a valid question check because we don't want to instantiate all the bank's questions
count = self.assessment_question_bank.assessment_question_count count = self.assessment_question_bank.assessment_question_count
else else
count = self.quiz_questions.select{|q| q.unsupported != true}.length rescue self.quiz_questions.length count = self.quiz_questions.active.count
end end
[self.pick_count.to_i, count].min [self.pick_count.to_i, count].min
@ -66,7 +66,7 @@ class QuizGroup < ActiveRecord::Base
"name" => self.name, "name" => self.name,
"pick_count" => self.pick_count, "pick_count" => self.pick_count,
"question_points" => self.question_points, "question_points" => self.question_points,
"questions" => self.assessment_question_bank_id ? [] : self.quiz_questions.map{|q| q.data}, "questions" => self.assessment_question_bank_id ? [] : self.quiz_questions.active.map{|q| q.data},
"assessment_question_bank_id" => self.assessment_question_bank_id "assessment_question_bank_id" => self.assessment_question_bank_id
}.with_indifferent_access }.with_indifferent_access
end end

View File

@ -19,6 +19,8 @@
require 'quiz_question_link_migrator' require 'quiz_question_link_migrator'
class QuizQuestion < ActiveRecord::Base class QuizQuestion < ActiveRecord::Base
include Workflow
attr_accessible :quiz, :quiz_group, :assessment_question, :question_data, :assessment_question_version attr_accessible :quiz, :quiz_group, :assessment_question, :question_data, :assessment_question_version
attr_readonly :quiz_id attr_readonly :quiz_id
belongs_to :quiz belongs_to :quiz
@ -31,11 +33,18 @@ class QuizQuestion < ActiveRecord::Base
validates_presence_of :quiz_id validates_presence_of :quiz_id
serialize :question_data serialize :question_data
after_save :update_quiz after_save :update_quiz
workflow do
state :active
state :deleted
end
scope :active, where("workflow_state='active' OR workflow_state IS NULL")
def infer_defaults def infer_defaults
if !self.position && self.quiz if !self.position && self.quiz
if self.quiz_group if self.quiz_group
self.position = (self.quiz_group.quiz_questions.map(&:position).compact.max || 0) + 1 self.position = (self.quiz_group.quiz_questions.active.map(&:position).compact.max || 0) + 1
else else
self.position = self.quiz.root_entries_max_position + 1 self.position = self.quiz.root_entries_max_position + 1
end end
@ -180,6 +189,12 @@ class QuizQuestion < ActiveRecord::Base
end end
end end
alias_method :destroy!, :destroy
def destroy
self.workflow_state = 'deleted'
self.save
end
private private
def update_question_regrade(regrade_option, regrade_user) def update_question_regrade(regrade_option, regrade_user)

View File

@ -19,7 +19,7 @@
<li> <li>
<div id='quiz-nav-inner-wrapper'> <div id='quiz-nav-inner-wrapper'>
<ul> <ul>
<% @quiz.quiz_questions.each_with_index do |question, i| %> <% @quiz.active_quiz_questions.each_with_index do |question, i| %>
<% index = i + 1 %> <% index = i + 1 %>
<li id='quiz_nav_<%= question.id %>' class="q<%= index %> quiz-nav-li" data-id='<%= question.id %>' data-index='<%=i%>'> <li id='quiz_nav_<%= question.id %>' class="q<%= index %> quiz-nav-li" data-id='<%= question.id %>' data-index='<%=i%>'>
<a class='question_<%= question.id %> question-nav-link' data-id='<%= question.id %>' href="#question_<%= question.id %>"> <a class='question_<%= question.id %> question-nav-link' data-id='<%= question.id %>' href="#question_<%= question.id %>">

View File

@ -325,11 +325,11 @@
<div id="questions_tab"> <div id="questions_tab">
<div id="show_question_details_wrap"> <div id="show_question_details_wrap">
<input type="checkbox" id="show_question_details" <input type="checkbox" id="show_question_details"
<%= "disabled='disabled'" if @quiz.quiz_questions.count > QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT %> <%= "disabled='disabled'" if @quiz.active_quiz_questions.size> QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT %>
/> />
<label for="show_question_details"> <%= t('labels.show_question_details', "Show Question Details") %></label> <label for="show_question_details"> <%= t('labels.show_question_details', "Show Question Details") %></label>
<br/> <br/>
<% if @quiz.quiz_questions.count > QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT %> <% if @quiz.quiz_questions.active.size > QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT %>
<span id='question-detail-disabled' class='hint'> <span id='question-detail-disabled' class='hint'>
<%= t('hints.question_detail_disabled', 'NOTE: Question details not available when more than %{question_count}.', <%= t('hints.question_detail_disabled', 'NOTE: Question details not available when more than %{question_count}.',
:question_count => QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT) %> :question_count => QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT) %>
@ -341,7 +341,7 @@
<% if question[:entry_type] == "quiz_group" %> <% if question[:entry_type] == "quiz_group" %>
<%= render :partial => "quizzes/question_group", :object => question %> <%= render :partial => "quizzes/question_group", :object => question %>
<% else %> <% else %>
<%= render :partial => (@quiz.quiz_questions.count > QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT ? "quizzes/question_teaser" : "quizzes/display_question"), :object => question, :locals => {:editing => true, :asset_string => "quiz_question_#{question['id']}"} %> <%= render :partial => (@quiz.quiz_questions.active.size > QuizzesController::QUIZ_QUESTIONS_DETAIL_LIMIT ? "quizzes/question_teaser" : "quizzes/display_question"), :object => question, :locals => {:editing => true, :asset_string => "quiz_question_#{question['id']}"} %>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>

View File

@ -33,7 +33,7 @@
<% if question[:entry_type] == "quiz_group" %> <% if question[:entry_type] == "quiz_group" %>
<%= render :partial => "quizzes/question_group", :object => question %> <%= render :partial => "quizzes/question_group", :object => question %>
<% else %> <% else %>
<%= render :partial => (@quiz.quiz_questions.count > 25 ? "quizzes/question_teaser" : "quizzes/display_question"), :object => question, :locals => {:editing => true, :asset_string => "quiz_question_#{question['id']}"} %> <%= render :partial => (@quiz.active_quiz_questions.size > 25 ? "quizzes/question_teaser" : "quizzes/display_question"), :object => question, :locals => {:editing => true, :asset_string => "quiz_question_#{question['id']}"} %>
<% end %> <% end %>
<% end %> <% end %>
</div> </div>

View File

@ -0,0 +1,11 @@
class AddWorkflowStateToQuizQuestion < ActiveRecord::Migration
tag :predeploy
def self.up
add_column :quiz_questions, :workflow_state, :string
end
def self.down
remove_column :quiz_questions, :workflow_state
end
end

View File

@ -0,0 +1,13 @@
class GiveWorkflowStateToQuizQuestionsWithoutWorkflowState < ActiveRecord::Migration
tag :postdeploy
def self.up
DataFixup::AddWorkflowStateForQuizQuestions.
send_later_if_production_enqueue_args(:run,
priority: Delayed::LOW_PRIORITY,
max_attempts: 1)
end
def self.down
end
end

View File

@ -0,0 +1,11 @@
module DataFixup
module AddWorkflowStateForQuizQuestions
def self.run
scope = QuizQuestion.where(workflow_state: nil)
QuizQuestion.find_ids_in_ranges do |first_id, last_id|
scope.where(id: first_id..last_id).update_all(workflow_state: 'active')
end
end
end
end

View File

@ -15,7 +15,7 @@ module QuizQuestionDataFixer
question.write_attribute(:question_data, good_data) question.write_attribute(:question_data, good_data)
question.with_versioning(&:save) question.with_versioning(&:save)
question.quiz_questions.each do |qq| question.quiz_questions.active.each do |qq|
if !is_valid_data(qq.question_data) if !is_valid_data(qq.question_data)
if qq.quiz && qq.quiz.published_at && !seen_quizzes[qq.quiz_id] if qq.quiz && qq.quiz.published_at && !seen_quizzes[qq.quiz_id]
Rails.logger.info("The quiz #{qq.quiz_id} may need to be republished.") Rails.logger.info("The quiz #{qq.quiz_id} may need to be republished.")
@ -58,11 +58,11 @@ module QuizQuestionDataFixer
end end
#try to find a good quiz question #try to find a good quiz question
aq.quiz_questions.each do |qq| aq.quiz_questions.active.each do |qq|
return qq.question_data if qq.question_data && is_valid_data(qq.question_data) return qq.question_data if qq.question_data && is_valid_data(qq.question_data)
end end
nil nil
end end
end end

View File

@ -152,13 +152,13 @@ describe "Standard Common Cartridge importing" do
it "should import assessment data" do it "should import assessment data" do
if Qti.qti_enabled? if Qti.qti_enabled?
quiz = @course.quizzes.find_by_migration_id("I_00003_R") quiz = @course.quizzes.find_by_migration_id("I_00003_R")
quiz.quiz_questions.count.should == 11 quiz.active_quiz_questions.size.should == 11
quiz.title.should == "Pretest" quiz.title.should == "Pretest"
quiz.quiz_type.should == 'assignment' quiz.quiz_type.should == 'assignment'
quiz.allowed_attempts.should == 2 quiz.allowed_attempts.should == 2
quiz.time_limit.should == 120 quiz.time_limit.should == 120
question = quiz.quiz_questions.first question = quiz.active_quiz_questions.first
question.question_data[:points_possible].should == 2 question.question_data[:points_possible].should == 2
bank = @course.assessment_question_banks.find_by_migration_id("I_00004_R_QDB_1") bank = @course.assessment_question_banks.find_by_migration_id("I_00004_R_QDB_1")

View File

@ -0,0 +1,20 @@
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper.rb")
describe DataFixup::AddWorkflowStateForQuizQuestions do
it "adds an active workflow_state to every existing & active quiz question" do
course_with_teacher_logged_in(active_all: true)
quiz = @course.quizzes.create!(title: "Test Quiz")
question_1 = quiz.quiz_questions.create!
question_1.update_attribute(:workflow_state, nil)
question_2 = quiz.quiz_questions.create!
question_2.destroy
DataFixup::AddWorkflowStateForQuizQuestions.run
question_2.reload.workflow_state.should == 'deleted'
question_1.reload.workflow_state.should == 'active'
end
end

View File

@ -41,9 +41,9 @@ describe "Quiz Import" do
data = get_import_data ['vista', 'quiz'], 'simple_quiz_data' data = get_import_data ['vista', 'quiz'], 'simple_quiz_data'
Quiz.import_from_migration(data, context, question_data) Quiz.import_from_migration(data, context, question_data)
quiz = Quiz.find_by_migration_id data[:migration_id] quiz = Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_questions.count.should == 1 quiz.quiz_questions.active.count.should == 1
# Check if the expected question name is in there # Check if the expected question name is in there
quiz.quiz_questions.first.question_data[:question_name].should == "Rocket Bee!" quiz.quiz_questions.active.first.question_data[:question_name].should == "Rocket Bee!"
end end
it "should import a text only question" do it "should import a text only question" do
@ -53,8 +53,8 @@ describe "Quiz Import" do
Quiz.import_from_migration(data, context, question_data) Quiz.import_from_migration(data, context, question_data)
quiz = Quiz.find_by_migration_id data[:migration_id] quiz = Quiz.find_by_migration_id data[:migration_id]
quiz.unpublished_question_count.should == 2 quiz.unpublished_question_count.should == 2
quiz.quiz_questions.count.should == 2 quiz.quiz_questions.active.count.should == 2
sorted_questions = quiz.quiz_questions.sort { |a, b| a.id <=> b.id } sorted_questions = quiz.quiz_questions.active.sort { |a, b| a.id <=> b.id }
sorted_questions.first.question_data[:question_text].should == data[:questions].first[:question_text] sorted_questions.first.question_data[:question_text].should == data[:questions].first[:question_text]
sorted_questions.first.question_data[:question_type].should == 'text_only_question' sorted_questions.first.question_data[:question_type].should == 'text_only_question'
end end
@ -66,7 +66,7 @@ describe "Quiz Import" do
Quiz.import_from_migration(data, context, question_data) Quiz.import_from_migration(data, context, question_data)
quiz = Quiz.find_by_migration_id data[:migration_id] quiz = Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_groups.count.should == 1 quiz.quiz_groups.count.should == 1
quiz.quiz_groups.first.quiz_questions.count.should == 3 quiz.quiz_groups.first.quiz_questions.active.count.should == 3
quiz.quiz_groups.first.pick_count.should == data[:questions].first[:pick_count] quiz.quiz_groups.first.pick_count.should == data[:questions].first[:pick_count]
quiz.quiz_groups.first.question_points.should == data[:questions].first[:question_points] quiz.quiz_groups.first.question_points.should == data[:questions].first[:question_points]
end end
@ -113,12 +113,12 @@ describe "Quiz Import" do
Quiz.import_from_migration(data, context, question_data) Quiz.import_from_migration(data, context, question_data)
quiz = Quiz.find_by_migration_id data[:migration_id] quiz = Quiz.find_by_migration_id data[:migration_id]
quiz.quiz_questions.first.question_data[:question_name].should == "Rocket Bee!" quiz.quiz_questions.active.first.question_data[:question_name].should == "Rocket Bee!"
question_data[:aq_data][data['questions'].first[:migration_id]]['question_name'] = "Not Rocket Bee?" question_data[:aq_data][data['questions'].first[:migration_id]]['question_name'] = "Not Rocket Bee?"
Quiz.import_from_migration(data, context, question_data) Quiz.import_from_migration(data, context, question_data)
quiz.quiz_questions.first.question_data[:question_name].should == "Not Rocket Bee?" quiz.quiz_questions.active.first.question_data[:question_name].should == "Not Rocket Bee?"
end end
end end

View File

@ -1625,7 +1625,7 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
att4_2 = @copy_to.attachments.find_by_migration_id(mig_id(att4)) att4_2 = @copy_to.attachments.find_by_migration_id(mig_id(att4))
q_to = @copy_to.quizzes.first q_to = @copy_to.quizzes.first
qq_to = q_to.quiz_questions.first qq_to = q_to.active_quiz_questions.first
qq_to.question_data[:question_text].should match_ignoring_whitespace(qtext % [@copy_to.id, att_2.id, @copy_to.id, "files/#{att2_2.id}/preview", @copy_to.id, "files/#{att4_2.id}/preview"]) qq_to.question_data[:question_text].should match_ignoring_whitespace(qtext % [@copy_to.id, att_2.id, @copy_to.id, "files/#{att2_2.id}/preview", @copy_to.id, "files/#{att4_2.id}/preview"])
qq_to.question_data[:answers][0][:html].should match_ignoring_whitespace(%{File ref:<img src="/courses/#{@copy_to.id}/files/#{att3_2.id}/download">}) qq_to.question_data[:answers][0][:html].should match_ignoring_whitespace(%{File ref:<img src="/courses/#{@copy_to.id}/files/#{att3_2.id}/download">})
end end
@ -1705,9 +1705,9 @@ equation: <img class="equation_image" title="Log_216" src="/equation_images/Log_
@copy_to.quizzes.count.should == 1 @copy_to.quizzes.count.should == 1
quiz = @copy_to.quizzes.first quiz = @copy_to.quizzes.first
quiz.quiz_questions.count.should == 1 quiz.active_quiz_questions.size.should == 1
qq = quiz.quiz_questions.first qq = quiz.active_quiz_questions.first
qq.question_data['question_type'].should == data[:question_type] qq.question_data['question_type'].should == data[:question_type]
qq.question_data['question_text'].should == data[:question_text] qq.question_data['question_text'].should == data[:question_text]
end end

View File

@ -23,12 +23,13 @@ describe QuizGroup do
it "should generate valid data" do it "should generate valid data" do
course course
quiz = @course.quizzes.create!(:title => "some quiz") quiz = @course.quizzes.create!(:title => "some quiz")
g = QuizGroup.create(:name => "question group", :pick_count => 2, :question_points => 5.0) g = quiz.quiz_groups.create(:name => "question group", :pick_count => 2, :question_points => 5.0)
g.quiz_questions << quiz.quiz_questions.create!(:question_data => {'name' => 'test question', 'answers' => [{'id' => 1}, {'id' => 2}]}) g.quiz_questions << quiz.quiz_questions.create!(:question_data => {'name' => 'test question', 'answers' => [{'id' => 1}, {'id' => 2}]})
g.quiz_questions << quiz.quiz_questions.create!(:question_data => {'name' => 'test question 2', 'answers' => [{'id' => 3}, {'id' => 4}]}) g.quiz_questions << quiz.quiz_questions.create!(:question_data => {'name' => 'test question 2', 'answers' => [{'id' => 3}, {'id' => 4}]})
g.name.should eql("question group") g.name.should eql("question group")
g.pick_count.should eql(2) g.pick_count.should eql(2)
g.question_points.should eql(5.0) g.question_points.should eql(5.0)
g.save!
data = g.data data = g.data
data[:name].should eql("question group") data[:name].should eql("question group")
@ -36,6 +37,7 @@ describe QuizGroup do
data[:question_points].should eql(5.0) data[:question_points].should eql(5.0)
data[:questions].should_not be_empty data[:questions].should_not be_empty
data[:questions].length.should eql(2) data[:questions].length.should eql(2)
data[:questions].sort_by! { |q| q[:id] }
data[:questions][0][:name].should eql("test question") data[:questions][0][:name].should eql("test question")
data[:questions][1][:name].should eql("test question 2") data[:questions][1][:name].should eql("test question 2")
end end
@ -46,7 +48,7 @@ describe QuizGroup do
group = quiz.quiz_groups.create!(:name => "question group", :pick_count => 3, :question_points => 5.0) group = quiz.quiz_groups.create!(:name => "question group", :pick_count => 3, :question_points => 5.0)
group.quiz_questions.create!(:quiz=>quiz, :question_data => {'name' => 'test question', 'answers' => [{'id' => 1}, {'id' => 2}]}) group.quiz_questions.create!(:quiz=>quiz, :question_data => {'name' => 'test question', 'answers' => [{'id' => 1}, {'id' => 2}]})
group.quiz_questions.create!(:quiz=>quiz, :question_data => {'name' => 'test question 2', 'answers' => [{'id' => 3}, {'id' => 4}]}) group.quiz_questions.create!(:quiz=>quiz, :question_data => {'name' => 'test question 2', 'answers' => [{'id' => 3}, {'id' => 4}]})
group.quiz_questions.count.should == 2 group.quiz_questions.active.size.should == 2
group.pick_count.should == 3 group.pick_count.should == 3
group.actual_pick_count.should == 2 group.actual_pick_count.should == 2

View File

@ -143,4 +143,19 @@ describe QuizQuestion do
}.to change(Attachment, :count).by(@attachment_count) }.to change(Attachment, :count).by(@attachment_count)
end end
end end
describe "#destroy" do
it "does not remove the record from the database, but changes workflow_state" do
course_with_teacher
course_quiz
question = @quiz.quiz_questions.create!
question.destroy
question = QuizQuestion.find(question.id)
question.should_not be_nil
question.should be_deleted
end
end
end end

View File

@ -354,9 +354,9 @@ describe Quiz do
q.quiz_questions.create!(:question_data => { :name => "test 3" }) q.quiz_questions.create!(:question_data => { :name => "test 3" })
q.quiz_questions.create!(:question_data => { :name => "test 4" }) q.quiz_questions.create!(:question_data => { :name => "test 4" })
q.save q.save
q.quiz_questions.length.should eql(4) q.active_quiz_questions.size.should eql(4)
q.quiz_groups.length.should eql(1) q.quiz_groups.length.should eql(1)
g.quiz_questions(true).length.should eql(2) g.quiz_questions(true).active.size.should eql(2)
entries = q.root_entries(true) entries = q.root_entries(true)
entries.length.should eql(3) entries.length.should eql(3)
@ -617,9 +617,12 @@ describe Quiz do
new_q.context.should_not eql(q.context) new_q.context.should_not eql(q.context)
new_q.title.should eql(q.title) new_q.title.should eql(q.title)
new_q.quiz_groups.length.should eql(q.quiz_groups.length) new_q.quiz_groups.length.should eql(q.quiz_groups.length)
new_q.quiz_questions.length.should eql(q.quiz_questions.length)
new_q.quiz_questions.first.question_data[:id].should be_nil new_q_questions = new_q.active_quiz_questions
new_q.quiz_questions.first.data[:id].should == new_q.quiz_questions.first.id
new_q_questions.size.should eql(q.quiz_questions.active.length)
new_q_questions.first.question_data[:id].should be_nil
new_q_questions.first.data[:id].should == new_q.quiz_questions.active.first.id
end end
it "should set the related assignment's group correctly" do it "should set the related assignment's group correctly" do
@ -647,7 +650,7 @@ describe Quiz do
q.save q.save
course course
new_q = q.clone_for(@course) new_q = q.clone_for(@course)
new_q.quiz_questions.first.question_data[:question_text].should match /\/courses\/#{@course.id}\/quizzes\/#{new_q.id}\/edit/ new_q.active_quiz_questions.first.question_data[:question_text].should match /\/courses\/#{@course.id}\/quizzes\/#{new_q.id}\/edit/
end end
it "should only create one associated assignment for a graded quiz" do it "should only create one associated assignment for a graded quiz" do

View File

@ -23,7 +23,7 @@ describe QuizStatistics::ItemAnalysis do
end end
it "should generate a csv" do it "should generate a csv" do
qs = @quiz.quiz_questions qs = @quiz.active_quiz_questions
csv = @quiz_statistics.csv_attachment.open.read csv = @quiz_statistics.csv_attachment.open.read
stats = CSV.parse(csv) stats = CSV.parse(csv)
stats[0].should == ["Question Id" , "Question Title" , "Answered Student Count" , "Top Student Count" , "Middle Student Count" , "Bottom Student Count" , "Quiz Question Count" , "Correct Student Count" , "Wrong Student Count" , "Correct Student Ratio" , "Wrong Student Ratio" , "Correct Top Student Count" , "Correct Middle Student Count" , "Correct Bottom Student Count" , "Variance" , "Standard Deviation" , "Difficulty Index" , "Alpha" , "Point Biserial of Correct" , "Point Biserial of Distractor 2" , "Point Biserial of Distractor 3" , "Point Biserial of Distractor 4"] stats[0].should == ["Question Id" , "Question Title" , "Answered Student Count" , "Top Student Count" , "Middle Student Count" , "Bottom Student Count" , "Quiz Question Count" , "Correct Student Count" , "Wrong Student Count" , "Correct Student Ratio" , "Wrong Student Ratio" , "Correct Top Student Count" , "Correct Middle Student Count" , "Correct Bottom Student Count" , "Variance" , "Standard Deviation" , "Difficulty Index" , "Alpha" , "Point Biserial of Correct" , "Point Biserial of Distractor 2" , "Point Biserial of Distractor 3" , "Point Biserial of Distractor 4"]

View File

@ -544,8 +544,8 @@ describe QuizSubmission do
@quiz.generate_quiz_data(:persist => true) @quiz.generate_quiz_data(:persist => true)
@sub = @quiz.generate_submission(@user) @sub = @quiz.generate_submission(@user)
@sub.submission_data = {} @sub.submission_data = {}
question_1 = @q1.question_data[:id] question_1 = @q1.data[:id]
question_2 = @q2.question_data[:id] question_2 = @q2.data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1 @sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1 @sub.submission_data["question_#{question_2}"] = answer_2 + 1
@sub.grade_submission @sub.grade_submission
@ -575,8 +575,8 @@ describe QuizSubmission do
@quiz.generate_quiz_data(:persist => true) @quiz.generate_quiz_data(:persist => true)
@sub = @quiz.generate_submission(@user) @sub = @quiz.generate_submission(@user)
@sub.submission_data = {} @sub.submission_data = {}
question_1 = @q1.question_data[:id] question_1 = @q1.data[:id]
question_2 = @q2.question_data[:id] question_2 = @q2.data[:id]
@sub.submission_data["question_#{question_1}"] = answer_1 @sub.submission_data["question_#{question_1}"] = answer_1
@sub.submission_data["question_#{question_2}"] = answer_2 + 1 @sub.submission_data["question_#{question_2}"] = answer_2 + 1
@sub.grade_submission @sub.grade_submission
@ -589,7 +589,6 @@ describe QuizSubmission do
@results.first.mastery.should eql(true) @results.first.mastery.should eql(true)
@results.last.associated_asset.should eql(@q2.assessment_question) @results.last.associated_asset.should eql(@q2.assessment_question)
@results.last.mastery.should eql(false) @results.last.mastery.should eql(false)
@sub = @quiz.generate_submission(@user) @sub = @quiz.generate_submission(@user)
@sub.attempt.should eql(2) @sub.attempt.should eql(2)
@sub.submission_data = {} @sub.submission_data = {}

View File

@ -267,8 +267,9 @@ describe "quizzes questions" do
:name => "Question", :name => "Question",
:question_name => "Question", :question_name => "Question",
:incorrect_comments => "", :incorrect_comments => "",
:assessment_question_id => nil :assessment_question_id => b.id
}) })
quest2.save!
q.generate_quiz_data q.generate_quiz_data
q.save! q.save!
get "/courses/#{@course.id}/quizzes/#{q.id}/edit" get "/courses/#{@course.id}/quizzes/#{q.id}/edit"