Skip to content

Commit 1bb9920

Browse files
authored
New Quizzes Accommodations (#699)
* New Quizzes Accommodations and backend tweak to JSON for POST requests (accommodations and graphql) * Spell accommodations correctly
1 parent 9aeb6e6 commit 1bb9920

File tree

12 files changed

+219
-38
lines changed

12 files changed

+219
-38
lines changed

.github/CONTRIBUTING.md

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,26 @@ Below you'll find guidelines for contributing that will keep our codebase clean
77
## Table of Contents
88

99
* [Contributing to CanvasAPI](#contributing-to-canvasapi)
10-
* [Table of Contents](#table-of-contents)
11-
* [How can I contribute?](#how-can-i-contribute)
12-
* [Bug reports](#bug-reports)
13-
* [Resolving issues](#resolving-issues)
14-
* [Making your first contribution](#making-your-first-contribution)
15-
* [Setting up the environment](#setting-up-the-environment)
16-
* [Writing tests](#writing-tests)
17-
* [API coverage tests](#api-coverage-tests)
18-
* [Engine tests](#engine-tests)
19-
* [Running tests / coverage reports](#running-tests--coverage-reports)
20-
* [Making a pull request](#making-a-pull-request)
21-
* [Code style guidelines](#code-style-guidelines)
22-
* [Running code style checks](#running-code-style-checks)
23-
* [Foolish consistency](#foolish-consistency)
24-
* [Method docstrings](#method-docstrings)
25-
* [Descriptions](#descriptions)
26-
* [Links to related API endpoints](#links-to-related-api-endpoints)
27-
* [Parameters](#parameters)
28-
* [Returns](#returns)
29-
* [Docstring Examples](#docstring-examples)
10+
* [Table of Contents](#table-of-contents)
11+
* [How can I contribute?](#how-can-i-contribute)
12+
* [Bug reports](#bug-reports)
13+
* [Resolving issues](#resolving-issues)
14+
* [Making your first contribution](#making-your-first-contribution)
15+
* [Setting up the environment](#setting-up-the-environment)
16+
* [Writing tests](#writing-tests)
17+
* [API coverage tests](#api-coverage-tests)
18+
* [Engine tests](#engine-tests)
19+
* [Running tests / coverage reports](#running-tests--coverage-reports)
20+
* [Making a pull request](#making-a-pull-request)
21+
* [Code style guidelines](#code-style-guidelines)
22+
* [Running code style checks](#running-code-style-checks)
23+
* [Foolish consistency](#foolish-consistency)
24+
* [Method docstrings](#method-docstrings)
25+
* [Descriptions](#descriptions)
26+
* [Links to related API endpoints](#links-to-related-api-endpoints)
27+
* [Parameters](#parameters)
28+
* [Returns](#returns)
29+
* [Docstring Examples](#docstring-examples)
3030

3131
## How can I contribute?
3232

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- LTI Resource Links (Thanks, [@jsmnhou](https://github.com/jsmnhou))
88
- Smart Search API [BETA] (Thanks, [@alportoricensis](https://github.com/alportoricensis))
9+
- New Quizzes Accommodations
910

1011
### General
1112

@@ -17,6 +18,7 @@
1718
- Updated deploy Action to use more modern processes.
1819
- Updated `PaginatedList` to be type-aware, showing which class is included in the response. (Thanks [@HandcartCactus](https://github.com/HandcartCactus))
1920
- Updated Sphinx
21+
- Reworked how `Requester` handles JSON-only POST requests (currently, only New Quizzes Accommodations and GraphQL)
2022

2123
## [3.3.0] - 2023-08-27
2224

canvasapi/canvas.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,11 +1276,13 @@ def graphql(self, query, variables=None, **kwargs):
12761276
"POST",
12771277
"graphql",
12781278
headers={"Content-Type": "application/json"},
1279-
_kwargs=combine_kwargs(**kwargs)
1280-
+ [("query", query), ("variables", variables)],
1279+
_kwargs=combine_kwargs(**kwargs),
12811280
# Needs to call special endpoint without api/v1
12821281
_url="graphql",
1283-
json=True,
1282+
json={
1283+
"query": query,
1284+
"variables": variables if variables else {},
1285+
},
12841286
)
12851287

12861288
return response.json()

canvasapi/course.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from canvasapi.license import License
2626
from canvasapi.lti_resource_link import LTIResourceLink
2727
from canvasapi.module import Module
28-
from canvasapi.new_quiz import NewQuiz
28+
from canvasapi.new_quiz import AccommodationResponse, NewQuiz
2929
from canvasapi.outcome_import import OutcomeImport
3030
from canvasapi.page import Page
3131
from canvasapi.paginated_list import PaginatedList
@@ -1846,7 +1846,7 @@ def get_new_quiz(self, assignment, **kwargs):
18461846

18471847
def get_new_quizzes(self, **kwargs):
18481848
"""
1849-
Get a list of new quizzes.
1849+
Get a list of new quizzes in this course.
18501850
18511851
:calls: `GET /api/quiz/v1/courses/:course_id/quizzes \
18521852
<https://canvas.instructure.com/doc/api/new_quizzes.html#method.new_quizzes/quizzes_api.index>`_
@@ -1861,6 +1861,7 @@ def get_new_quizzes(self, **kwargs):
18611861
self._requester,
18621862
"GET",
18631863
endpoint,
1864+
{"course_id": self.id},
18641865
_url_override="new_quizzes",
18651866
_kwargs=combine_kwargs(**kwargs),
18661867
)
@@ -2654,6 +2655,33 @@ def resolve_path(self, full_path=None, **kwargs):
26542655
_kwargs=combine_kwargs(**kwargs),
26552656
)
26562657

2658+
def set_new_quizzes_accommodations(self, accommodations, **kwargs):
2659+
"""
2660+
Apply accommodations to New Quizzes at the **course level** for
2661+
students enrolled in this course.
2662+
2663+
:calls: `POST /api/quiz/v1/courses/:course_id/accommodations \
2664+
<https://developerdocs.instructure.com/services/canvas/resources/new_quizzes_accommodations#method.new_quizzes-accommodation_api.course_level_accommodations>`_
2665+
2666+
:param accommodations: A list of dictionaries containing accommodation details
2667+
for each user. Each dictionary must contain `user_id` and can optionally include
2668+
`extra_time`, `apply_to_in_progress_quiz_sessions`, and/or `reduce_choices_enabled`.
2669+
:type accommodations: list of dict
2670+
2671+
:returns: AccommodationResponse object containing the status of the accommodation request.
2672+
:rtype: :class:`canvasapi.new_quiz.AccommodationResponse`
2673+
"""
2674+
endpoint = "courses/{}/accommodations".format(self.id)
2675+
2676+
response = self._requester.request(
2677+
"POST",
2678+
endpoint,
2679+
_url="new_quizzes",
2680+
_kwargs=combine_kwargs(**kwargs),
2681+
json=accommodations,
2682+
)
2683+
return AccommodationResponse(self._requester, response.json())
2684+
26572685
def set_quiz_extensions(self, quiz_extensions, **kwargs):
26582686
"""
26592687
Set extensions for student all quiz submissions in a course.

canvasapi/new_quiz.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,34 @@ def delete(self, **kwargs):
2929

3030
return NewQuiz(self._requester, response_json)
3131

32+
def set_accommodations(self, accommodations, **kwargs):
33+
"""
34+
Apply accommodations at the quiz level for students in a specific assignment.
35+
36+
:calls: `POST /api/quiz/v1/courses/:course_id/quizzes/:assignment_id/accommodations \
37+
<https://developerdocs.instructure.com/services/canvas/resources/new_quizzes_accommodations#method.new_quizzes-accommodation_api.quiz_level_accommodations>`_
38+
39+
:param accommodations: A list of dictionaries containing accommodation details
40+
for each user. Each dictionary must contain `user_id` and can optionally include
41+
`extra_time`, `extra_attempts`, and/or `reduce_choices_enabled`.
42+
:type accommodations: list of dict
43+
44+
:returns: AccommodationResponse object containing the status of the accommodation request.
45+
:rtype: :class:`canvasapi.new_quiz.AccommodationResponse`
46+
"""
47+
endpoint = "courses/{}/quizzes/{}/accommodations".format(
48+
self.course_id, self.id
49+
)
50+
51+
response = self._requester.request(
52+
"POST",
53+
endpoint,
54+
_url="new_quizzes",
55+
_kwargs=combine_kwargs(**kwargs),
56+
json=accommodations,
57+
)
58+
return AccommodationResponse(self._requester, response.json())
59+
3260
def update(self, **kwargs):
3361
"""
3462
Update a single New Quiz for the course.
@@ -51,3 +79,8 @@ def update(self, **kwargs):
5179
response_json.update({"course_id": self.course_id})
5280

5381
return NewQuiz(self._requester, response_json)
82+
83+
84+
class AccommodationResponse(CanvasObject):
85+
def __str__(self):
86+
return f"{self.message}"

canvasapi/requester.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def _patch_request(self, url, headers, data=None, **kwargs):
8080
"""
8181
return self._session.patch(url, headers=headers, data=data)
8282

83-
def _post_request(self, url, headers, data=None, json=False):
83+
def _post_request(self, url, headers, data=None, json=None):
8484
"""
8585
Issue a POST request to the specified endpoint with the data provided.
8686
@@ -90,11 +90,11 @@ def _post_request(self, url, headers, data=None, json=False):
9090
:type headers: dict
9191
:param data: The data to send with this request.
9292
:type data: dict
93-
:param json: Whether or not to send the data as json
94-
:type json: bool
93+
:param json: JSON-encoded data to send in the body of the request.
94+
:type json: dict
9595
"""
9696
if json:
97-
return self._session.post(url, headers=headers, json=dict(data))
97+
return self._session.post(url, headers=headers, params=data, json=json)
9898

9999
# Grab file from data.
100100
files = None
@@ -222,6 +222,9 @@ def request(
222222
if _kwargs:
223223
logger.debug("Data: {data}".format(data=pformat(_kwargs)))
224224

225+
if json:
226+
logger.debug("JSON: {json}".format(json=pformat(json)))
227+
225228
response = req_method(full_url, headers, _kwargs, json=json)
226229
logger.info(
227230
"Response: {method} {url} {status}".format(

docs/class-reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Class Reference
4343
license-ref
4444
lti-resource-link-ref
4545
module-ref
46+
new-quiz-ref
4647
outcome-ref
4748
outcome-import-ref
4849
page-ref

docs/new-quiz-ref.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=======
2+
NewQuiz
3+
=======
4+
5+
.. autoclass:: canvasapi.new_quiz.NewQuiz
6+
:members:
7+
8+
=====================
9+
AccommodationResponse
10+
=====================
11+
12+
.. autoclass:: canvasapi.new_quiz.AccommodationResponse
13+
:members:

tests/fixtures/graphql.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
"data": {
66
"term": {
77
"coursesConnection": {
8-
"nodes": [
9-
{
10-
"_id": "1",
11-
"assignmentsConnection": {
12-
"nodes": []
8+
"nodes": [
9+
{
10+
"_id": "1",
11+
"assignmentsConnection": {
12+
"nodes": []
13+
}
1314
}
14-
}
1515
]
1616
}
1717
}
1818
},
1919
"status_code": 200
2020
}
21-
}
21+
}

tests/fixtures/new_quiz.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,33 @@
5555
"instructions": "<p>This is the updated New Quiz. You got this!</p>"
5656
},
5757
"status_code": 200
58+
},
59+
"set_new_quizzes_accommodations_quiz_level": {
60+
"method": "POST",
61+
"endpoint": "courses/1/quizzes/1/accommodations",
62+
"data": {
63+
"message": "Accommodations processed",
64+
"successful": [
65+
{
66+
"user_id": 123
67+
}
68+
],
69+
"failed": []
70+
},
71+
"status_code": 200
72+
},
73+
"set_new_quizzes_accommodations_course_level": {
74+
"method": "POST",
75+
"endpoint": "courses/1/accommodations",
76+
"data": {
77+
"message": "Accommodations processed",
78+
"successful": [
79+
{
80+
"user_id": 456
81+
}
82+
],
83+
"failed": []
84+
},
85+
"status_code": 200
5886
}
5987
}

0 commit comments

Comments
 (0)