-
Notifications
You must be signed in to change notification settings - Fork 185
Description
Describe the bug
I'm working with the new quiz items API (https://canvas.instructure.com/doc/api/new_quiz_items.html) and have run into an issue where it appears that the structure of canvasapi's request class is incompatible with this endpoint.
Specifically, the scoring_data dictionary for the multiple choice with varying point values by answer item type (https://canvas.instructure.com/doc/api/new_quiz_items.html#choice) requires an internal list of dictionaries, e.g.:
"scoring_data": {
"value": "f1feae62-566d-4d85-9afb-182d757030c9",
"values": [
{
"value": "f1feae62-566d-4d85-9afb-182d757030c9",
"points": 2
},
{
"value": "22d2bd84-5d5a-4640-8acf-484d799a735a",
"points": 1
},
{
"value": "74a22711-85a6-4b46-8fb3-ea22947e869a",
"points": 0
},
{
"value": "3ae2c63d-b990-40f0-aa70-e6bb854d0de2",
"points": 0
},
{
"value": "8193a348-d336-4b00-b60e-dece48739ce4",
"points": 0
}
]
}
If you run this through combine_kwargs you'll get something like:
[('item[scoring_data][value]', 'f1feae62-566d-4d85-9afb-182d757030c9'),
('item[scoring_data][values][][value]',
'f1feae62-566d-4d85-9afb-182d757030c9'),
('item[scoring_data][values][][points]', 2),
('item[scoring_data][values][][value]',
'22d2bd84-5d5a-4640-8acf-484d799a735a'),
('item[scoring_data][values][][points]', 1),
('item[scoring_data][values][][value]',
'74a22711-85a6-4b46-8fb3-ea22947e869a'),
('item[scoring_data][values][][points]', 0),
('item[scoring_data][values][][value]',
'3ae2c63d-b990-40f0-aa70-e6bb854d0de2'),
('item[scoring_data][values][][points]', 0),
('item[scoring_data][values][][value]',
'8193a348-d336-4b00-b60e-dece48739ce4'),
('item[scoring_data][values][][points]', 0)]
Trying to then PUT this data up to Canvas will generate the error:
UnprocessableEntity: {"errors":{"scoring_data":["Invalid Scoring Data: The property '#/values/0/points' of type string did not match the following type: number"]},"message":"Unprocessable Entity"}
This seems to be a case where it is much simpler to just send a dictionary directly, and I see that the request method has a json keyword, but it looks like setting this to True, doesn't actually allow you to send a data dictionary as _kwargs is still expected to be a list of tuples. Is there any way to make this work with the canvasapi requester, or is this a case where just manually assembling the request is the best way to go?
To Reproduce
Minimal worked example:
#course is a canvasapi course object
nq = course.create_new_quiz(
quiz={
"title": "Quiz Example",
"assignment_group_id": xxxxxx,
"points_possible": 10,
"grading_type": "points",
"instructions": "something"})
item = {
"position": 1,
"points_possible": 3.0,
"entry_type": "Item",
"status": "immutable",
"entry": {
"title": "Q1",
"item_body": "<p>Answer the question</p>",
"calculator_type": "none",
"interaction_data": {
"choices": [
{
"id": "e5519152-c925-4abd-a0ec-3081511ca636",
"position": 1,
"item_body": "<p>0</p>",
},
{
"id": "3f55f6d8-7115-4d5b-bb70-07e15bb76112",
"position": 2,
"item_body": "<p>1</p>",
},
{
"id": "7d243ebc-cff9-4b97-b047-b96d0b8e2aed",
"position": 3,
"item_body": "<p>2</p>",
},
{
"id": "ff4cc47b-1846-4e7f-8482-b471685f8865",
"position": 4,
"item_body": "<p>3</p>",
},
]
},
"properties": {
"shuffle_rules": {"choices": {"to_lock": [], "shuffled": False}},
"vary_points_by_answer": True,
},
"scoring_data": {
"value": "ff4cc47b-1846-4e7f-8482-b471685f8865",
"values": [
{"value": "e5519152-c925-4abd-a0ec-3081511ca636", "points": 0},
{"value": "3f55f6d8-7115-4d5b-bb70-07e15bb76112", "points": 1},
{"value": "7d243ebc-cff9-4b97-b047-b96d0b8e2aed", "points": 2},
{"value": "ff4cc47b-1846-4e7f-8482-b471685f8865", "points": 3},
],
},
"answer_feedback": {"e5519152-c925-4abd-a0ec-3081511ca636": ""},
"scoring_algorithm": "VaryPointsByAnswer",
"interaction_type_slug": "choice",
"feedback": {},
},
}
endpoint = "courses/{}/quizzes/{}/items".format(course.id, nq.id)
# this leads to Canvas error:
r =nq._requester.request(
"POST",
endpoint,
_url="new_quizzes",
_kwargs=combine_kwargs(**{"item":item}),
)
#setting json=True leads to different error: BadRequest: {"error":"Expected `item` object"}
Expected behavior
Setting up the request manually works as expected:
full_url = "{}{}".format(
course._requester.new_quizzes_url,
"courses/{}/quizzes/{}/items".format(course.id, nq.id),
)
headers = {"Authorization": "Bearer {}".format(course._requester.access_token)}
r = requests.post(full_url, json={"item": tmp}, headers=headers)
Environment information
- Python 3.11.9
- CanvasAPI version 3.3.0
Additional context
Thanks!