canvas-lms/spec/factories/quiz_factory.rb

360 lines
19 KiB
Ruby
Raw Normal View History

# coding: utf-8
#
# Copyright (C) 2011 - present 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/>.
#
module Factories
def quiz_model(opts={})
@context ||= opts.delete(:course) || course_model(:reusable => true)
@quiz = @context.quizzes.build(valid_quiz_attributes.merge(opts))
@quiz.published_at = Time.zone.now
@quiz.workflow_state = 'available'
@quiz.save!
@quiz
end
def valid_quiz_attributes
{
:title => "Test Quiz",
:description => "Test Quiz Description"
}
end
def quiz_with_submission(complete_quiz = true, skip_submission = false)
@course ||= course_model(:reusable => true)
@student ||= user_model
@course.enroll_student(@student).accept
@quiz = @course.quizzes.create
@quiz.workflow_state = "available"
re-apply late policies to any late submissions as needed the "as needed" refers to two cases: * when late policy changes * when an assignment's points_possible changes closes CNVS-36656 test plan: * Create a course with two assignments, two grading periods and two students enrolled * Ensure one assignment is due in a closed grading period and the other in an open grading period * Submit homework from both students so for each assignment one student submits the homework on time and the other submits it late * Go to gradebook and grade the students * Add a late policy to the course using the Rails console: course = Course.find(my_course_id) late_policy = LatePolicy.create( course: course, late_submission_deduction_enabled: true, late_submission_deduction: 50.0 ) * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed (lowered) * Verify that none of the other submissions had their scores changed * Now edit the assignment in the open grading period and change its points possible to a higher number and save this change * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed again * Verify that none of the other submissions had their scores changed * Now try this using three quiz submissions (early and late and just 45 seconds past the deadline). * Verify in the gradebook that late policy deductions are applied only to quiz submissions that are later than 60 seconds after the quiz due date Change-Id: I58ed3e3d0665870cf46d1b1e3ddf00f5f2f7008c Reviewed-on: https://gerrit.instructure.com/110598 Tested-by: Jenkins Reviewed-by: Jeremy Neander <jneander@instructure.com> Reviewed-by: Derek Bender <djbender@instructure.com> QA-Review: KC Naegle <knaegle@instructure.com> Product-Review: Keith T. Garner <kgarner@instructure.com>
2017-05-03 07:00:21 +08:00
@quiz.quiz_questions.create!({ question_data: test_quiz_data.first })
@quiz.save!
return if skip_submission
@qsub = Quizzes::SubmissionManager.new(@quiz).find_or_create_submission(@student)
re-apply late policies to any late submissions as needed the "as needed" refers to two cases: * when late policy changes * when an assignment's points_possible changes closes CNVS-36656 test plan: * Create a course with two assignments, two grading periods and two students enrolled * Ensure one assignment is due in a closed grading period and the other in an open grading period * Submit homework from both students so for each assignment one student submits the homework on time and the other submits it late * Go to gradebook and grade the students * Add a late policy to the course using the Rails console: course = Course.find(my_course_id) late_policy = LatePolicy.create( course: course, late_submission_deduction_enabled: true, late_submission_deduction: 50.0 ) * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed (lowered) * Verify that none of the other submissions had their scores changed * Now edit the assignment in the open grading period and change its points possible to a higher number and save this change * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed again * Verify that none of the other submissions had their scores changed * Now try this using three quiz submissions (early and late and just 45 seconds past the deadline). * Verify in the gradebook that late policy deductions are applied only to quiz submissions that are later than 60 seconds after the quiz due date Change-Id: I58ed3e3d0665870cf46d1b1e3ddf00f5f2f7008c Reviewed-on: https://gerrit.instructure.com/110598 Tested-by: Jenkins Reviewed-by: Jeremy Neander <jneander@instructure.com> Reviewed-by: Derek Bender <djbender@instructure.com> QA-Review: KC Naegle <knaegle@instructure.com> Product-Review: Keith T. Garner <kgarner@instructure.com>
2017-05-03 07:00:21 +08:00
@qsub.quiz_data = test_quiz_data
@qsub.started_at = 1.minute.ago
@qsub.attempt = 1
if complete_quiz
@qsub.submission_data = [{:points=>0, :text=>"7051", :question_id=>128, :correct=>false, :answer_id=>7051}]
@qsub.score = 0
@qsub.finished_at = Time.now.utc
@qsub.workflow_state = 'complete'
@qsub.submission = @quiz.assignment.find_or_create_submission(@student.id)
else
@qsub.submission_data = {}
end
@qsub.with_versioning(true) do
@qsub.save!
end
@qsub
end
re-apply late policies to any late submissions as needed the "as needed" refers to two cases: * when late policy changes * when an assignment's points_possible changes closes CNVS-36656 test plan: * Create a course with two assignments, two grading periods and two students enrolled * Ensure one assignment is due in a closed grading period and the other in an open grading period * Submit homework from both students so for each assignment one student submits the homework on time and the other submits it late * Go to gradebook and grade the students * Add a late policy to the course using the Rails console: course = Course.find(my_course_id) late_policy = LatePolicy.create( course: course, late_submission_deduction_enabled: true, late_submission_deduction: 50.0 ) * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed (lowered) * Verify that none of the other submissions had their scores changed * Now edit the assignment in the open grading period and change its points possible to a higher number and save this change * Reload the gradebook and you should see the score of the late submission for the assignment in the open grading period to have changed again * Verify that none of the other submissions had their scores changed * Now try this using three quiz submissions (early and late and just 45 seconds past the deadline). * Verify in the gradebook that late policy deductions are applied only to quiz submissions that are later than 60 seconds after the quiz due date Change-Id: I58ed3e3d0665870cf46d1b1e3ddf00f5f2f7008c Reviewed-on: https://gerrit.instructure.com/110598 Tested-by: Jenkins Reviewed-by: Jeremy Neander <jneander@instructure.com> Reviewed-by: Derek Bender <djbender@instructure.com> QA-Review: KC Naegle <knaegle@instructure.com> Product-Review: Keith T. Garner <kgarner@instructure.com>
2017-05-03 07:00:21 +08:00
def test_quiz_data
[
{
correct_comments: '',
assessment_question_id: nil,
incorrect_comments: '',
question_name: 'Question 1',
points_possible: 1,
question_text: 'Which book(s) are required for this course?',
name: 'Question 1',
id: 128,
answers: [
{ weight: 100, text: 'A', comments: '', id: 1490 },
{ weight: 0, text: 'B', comments: '', id: 1020 },
{ weight: 0, text: 'C', comments: '', id: 7051 }
],
question_type: 'multiple_choice_question'
}
]
end
def generate_quiz(course)
quiz = course.quizzes.create(workflow_state: 'available')
quiz.quiz_questions.create!(question_data: test_quiz_data().first)
quiz.save!
quiz
end
def generate_quiz_submission(quiz, student:, finished_at: nil)
qsub = Quizzes::SubmissionManager.new(quiz).find_or_create_submission(student)
qsub.quiz_data = test_quiz_data()
qsub.started_at = 1.minute.ago
qsub.attempt = 1
if finished_at.nil?
qsub.submission_data = {}
else
qsub.submission_data = [{ points: 0, text: "7051", question_id: 128, correct: false, answer_id: 7051 }]
qsub.score = 0
qsub.finished_at = finished_at || Time.now.utc
qsub.workflow_state = 'complete'
end
qsub.submission = quiz.assignment.find_or_create_submission(student.id)
qsub.submission.quiz_submission = qsub
qsub.submission.submission_type = 'online_quiz'
qsub.submission.submitted_at = qsub.finished_at
qsub.save!
qsub
end
def multiple_choice_question_data
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"multiple_choice_question", "assessment_question_id"=>4, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>50.0, "answers"=>[{"comments"=>"", "weight"=>0, "text"=>"a", "id"=>2405}, {"comments"=>"", "weight"=>0, "text"=>"b", "id"=>8544}, {"comments"=>"", "weight"=>100, "text"=>"c", "id"=>1658}, {"comments"=>"", "weight"=>0, "text"=>"d", "id"=>2903}], "question_text"=>"<p>test</p>", "id" => 1}.with_indifferent_access
end
def true_false_question_data
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"true_false_question", "assessment_question_id"=>8197062, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>45, "answers"=>[{"comments"=>"", "weight"=>0, "text"=>"True", "id"=>8403}, {"comments"=>"", "weight"=>100, "text"=>"False", "id"=>8950}], "question_text"=>"<p>test</p>", "id" => 1}.with_indifferent_access
end
def short_answer_question_data
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"short_answer_question", "assessment_question_id"=>8197062, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>16.5, "answers"=>[{"comments"=>"", "weight"=>100, "text"=>"stupid", "id"=>7100}, {"comments"=>"", "weight"=>100, "text"=>"dumb", "id"=>2159}], "question_text"=>"<p>there's no such thing as a _____ question</p>", "id" => 1}.with_indifferent_access
end
def short_answer_question_data_one_blank
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"short_answer_question", "assessment_question_id"=>8197062, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>16.5, "answers"=>[{"comments"=>"", "weight"=>100, "text"=>"stupid", "id"=>7100}, {"comments"=>"", "weight"=>100, "text"=>"dumb", "id"=>2159}, {"comments"=>"", "weight"=>100, "text"=>"", "id"=>9090}], "question_text"=>"<p>there's no such thing as a _____ question</p>", "id" => 1}.with_indifferent_access
end
def essay_question_data
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"essay_question", "comments"=>nil, "assessment_question_id"=>8197062, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>13.6, "answers"=>[], "question_text"=>"<p>Please summarize the history of the world in 3 sentences</p>", "id" => 1}.with_indifferent_access
end
def text_only_question_data
{ "question_type" => "text_only_question", "id" => 3 }.with_indifferent_access
end
def multiple_dropdowns_question_data
{"position"=>3, "correct_comments"=>"", "name"=>"Question 3", "question_type"=>"multiple_dropdowns_question", "assessment_question_id"=>1695442, "neutral_comments"=>"", "incorrect_comments"=>"", "id"=>1630873, "question_name"=>"Question 3", "points_possible"=>0.5, "original_question_text"=>"[structure1] [event1] [structure2] [structure3] [structure4] [structure5] [structure6] [event2] [structure7]", "answers"=>[
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>4390, "blank_id"=>"structure1"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>1522, "blank_id"=>"structure1"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>7446, "blank_id"=>"structure1"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>279, "blank_id"=>"structure1"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>3390, "blank_id"=>"event1"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>5498, "blank_id"=>"event1"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>6955, "blank_id"=>"structure2"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>1228, "blank_id"=>"structure2"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>6578, "blank_id"=>"structure2"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>7676, "blank_id"=>"structure3"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>5213, "blank_id"=>"structure3"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>5988, "blank_id"=>"structure3"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>7604, "blank_id"=>"structure4"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>9532, "blank_id"=>"structure4"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>4772, "blank_id"=>"structure4"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>3353, "blank_id"=>"event2"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>599, "blank_id"=>"event2"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>9908, "blank_id"=>"structure5"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>878, "blank_id"=>"structure5"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>670, "blank_id"=>"structure5"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>4351, "blank_id"=>"structure5"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>6994, "blank_id"=>"structure6"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>1883, "blank_id"=>"structure6"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>4717, "blank_id"=>"structure6"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>7697, "blank_id"=>"structure6"},
{"comments"=>"", "weight"=>100, "text"=>"y", "id"=>1121, "blank_id"=>"structure7"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>9570, "blank_id"=>"structure7"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>4764, "blank_id"=>"structure7"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>3477, "blank_id"=>"structure7"},
{"comments"=>"", "weight"=>0, "text"=>"n", "id"=>461, "blank_id"=>"structure7"}
], "question_text"=>"[structure1] [event1] [structure2] [structure3] [structure4] [structure5] [structure6] [event2] [structure7]"}.with_indifferent_access
end
# @param [Hash] options
# @param [Boolean] options.answer_parser_compatibility
# Set this to true if you want the fixture to be compatible with
# QuizQuestion::AnswerParsers::Matching.
def matching_question_data(options = {})
data = {"id" => 1, "name"=>"Question", "correct_comments"=>"", "question_type"=>"matching_question", "assessment_question_id"=>4, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>50.0, "matches"=>[{"match_id"=>6061, "text"=>"1"}, {"match_id"=>3855, "text"=>"2"}, {"match_id"=>1397, "text"=>"1"}, {"match_id"=>2369, "text"=>"3"}, {"match_id"=>6065, "text"=>"4"}, {"match_id"=>5779, "text"=>"5"}, {"match_id"=>3562, "text"=>"6"}, {"match_id"=>1500, "text"=>"7"}, {"match_id"=>8513, "text"=>"8"}, {"match_id" => 6067, "text" => "a2"}, {"match_id" => 6068, "text" => "a3"}, {"match_id" => 6069, "text" => "a4"}], "answers"=>[
{"left"=>"a", "comments"=>"", "match_id"=>6061, "text"=>"a", "id"=>7396, "right"=>"1"},
{"left"=>"b", "comments"=>"", "match_id"=>3855, "text"=>"b", "id"=>6081, "right"=>"2"},
{"left"=>"ca", "comments"=>"", "match_id"=>1397, "text"=>"ca", "id"=>4224, "right"=>"1"},
{"left"=>"a2", "comments"=>"", "match_id"=>6067, "text"=>"a", "id"=>7397, "right"=>"a2"},
{"left"=>"a3", "comments"=>"", "match_id"=>6068, "text"=>"a", "id"=>7398, "right"=>"a3"},
{"left"=>"a4", "comments"=>"", "match_id"=>6069, "text"=>"a", "id"=>7399, "right"=>"a4"},
], "question_text"=>"<p>Test Question</p>"}.with_indifferent_access
if options[:answer_parser_compatibility]
data['answers'].each do |record|
record['answer_match_left'] = record['left']
record['answer_match_text'] = record['text']
record['answer_match_right'] = record['right']
record['answer_comments'] = record['comments']
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
%w[ left text right comments ].each { |k| record.delete k }
end
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
# match#1397 has a duplicate text with #7396 that needs to be adjusted
i = data['matches'].index { |record| record['match_id'] == 1397 }
data['matches'][i]['text'] = '_1'
i = data['answers'].index { |record| record['match_id'] == 1397 }
data['answers'][i]['text'] = '_1'
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
end
data
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
end
def numerical_question_data
{"name"=>"Question", "correct_comments"=>"", "question_type"=>"numerical_question", "assessment_question_id"=>8197062, "neutral_comments"=>"", "incorrect_comments"=>"", "question_name"=>"Question", "points_possible"=>26.2, "answers"=>[
{"exact"=>4, "comments"=>"", "numerical_answer_type"=>"exact_answer", "margin"=>0, "weight"=>100, "text"=>"", "id"=>9222},
{"exact"=>-4, "comments"=>"", "numerical_answer_type"=>"exact_answer", "margin"=>0, "weight"=>100, "text"=>"", "id"=>997},
{"comments"=>"", "numerical_answer_type"=>"range_answer", "weight"=>100, "text"=>"", "id"=>9370, "end"=>4.1, "start"=>3.9},
{"exact"=>-4, "comments"=>"", "numerical_answer_type"=>"exact_answer", "margin"=>0.1, "weight"=>100, "text"=>"", "id"=>5450},
{"numerical_answer_type"=>"precision_answer", "approximate"=>"1.23456e+21", "precision"=>"6", "comments"=>"", "weight"=>100, "text"=>"", "id"=>123}
], "question_text"=>"<p>abs(x) = 4</p>", "id" => 1}.with_indifferent_access
end
def calculated_question_data
{"name"=>"Question",
"correct_comments"=>"",
"answer_tolerance"=>0.02,
"question_type"=>"calculated_question",
"formulas"=>[{"formula"=>"5 + (x - y)"}],
"assessment_question_id"=>8197062,
"variables"=>
[{"name"=>"x", "scale"=>2, "max"=>7, "min"=>2},
{"name"=>"y", "scale"=>0, "max"=>23, "min"=>17}],
"neutral_comments"=>"",
"incorrect_comments"=>"",
"question_name"=>"Question",
"points_possible"=>26.2,
"formula_decimal_places"=>2,
"answers"=> [
{"variables"=>[{"name"=>"x", "value"=>4.3}, {"name"=>"y", "value"=>21}],
"weight"=>100,
"id"=>6396,
"answer"=>-11.7},
],
"question_text"=>"<p>What is 5 plus [x] - [y]</p>",
"id" => 1}.with_indifferent_access
end
# @param [Hash] options
# @param [Boolean] options.answer_parser_compatibility
# Set this to true if you want the fixture to be compatible with
# QuizQuestion::AnswerParsers::MultipleAnswers.
def multiple_answers_question_data(options = {})
data = {"name"=>"Question",
"correct_comments"=>"",
"question_type"=>"multiple_answers_question",
"assessment_question_id"=>8197062,
"neutral_comments"=>"",
"incorrect_comments"=>"",
"question_name"=>"Question",
"points_possible"=>50,
"answers"=>
[{"comments"=>"", "weight"=>100, "text"=>"5", "id"=>9761},
{"comments"=>"", "weight"=>0, "text"=>"cat", "id"=>3079},
{"comments"=>"", "weight"=>100, "text"=>"8", "id"=>5194},
{"comments"=>"", "weight"=>100, "text"=>"37.5", "id"=>166},
{"comments"=>"", "weight"=>0, "text"=>"airplane", "id"=>4739},
{"comments"=>"", "weight"=>100, "text"=>"114", "id"=>2196},
{"comments"=>"", "weight"=>100, "text"=>"869", "id"=>8982},
{"comments"=>"", "weight"=>100, "text"=>"431", "id"=>9701},
{"comments"=>"", "weight"=>0, "text"=>"schadenfreude", "id"=>7381}],
"question_text"=>"<p>which of these are numbers?</p>", "id" => 1}.with_indifferent_access
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
if options[:answer_parser_compatibility]
data['answers'].each do |record|
record['answer_weight'] = record['weight']
end
Quiz Submission Questions API - Update This patch provides support for answering Quiz Questions via the API. closes CNVS-9844, CNVS-10225 TEST PLAN ---- ---- Testing this will be a bit rough because there are many variations and validations to cover. I'll spare the validations that are covered by specs from the test plan. Create a quiz with a question of *each* type except "Text" and "File Upload". There's a script that creates a quiz with its questions automatically for you if you don't want to keep doing this manually. See references. > Answering Questions Now you need to answer each question via the API. Most of them vary in formats, but they are fully specified in the API documentation page (along with examples). See DOCUMENTATION for more info. > Flagging Questions Flagging, and unflagging, a question is the same regardless of its type, see the "EXAMPLE REQUESTS" section. > Access Validations Here are some generic, non-question based validations to verify. You should NOT be able to answer a question if: - the quiz submission has been turned in - the quiz submission is overdue - the Access Code for the quiz is invalid - the IP filter of the Quiz prohibits you from taking the quiz - the quiz submission :validation_token is incorrectly specified (ie, other students shouldn't be able to answer your questions) - you don't specify the latest :attempt, so if the Quiz has multiple attempts, and this is your 2nd take, you specify an :attempt of 1, 3, or anything but 2 should fail - NEW: turn quiz into an OQAAT quiz with the "Can't go back" flag on; the API should not reject all requests to modify any of the questions with a 501 error saying that type of quizzes is not supported yet (support will come in CNVS-10224) > Grading Also, when you're done answering the questions, take a look at the grades and make sure everything gets graded just like it does when using the UI directly. > Verifying results in the browser While taking a quiz in the canvas UI, the scripts perform backups in the background that would overwrite any changes you do via the API. If you want to verify the changes you make via the API from the UI, you must append "?backup=false" to the take quiz page URL, something like this: http://localhost:3000/courses/1/quizzes/1/take?backup=false Setting that flag will (for now) disable the backup behaviour and should make things tick. EXAMPLE REQUESTS ------- -------- Don't forget to set the 'Content-Type' header to 'application/json'! > Answering a Multiple-Choice question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id { "attempt": 1, "validation_token": "1babd0...", "answer": 10 } > Flagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/flag { "attempt": 1, "validation_token": "1babd0..." } > Unflagging a question [PUT] /api/v1/quiz_submissions/:quiz_submission_id/questions/:id/unflag { "attempt": 1, "validation_token": "1babd0..." } DOCUMENTATION ------------- Run `bundle exec rake doc:api` and check out the Quiz Submission Questions page. There's an Appendix that contains example requests for each question type, as well as the errors produced by each handler. LINKS ----- - bootstrap script: https://gist.github.com/amireh/e7e8f835ffbf1d053e4c - direct link to the API documentation page: http://canvas.docs.kodoware.com/quiz_submission_questions.html Change-Id: I9a958323ece8854bc21a24c2affd8dc3972e46d5 Reviewed-on: https://gerrit.instructure.com/27206 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Derek DeVries <ddevries@instructure.com> QA-Review: Myller de Araujo <myller@instructure.com> Product-Review: Ahmad Amireh <ahmad@instructure.com>
2013-12-10 17:15:01 +08:00
end
data
end
def fill_in_multiple_blanks_question_data
{:position=>1, :name=>"Question 1", :correct_comments=>"", :question_type=>"fill_in_multiple_blanks_question", :assessment_question_id=>7903, :incorrect_comments=>"", :neutral_comments=>"", :id=>1, :points_possible=>50, :question_name=>"Question 1", :answers=>[
{:comments=>"", :text=>"control", :weight=>100, :id=>3950, :blank_id=>"answer1"},
{:comments=>"", :text=>"controll", :weight=>100, :id=>9177, :blank_id=>"answer1"},
{:comments=>"", :text=>"patrol", :weight=>100, :id=>9181, :blank_id=>"answer2"},
{:comments=>"", :text=>"soul", :weight=>100, :id=>3733, :blank_id=>"answer3"},
{:comments=>"", :text=>"tolls", :weight=>100, :id=>9756, :blank_id=>"answer4"},
{:comments=>"", :text=>"toll", :weight=>100, :id=>7829, :blank_id=>"answer4"},
{:comments=>"", :text=>"explode", :weight=>100, :id=>3046, :blank_id=>"answer5"},
{:comments=>"", :text=>"assplode", :weight=>100, :id=>5301, :blank_id=>"answer5"},
{:comments=>"", :text=>"old", :weight=>100, :id=>3367, :blank_id=>"answer6"}
], :question_text=>"<p><span>Ayo my quality [answer1], captivates your party [answer2]. </span>Your mind, body, and [answer3]. For whom the bell [answer4], let the rhythm [answer5]. Big, bad, and bold b-boys of [answer6].</p>"}.with_indifferent_access
end
def fill_in_multiple_blanks_question_one_blank_data
{ :name => "Question", :question_type => "fill_in_multiple_blanks_question", :id => 2, :points_possible => 3.75, :answers => [
{ :text => "stupid", :weight => 100, :id => 1234, :blank_id => "myblank" },
{ :text => "dumb", :weight => 100, :id => 1235, :blank_id => "myblank" },
], :question_text => "<p>there's no such thing as a [myblank] question</p>" }.with_indifferent_access
end
def assignment_quiz(questions, opts={})
course = opts[:course] || course_factory(active_course: true)
user = opts[:user] || user_factory(:active_user => true)
course.enroll_student(user, :enrollment_state => 'active') unless user.enrollments.any? { |e| e.course_id == course.id }
@assignment = course.assignments.create(title: opts.fetch(:title, "Test Assignment"))
@assignment.workflow_state = "published"
@assignment.submission_types = "online_quiz"
@assignment.save
@quiz = Quizzes::Quiz.where(assignment_id: @assignment).first
@questions = questions.map { |q| @quiz.quiz_questions.create!(q.slice(:quiz_group, :assessment_question, :question_data, :assessment_question_version)) }
@quiz.generate_quiz_data
@quiz.due_at = opts.fetch(:due_at, Time.zone.now.advance(days: 7))
@quiz.published_at = Time.zone.now
@quiz.workflow_state = "available"
@quiz.save!
@quiz
end
# The block should return the submission_data. We pass the newly created
# questions to the block (legacy code uses @questions)
def quiz_with_graded_submission(questions, opts={}, &block)
assignment_quiz(questions, opts)
@quiz_submission = graded_submission(@quiz, @user, &block)
end
def graded_submission(quiz, user)
quiz_submission = quiz.generate_submission(user)
quiz_submission.mark_completed
quiz_submission.submission_data = yield(quiz.quiz_questions.order(:id)) if block_given?
Quizzes::SubmissionGrader.new(quiz_submission).grade_submission
quiz_submission
end
def survey_with_submission(questions, &block)
course_with_student(:active_all => true)
@assignment = @course.assignments.create(:title => "Test Assignment")
@assignment.workflow_state = "published"
@assignment.submission_types = "online_quiz"
@assignment.save
@quiz = Quizzes::Quiz.where(assignment_id: @assignment).first
@quiz.anonymous_submissions = true
@quiz.quiz_type = "graded_survey"
@questions = questions.map { |q| @quiz.quiz_questions.create!(q) }
@quiz.generate_quiz_data
@quiz.save!
@quiz_submission = @quiz.generate_submission(@user)
@quiz_submission.mark_completed
@quiz_submission.submission_data = yield if block_given?
end
def course_quiz(active=false)
@quiz = @course.quizzes.create
@quiz.workflow_state = "available" if active
@quiz.save!
@quiz
end
end