2012-05-12 06:32:11 +08:00
# coding: utf-8
2011-08-11 23:13:00 +08:00
#
2017-04-28 12:02:05 +08:00
# Copyright (C) 2011 - present Instructure, Inc.
2011-08-11 23:13:00 +08:00
#
# 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/>.
#
2016-10-12 06:59:38 +08:00
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
2011-08-11 23:13:00 +08:00
2016-10-12 06:59:38 +08:00
def valid_quiz_attributes
{
:title = > " Test Quiz " ,
:description = > " Test Quiz Description "
}
end
2011-10-04 01:59:41 +08:00
2019-03-07 02:29:31 +08:00
def quiz_with_submission ( complete_quiz = true , skip_submission = false )
2016-10-12 06:59:38 +08:00
@course || = course_model ( :reusable = > true )
@student || = user_model
@course . enroll_student ( @student ) . accept
@quiz = @course . quizzes . create
@quiz . workflow_state = " available "
2017-05-03 07:00:21 +08:00
@quiz . quiz_questions . create! ( { question_data : test_quiz_data . first } )
2016-10-12 06:59:38 +08:00
@quiz . save!
2019-03-07 02:29:31 +08:00
return if skip_submission
2016-01-12 23:19:41 +08:00
2016-10-12 06:59:38 +08:00
@qsub = Quizzes :: SubmissionManager . new ( @quiz ) . find_or_create_submission ( @student )
2017-05-03 07:00:21 +08:00
@qsub . quiz_data = test_quiz_data
2017-02-03 07:06:04 +08:00
@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
2016-10-12 06:59:38 +08:00
@qsub . with_versioning ( true ) do
@qsub . save!
end
@qsub
2011-10-04 01:59:41 +08:00
end
2012-04-21 07:55:18 +08:00
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
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-05-12 04:26:41 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
def text_only_question_data
{ " question_type " = > " text_only_question " , " id " = > 3 } . with_indifferent_access
end
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2016-10-12 06:59:38 +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
2016-10-12 06:59:38 +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
2016-10-12 06:59:38 +08:00
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
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
# @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
2016-10-12 06:59:38 +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
2016-10-12 06:59:38 +08:00
data
end
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2012-04-21 07:55:18 +08:00
2016-10-12 06:59:38 +08:00
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
2015-07-08 03:04:12 +08:00
2016-10-12 06:59:38 +08:00
def assignment_quiz ( questions , opts = { } )
2016-12-25 13:35:06 +08:00
course = opts [ :course ] || course_factory ( active_course : true )
2016-12-24 07:53:27 +08:00
user = opts [ :user ] || user_factory ( :active_user = > true )
2016-10-12 06:59:38 +08:00
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
2016-11-08 23:04:49 +08:00
@questions = questions . map { | q | @quiz . quiz_questions . create! ( q . slice ( :quiz_group , :assessment_question , :question_data , :assessment_question_version ) ) }
2016-10-12 06:59:38 +08:00
@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
2015-07-08 03:04:12 +08:00
2016-12-16 08:34:42 +08:00
# The block should return the submission_data. We pass the newly created
# questions to the block (legacy code uses @questions)
2016-10-12 06:59:38 +08:00
def quiz_with_graded_submission ( questions , opts = { } , & block )
assignment_quiz ( questions , opts )
2016-12-16 08:34:42 +08:00
@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
2016-10-12 06:59:38 +08:00
end
2015-07-08 03:04:12 +08:00
2016-10-12 06:59:38 +08:00
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
2015-07-08 03:04:12 +08:00
2016-10-12 06:59:38 +08:00
def course_quiz ( active = false )
@quiz = @course . quizzes . create
@quiz . workflow_state = " available " if active
@quiz . save!
@quiz
end
2015-07-08 03:04:12 +08:00
end