Skip to content

Commit 9f03017

Browse files
authored
Release v3.4.0
Release v3.4.0
2 parents 88e3937 + 5104eb6 commit 9f03017

32 files changed

+864
-60
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

.github/workflows/deploy.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ jobs:
99
deploy:
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v1
12+
- uses: actions/checkout@v4
1313

1414
- name: Set up Python
15-
uses: actions/setup-python@v2
15+
uses: actions/setup-python@v5
16+
17+
- name: Install build dependency
18+
run: pip install build
1619

1720
- name: Create source distribution
18-
run: python setup.py sdist
21+
run: python -m build
1922

2023
- name: Publish distribution to PyPI
2124
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/run-tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
14+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
1515

1616
steps:
17-
- uses: actions/checkout@v2
17+
- uses: actions/checkout@v4
1818
- name: Set up Python ${{ matrix.python-version }}
19-
uses: actions/setup-python@v2
19+
uses: actions/setup-python@v5
2020
with:
2121
python-version: ${{ matrix.python-version }}
2222
- name: Install dependencies

AUTHORS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- Adrian Goetz [@a-goetz](https://github.com/a-goetz)
1818
- Aileen Pongnon [@aileenpongnon](https://github.com/aileenpongnon)
1919
- Alyssa Davis [@allygator](https://github.com/allygator)
20+
- Alex Gabriel Nunez-Carrasquillo [@alportoricensis](https://github.com/alportoricensis)
2021
- [@amorqiu](https://github.com/amorqiu)
2122
- Andrew Gardener [@andrew-gardener](https://github.com/andrew-gardener)
2223
- Anthony Rodriguez [@AnthonyRodriguez726](https://github.com/AnthonyRodriguez726)
@@ -42,6 +43,7 @@
4243
- Deundre Williams [@deundrewilliams](https://github.com/deundrewilliams)
4344
- Devin Singh [@devints47](https://github.com/devints47)
4445
- Dmitry Savransky [@dsavransky](https://github.com/dsavransky)
46+
- Elias [@HandcartCactus](https://github.com/HandcartCactus)
4547
- Elise Heron [@thedarkestknight](https://github.com/thedarkestknight)
4648
- Elli Howard [@qwertynerd97](https://github.com/qwertynerd97)
4749
- Erik Tews [@eriktews](https://github.com/eriktews)
@@ -53,6 +55,7 @@
5355
- Ian Altgilbers [@altgilbers](https://github.com/altgilbers)
5456
- Ian Turgeon [@iturgeon](https://github.com/iturgeon)
5557
- [@jackrsteiner](https://github.com/jackrsteiner)
58+
- Jasmine Hou [@jsmnhou](https://github.com/jsmnhou)
5659
- John Raible [@rebelaide](https://github.com/rebelaide)
5760
- Joon Ro [@joonro](https://github.com/joonro)
5861
- Jonah Majumder [@jonahmajumder](https://github.com/jonahmajumder)

CHANGELOG.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,36 @@
22

33
## [Unreleased]
44

5+
## [3.4.0] - 2025-11-10
6+
7+
### New Endpoint Coverage
8+
9+
- LTI Resource Links (Thanks, [@jsmnhou](https://github.com/jsmnhou))
10+
- Smart Search API [BETA] (Thanks, [@alportoricensis](https://github.com/alportoricensis))
11+
- New Quizzes Accommodations
12+
13+
### General
14+
15+
- Added support for Python 3.12 and 3.13
16+
- Dropped support for Python 3.7 and 3.8
17+
18+
### Backstage
19+
20+
- Updated deploy Action to use more modern processes.
21+
- Updated `PaginatedList` to be type-aware, showing which class is included in the response. (Thanks [@HandcartCactus](https://github.com/HandcartCactus))
22+
- Updated Sphinx
23+
- Reworked how `Requester` handles JSON-only POST requests (currently, only New Quizzes Accommodations and GraphQL)
24+
525
## [3.3.0] - 2023-08-27
626

727
### General
828

929
- Added documentation for PaginatedList
10-
- Rework requester URLs to accomodate graphql and new quizzes endpoints (Thanks, [@bennettscience](https://github.com/bennettscience))
30+
- Rework requester URLs to accommodate graphql and new quizzes endpoints (Thanks, [@bennettscience](https://github.com/bennettscience))
1131

1232
### Bugfixes
1333

14-
- Fixed PaginatedList not respecting new quizzes endpoints (Thanks, [@matthewf-ucsd](https://github.com/matthewf-ucsd))
34+
- Fixed PaginatedList not respecting new quizzes endpoints (Thanks, [@jonespm](https://github.com/jonespm))
1535

1636
### Backstage
1737

@@ -636,7 +656,8 @@ Huge thanks to [@liblit](https://github.com/liblit) for lots of issues, suggesti
636656
- Fixed some incorrectly defined parameters
637657
- Fixed an issue where tests would fail due to an improperly configured requires block
638658

639-
[Unreleased]: https://github.com/ucfopen/canvasapi/compare/v3.3.0...develop
659+
[Unreleased]: https://github.com/ucfopen/canvasapi/compare/v3.4.0...develop
660+
[3.4.0]: https://github.com/ucfopen/canvasapi/compare/v3.3.0...v3.4.0
640661
[3.3.0]: https://github.com/ucfopen/canvasapi/compare/v3.2.0...v3.3.0
641662
[3.2.0]: https://github.com/ucfopen/canvasapi/compare/v3.1.0...v3.2.0
642663
[3.1.0]: https://github.com/ucfopen/canvasapi/compare/v3.0.0...v3.1.0

canvasapi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
__all__ = ["Canvas"]
66

7-
__version__ = "3.3.0"
7+
__version__ = "3.4.0"

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: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@
2323
from canvasapi.grading_period import GradingPeriod
2424
from canvasapi.grading_standard import GradingStandard
2525
from canvasapi.license import License
26+
from canvasapi.lti_resource_link import LTIResourceLink
2627
from canvasapi.module import Module
27-
from canvasapi.new_quiz import NewQuiz
28+
from canvasapi.new_quiz import AccommodationResponse, NewQuiz
2829
from canvasapi.outcome_import import OutcomeImport
2930
from canvasapi.page import Page
3031
from canvasapi.paginated_list import PaginatedList
3132
from canvasapi.progress import Progress
3233
from canvasapi.quiz import QuizExtension
3334
from canvasapi.rubric import Rubric, RubricAssociation
35+
from canvasapi.searchresult import SearchResult
3436
from canvasapi.submission import GroupedSubmission, Submission
3537
from canvasapi.tab import Tab
3638
from canvasapi.todo import Todo
@@ -438,6 +440,39 @@ def create_late_policy(self, **kwargs):
438440

439441
return LatePolicy(self._requester, late_policy_json["late_policy"])
440442

443+
def create_lti_resource_link(self, url, title=None, custom=None, **kwargs):
444+
"""
445+
Create a new LTI resource link.
446+
447+
:calls: `POST /api/v1/courses/:course_id/lti_resource_links \
448+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.create>`_
449+
450+
:param url: The launch URL for the resource link.
451+
:type url: `str`
452+
:param title: The title of the resource link.
453+
:type title: `str`, optional
454+
:param custom: Custom parameters to send to the tool.
455+
:type custom: `dict`, optional
456+
457+
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
458+
"""
459+
460+
if not url:
461+
raise RequiredFieldMissing("url is required as a str.")
462+
463+
kwargs["url"] = url
464+
if title:
465+
kwargs["title"] = title
466+
if custom:
467+
kwargs["custom"] = custom
468+
469+
response = self._requester.request(
470+
"POST",
471+
f"courses/{self.id}/lti_resource_links",
472+
_kwargs=combine_kwargs(**kwargs),
473+
)
474+
return LTIResourceLink(self._requester, response.json())
475+
441476
def create_module(self, module, **kwargs):
442477
"""
443478
Create a new module.
@@ -1645,6 +1680,49 @@ def get_licenses(self, **kwargs):
16451680
_kwargs=combine_kwargs(**kwargs),
16461681
)
16471682

1683+
def get_lti_resource_link(self, lti_resource_link, **kwargs):
1684+
"""
1685+
Return details about the specified resource link.
1686+
1687+
:calls: `GET /api/v1/courses/:course_id/lti_resource_links/:id \
1688+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.show>`_
1689+
1690+
:param lti_resource_link: The object or ID of the LTI resource link.
1691+
:type lti_resource_link: :class:`canvasapi.lti_resource_link.LTIResourceLink` or int
1692+
1693+
:rtype: :class:`canvasapi.lti_resource_link.LTIResourceLink`
1694+
"""
1695+
1696+
lti_resource_link_id = obj_or_id(
1697+
lti_resource_link, "lti_resource_link", (LTIResourceLink,)
1698+
)
1699+
1700+
response = self._requester.request(
1701+
"GET",
1702+
f"courses/{self.id}/lti_resource_links/{lti_resource_link_id}",
1703+
_kwargs=combine_kwargs(**kwargs),
1704+
)
1705+
return LTIResourceLink(self._requester, response.json())
1706+
1707+
def get_lti_resource_links(self, **kwargs):
1708+
"""
1709+
Returns all LTI resource links for this course as a PaginatedList.
1710+
1711+
:calls: `GET /api/v1/courses/:course_id/lti_resource_links \
1712+
<https://canvas.instructure.com/doc/api/lti_resource_links.html#method.lti/resource_links.index>`_
1713+
1714+
:rtype: :class:`canvasapi.paginated_list.PaginatedList` of
1715+
:class:`canvasapi.lti_resource_link.LTIResourceLink`
1716+
"""
1717+
1718+
return PaginatedList(
1719+
LTIResourceLink,
1720+
self._requester,
1721+
"GET",
1722+
f"courses/{self.id}/lti_resource_links",
1723+
kwargs=combine_kwargs(**kwargs),
1724+
)
1725+
16481726
def get_migration_systems(self, **kwargs):
16491727
"""
16501728
Return a list of migration systems.
@@ -1768,7 +1846,7 @@ def get_new_quiz(self, assignment, **kwargs):
17681846

17691847
def get_new_quizzes(self, **kwargs):
17701848
"""
1771-
Get a list of new quizzes.
1849+
Get a list of new quizzes in this course.
17721850
17731851
:calls: `GET /api/quiz/v1/courses/:course_id/quizzes \
17741852
<https://canvas.instructure.com/doc/api/new_quizzes.html#method.new_quizzes/quizzes_api.index>`_
@@ -1783,6 +1861,7 @@ def get_new_quizzes(self, **kwargs):
17831861
self._requester,
17841862
"GET",
17851863
endpoint,
1864+
{"course_id": self.id},
17861865
_url_override="new_quizzes",
17871866
_kwargs=combine_kwargs(**kwargs),
17881867
)
@@ -2576,6 +2655,33 @@ def resolve_path(self, full_path=None, **kwargs):
25762655
_kwargs=combine_kwargs(**kwargs),
25772656
)
25782657

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+
25792685
def set_quiz_extensions(self, quiz_extensions, **kwargs):
25802686
"""
25812687
Set extensions for student all quiz submissions in a course.
@@ -2667,6 +2773,32 @@ def show_front_page(self, **kwargs):
26672773

26682774
return Page(self._requester, page_json)
26692775

2776+
def smartsearch(self, q, **kwargs):
2777+
"""
2778+
AI-powered course content search.
2779+
2780+
:calls: `GET /api/v1/courses/:course_id/smartsearch \
2781+
<https://canvas.instructure.com/doc/api/smart_search.html#method.smart_search.search>`_
2782+
2783+
:param q: The search query string.
2784+
:type q: str
2785+
:param kwargs: Optional query parameters (e.g., filter, per_page).
2786+
:type kwargs: dict
2787+
:rtype: :class:`canvasapi.paginated_list.PaginatedList` of
2788+
:class:`canvasapi.searchresult.SearchResult`
2789+
"""
2790+
kwargs["q"] = q
2791+
2792+
return PaginatedList(
2793+
SearchResult,
2794+
self._requester,
2795+
"GET",
2796+
f"courses/{self.id}/smartsearch",
2797+
{"course_id": self.id},
2798+
_root="results",
2799+
_kwargs=combine_kwargs(**kwargs),
2800+
)
2801+
26702802
def submissions_bulk_update(self, **kwargs):
26712803
"""
26722804
Update the grading and comments on multiple student's assignment

canvasapi/lti_resource_link.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from canvasapi.canvas_object import CanvasObject
2+
3+
4+
class LTIResourceLink(CanvasObject):
5+
def __str__(self):
6+
return "{} ({})".format(self.url, self.title)

0 commit comments

Comments
 (0)