Repository: ucfopen/canvasapi (language: python)

Issue:
Course.get_enabled_features throws AttributeError: 'str' object has no attribute 'update'
# Describe the bug

When trying to access the enabled features for a course using Course.get_enabled_features, an AttributeError is thrown. Full output from an ipython session is below. I am guessing that this is because the API call to `/features/enabled` does not return output in the same format as the call to `/features` (see `curl` output below), but I have not been able to verify that this is the cause of the problem.

# To Reproduce

Steps to reproduce the behavior:

```
In [18]: canvas = Canvas(API_URL, API_KEY)

In [19]: course = canvas.get_course(COURSE_ID)

In [20]: features = course.get_features()

In [21]: type(features)
Out[21]: canvasapi.paginated_list.PaginatedList

In [22]: features[0]
Out[22]: Feature(_requester=<canvasapi.requester.Requester object at 0x7fca7374aad0>, feature=analytics_2,<. . .>

In [23]: enabled_features = course.get_enabled_features()

In [24]: type(enabled_features)
Out[24]: canvasapi.paginated_list.PaginatedList

In [25]: enabled_features[0]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-25-59616ad1fc05> in <module>
----> 1 enabled_features[0]

/usr/local/lib/python3.10/dist-packages/canvasapi/paginated_list.py in __getitem__(self, index)
     13             if index < 0:
     14                 raise IndexError("Cannot negative index a PaginatedList")
---> 15             self._get_up_to_index(index)
     16             return self._elements[index]
     17         else:

/usr/local/lib/python3.10/dist-packages/canvasapi/paginated_list.py in _get_up_to_index(self, index)
     87     def _get_up_to_index(self, index):
     88         while len(self._elements) <= index and self._has_next():
---> 89             self._grow()
     90
     91     def _grow(self):

/usr/local/lib/python3.10/dist-packages/canvasapi/paginated_list.py in _grow(self)
     90
     91     def _grow(self):
---> 92         new_elements = self._get_next_page()
     93         self._elements += new_elements
     94         return new_elements

/usr/local/lib/python3.10/dist-packages/canvasapi/paginated_list.py in _get_next_page(self)
     80         for element in data:
     81             if element is not None:
---> 82                 element.update(self._extra_attribs)
     83                 content.append(self._content_class(self._requester, element))
     84
```


# Expected behavior

get_enabled_features() should return a response of the same format as get_features()

# Environment information

<!--(please complete the following information)-->

- Python version: 3.10.6
- CanvasAPI version: 3.0.0

# Additional context

The API call to `/features/enabled` returns a simple list as output, rather than a full dict of feature information.

```
curl "https://<API_URL>/api/v1/courses/<COURSE_ID>/features" -H "Authorization: Bearer <API_TOKEN>"
[{"feature":"analytics_2","applies_to":"Course","beta":true,"type":"feature_option","display_name":"New Course and User Analytics","description":"Show new analytics for course and user data. Data may take 24 hours to appear in New Analytics after setting this feature flag to ON. For full details, please see the \u003ca href=\"https://community.canvaslms.com/t5/New-Analytics-Users/gh-p/analytics\"\u003eNew Analytics user group\u003c/a\u003e in the Canvas Community.","feature_flag":{"context_id":1,"context_type":"Account","feature":"analytics_2","state":"allowed_on","locking_account_id":null,"transitions":{"off":{"locked":false},"on":{"locked":false}},"locked":false,"parent_state":"allowed_on"}},{"feature":"anonymous_instructor_annotations","applies_to":"Course","root_opt_in":false,"type":"setting","display_name":"Anonymous Instructor Annotations","description":"Anonymize all instructor comments and annotations within DocViewer","feature_flag":{"context_id":1,"context_type":"Account","feature":"anonymous_instructor_annotations","state":"on","locking_account_id":null,"transitions":{"off":{"locked":false}},"locked":true,"parent_state":"on"}},{"feature":"epub_export","applies_to":"Course","root_opt_in":true,"type":"setting","display_name":"ePub Exporting","description":"This enables users to generate and download course ePub.","feature_flag":{"context_id":1,"context_type":"Account","feature":"epub_export","state":"allowed","locking_account_id":null,"transitions":{"off":{"locked":false},"on":{"locked":false}},"locked":false,"parent_state":"allowed"}},{"feature":"final_grades_override","applies_to":"Course","root_opt_in":true,"type":"setting","display_name":"Final Grade Override","description":"Enable ability to alter the final grade for the entire course without changing scores for assignments.","feature_flag":{"context_id":65221,"context_type":"Course","feature":"final_grades_override","state":"on","locking_account_id":null,"transitions":{"off":{"locked":true},"on":{}},"locked":false,"parent_state":"allowed"}},{"feature":"outcome_gradebook","applies_to":"Course","root_opt_in":false,"type":"setting","display_name":"Learning Mastery Gradebook","description":"Learning Mastery Gradebook provides a way for teachers to quickly view student and course\nprogress on course learning outcomes. Outcomes are presented in a Gradebook-like\nformat and student progress is displayed both as a numerical score and as mastered/near\nmastery/remedial.","feature_flag":{"feature":"outcome_gradebook","state":"allowed","transitions":{"off":{"locked":false},"on":{"locked":false}},"locked":false,"parent_state":"allowed"}},{"feature":"quizzes_next","applies_to":"Course","type":"feature_option","display_name":"New Quizzes","description":"This feature enhances the Canvas experience for quizzing. When this flag is enabled, the New Quizzes tool will be available. For full details, please see the \u003ca href=\"https://community.canvaslms.com/t5/New-Quizzes-Users/gh-p/quizzes\"\u003eNew Quizzes user group\u003c/a\u003e in the Canvas Community.","feature_flag":{"context_id":1,"context_type":"Account","feature":"quizzes_next","state":"on","locking_account_id":null,"transitions":{"off":{"locked":false}},"locked":true,"parent_state":"on"}}]

curl "https://<API_URL>/api/v1/courses/<COURSE_ID>/features/enabled" -H "Authorization: Bearer <API_TOKEN>"
["analytics_2","anonymous_instructor_annotations","final_grades_override","quizzes_next","quiz_log_auditing","anonymous_marking","moderated_grading","new_quizzes_by_default"]
```

This is confirmed by the Canvas API docs: https://canvas.instructure.com/doc/api/feature_flags.html#method.feature_flags.enabled_features.



Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9d0cb7b7..994832db 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
 repos:
-  - repo: https://github.com/ambv/black
-    rev: stable
+  - repo: https://github.com/psf/black
+    rev: 23.3.0
     hooks:
     - id: black
-      language_version: python3.7
+      language_version: python3
diff --git a/AUTHORS.md b/AUTHORS.md
index f82fa2ef..3b38cd8d 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -33,6 +33,7 @@ Patches and Suggestions
 - Damian Sweeney [@damianfs](https://github.com/damianfs)
 - Daniel Brinkman [@DanBrink91](https://github.com/DanBrink91)
 - Daniel Grobani [@dgrobani](https://github.com/dgrobani)
+- Daniel Molares [@dmols](https://github.com/dmols)
 - David Warden [@dfwarden](https://github.com/dfwarden)
 - Davis Goff [@Goff-Davis](https://github.com/Goff-Davis)
 - Deundre Williams [@deundrewilliams](https://github.com/deundrewilliams)
@@ -60,13 +61,16 @@ Patches and Suggestions
 - Kenny Perez [@kennygperez](https://github.com/kennygperez)
 - [@kensler](https://github.com/kensler)
 - Lawrence Oks [@ljoks](https://github.com/ljoks)
+- Lee Fent [@lafent](https://github.com/lafent)
 - Leonard Camacho [@lcamacho](https://github.com/lcamacho)
+- Lucas Salas [@lucas-salas](https://github.com/lucas-salas)
 - Mark Lalor [@MarkLalor](https://github.com/MarkLalor)
 - Markus [@elec3647](https://github.com/elec3647)
 - Matthew Fedder [@matthewf-ucsd](https://github.com/matthewf-ucsd)
 - Matthew Jones [@jonespm](https://github.com/jonespm)
 - Michael Phelps [@nottheswimmer](https://github.com/nottheswimmer)
 - Mike Nahmias [@Mike-Nahmias](https://github.com/Mike-Nahmias)
+- Mike Suhan [@mikesuhan](https://github.com/mikesuhan)
 - Nathan Dabu [@nathaned](https://github.com/nathaned)
 - Petar Nikolovski [@petarGitNik](https://github.com/petarGitNik)
 - Philip Austin [@phaustin](https://github.com/phaustin)
@@ -79,6 +83,7 @@ Patches and Suggestions
 - Stephen Woosley [@stephenwoosley](https://github.com/stephenwoosley)
 - Steven Bell [@stevenbell](https://github.com/stevenbell)
 - Steven Williams [@onomou](https://github.com/onomou)
+- [@svanderwulp](https://github.com/svanderwulp)
 - Tinson Lai [@laitingsheng](https://github.com/laitingsheng)
 - Toan Vu [@vutoan1245](https://github.com/vutoan1245)
 - [@Tobiaqs](https://github.com/Tobiaqs)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 532f4cc9..39d8e444 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,48 @@
 
 ## [Unreleased]
 
+## [3.1.0] - 2023-04-21
+
+### New Endpoint Coverage
+
+- Account Calendars (Thanks, [@dmols](https://github.com/dmols))
+    - List available account calendars
+    - Get a single account calendar
+    - Update a calendar's visibility
+    - Update many calendars' visibility
+    - List all account calendars
+- Enrollments (Thanks, [@svanderwulp](https://github.com/svanderwulp))
+    - Accept Course Invitation
+    - Reject Course Invitation
+- File (Thanks, [@bennettscience](https://github.com/bennettscience))
+    - Update File
+- JWTs (Thanks [@dmols](https://github.com/dmols))
+    - Create JWTs
+    - Refresh JWTs
+- Moderation Grading (Moderation Set)
+    - List students selected for moderation
+    - Select students for moderation
+- Query Course Events (Thanks, [@dmols](https://github.com/dmols))
+    - Query by course
+    - Query by account
+- Rubrics (Thanks, [@bennettscience](https://github.com/bennettscience))
+    - Create, Update, and Delete Rubric Assessments
+    - Create a Rubric Association
+- Users
+    - Terminate all user sessions (Thanks, [@lucas-salas](https://github.com/lucas-salas))
+
+### General
+
+- Updated Codecov action to v3
+
+### Bugfixes
+
+- Fixed an issue where kwargs were not passed along to Canvas in `Course.get_module()`. (Thanks, [@bennettscience](https://github.com/bennettscience))
+- Fixed an issue where not all functions allowed arbitrary keyword arguments. Added a test to detect and prevent this for the future.
+- Fixed an issue with `Course.get_enabled_features()` where it would throw an error trying to paginate. It now returns a list of strings directly. (Thanks, [@bennettscience](https://github.com/bennettscience))
+- Added missing docs for `AssignmentOverride`. (Thanks, [@lafent](https://github.com/lafent))
+- Fixed a typo in `Canvas.create_calendar_event()` where an error message improperly listed the missing key as 'context_codes' instead of 'context_code'. (Thanks, [@dmols](https://github.com/dmols) and [@mikesuhan](https://github.com/mikesuhan))
+
 ## [3.0.0] - 2022-09-21
 
 ### New Endpoint Coverage
@@ -563,7 +605,8 @@ Huge thanks to [@liblit](https://github.com/liblit) for lots of issues, suggesti
 - Fixed some incorrectly defined parameters
 - Fixed an issue where tests would fail due to an improperly configured requires block
 
-[Unreleased]: https://github.com/ucfopen/canvasapi/compare/v3.0.0...develop
+[Unreleased]: https://github.com/ucfopen/canvasapi/compare/v3.1.0...develop
+[3.1.0]: https://github.com/ucfopen/canvasapi/compare/v3.0.0...v3.1.0
 [3.0.0]: https://github.com/ucfopen/canvasapi/compare/v2.2.0...v3.0.0
 [2.2.0]: https://github.com/ucfopen/canvasapi/compare/v2.1.0...v2.2.0
 [2.1.0]: https://github.com/ucfopen/canvasapi/compare/v2.0.0...v2.1.0
diff --git a/canvasapi/__init__.py b/canvasapi/__init__.py
index 45b18f83..c3f2ed26 100644
--- a/canvasapi/__init__.py
+++ b/canvasapi/__init__.py
@@ -4,4 +4,4 @@
 
 __all__ = ["Canvas"]
 
-__version__ = "3.0.0"
+__version__ = "3.1.0"
diff --git a/canvasapi/account.py b/canvasapi/account.py
index 2aa37ee5..a1c39c12 100644
--- a/canvasapi/account.py
+++ b/canvasapi/account.py
@@ -1,12 +1,26 @@
+from canvasapi.account_calendar import AccountCalendar
+from canvasapi.authentication_event import AuthenticationEvent
+from canvasapi.authentication_provider import AuthenticationProvider
 from canvasapi.canvas_object import CanvasObject
+from canvasapi.content_migration import ContentMigration, Migrator
+from canvasapi.course import Course
+from canvasapi.course_event import CourseEvent
+from canvasapi.enrollment import Enrollment
+from canvasapi.enrollment_term import EnrollmentTerm
 from canvasapi.exceptions import CanvasException, RequiredFieldMissing
+from canvasapi.external_tool import ExternalTool
 from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.grading_period import GradingPeriod
 from canvasapi.grading_standard import GradingStandard
+from canvasapi.group import Group, GroupCategory
+from canvasapi.login import Login
+from canvasapi.outcome import OutcomeGroup, OutcomeLink
 from canvasapi.outcome_import import OutcomeImport
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.rubric import Rubric
+from canvasapi.scope import Scope
 from canvasapi.sis_import import SisImport
+from canvasapi.user import User
 from canvasapi.util import combine_kwargs, file_or_path, obj_or_id, obj_or_str
 
 
@@ -62,8 +76,6 @@ def add_authentication_providers(self, **kwargs):
 
         :rtype: :class:`canvasapi.authentication_provider.AuthenticationProvider`
         """
-        from canvasapi.authentication_provider import AuthenticationProvider
-
         response = self._requester.request(
             "POST",
             "accounts/{}/authentication_providers".format(self.id),
@@ -123,8 +135,6 @@ def close_notification_for_user(self, user, notification, **kwargs):
 
         :rtype: :class:`canvasapi.account.AccountNotification`
         """
-        from canvasapi.user import User
-
         user_id = obj_or_id(user, "user", (User,))
         notif_id = obj_or_id(notification, "notification", (AccountNotification,))
 
@@ -165,8 +175,6 @@ def create_admin(self, user, **kwargs):
 
         :rtype: :class:`canvasapi.account.Admin`
         """
-        from canvasapi.user import User
-
         user_id = obj_or_id(user, "user", (User,))
         kwargs["user_id"] = user_id
 
@@ -189,8 +197,6 @@ def create_content_migration(self, migration_type, **kwargs):
 
         :rtype: :class:`canvasapi.content_migration.ContentMigration`
         """
-        from canvasapi.content_migration import ContentMigration, Migrator
-
         if isinstance(migration_type, Migrator):
             kwargs["migration_type"] = migration_type.type
         elif isinstance(migration_type, str):
@@ -218,8 +224,6 @@ def create_course(self, **kwargs):
 
         :rtype: :class:`canvasapi.course.Course`
         """
-        from canvasapi.course import Course
-
         response = self._requester.request(
             "POST",
             "accounts/{}/courses".format(self.id),
@@ -237,8 +241,6 @@ def create_enrollment_term(self, **kwargs):
 
         :rtype: :class:`canvasapi.enrollment_term.EnrollmentTerm`
         """
-        from canvasapi.enrollment_term import EnrollmentTerm
-
         response = self._requester.request(
             "POST",
             "accounts/{}/terms".format(self.id),
@@ -269,8 +271,6 @@ def create_external_tool(
         :type shared_secret: str
         :rtype: :class:`canvasapi.external_tool.ExternalTool`
         """
-        from canvasapi.external_tool import ExternalTool
-
         response = self._requester.request(
             "POST",
             "accounts/{}/external_tools".format(self.id),
@@ -296,8 +296,6 @@ def create_group_category(self, name, **kwargs):
         :type name: str
         :rtype: :class:`canvasapi.group.GroupCategory`
         """
-        from canvasapi.group import GroupCategory
-
         response = self._requester.request(
             "POST",
             "accounts/{}/group_categories".format(self.id),
@@ -326,10 +324,8 @@ def create_notification(self, account_notification, **kwargs):
             kwargs["account_notification"] = account_notification
         else:
             raise RequiredFieldMissing(
-                (
-                    "account_notification must be a dictionary with keys "
-                    "'subject', 'message', 'start_at', and 'end_at'."
-                )
+                "account_notification must be a dictionary with keys "
+                "'subject', 'message', 'start_at', and 'end_at'."
             )
 
         response = self._requester.request(
@@ -355,7 +351,6 @@ def create_report(self, report_type, **kwargs):
 
         :rtype: :class:`canvasapi.account.AccountReport`
         """
-
         response = self._requester.request(
             "POST",
             "accounts/{}/reports/{}".format(self.id, report_type),
@@ -398,7 +393,6 @@ def create_sis_import(self, attachment, **kwargs):
 
         :rtype: :class:`canvasapi.sis_import.SisImport`
         """
-
         attachment, is_path = file_or_path(attachment)
 
         try:
@@ -452,8 +446,6 @@ def create_user(self, pseudonym, **kwargs):
         :type pseudonym: dict
         :rtype: :class:`canvasapi.user.User`
         """
-        from canvasapi.user import User
-
         if isinstance(pseudonym, dict) and "unique_id" in pseudonym:
             kwargs["pseudonym"] = pseudonym
         else:
@@ -479,18 +471,16 @@ def create_user_login(self, user, login, **kwargs):
         :type login: `dict`
         :rtype: :class:`canvasapi.login.Login`
         """
-        from canvasapi.login import Login
-
         if isinstance(user, dict) and "id" in user:
             kwargs["user"] = user
         else:
-            raise RequiredFieldMissing(("user must be a dictionary with keys " "'id'."))
+            raise RequiredFieldMissing("user must be a dictionary with keys 'id'.")
 
         if isinstance(login, dict) and "unique_id" in login:
             kwargs["login"] = login
         else:
             raise RequiredFieldMissing(
-                ("login must be a dictionary with keys " "'unique_id'.")
+                "login must be a dictionary with keys 'unique_id'."
             )
 
         response = self._requester.request(
@@ -557,8 +547,6 @@ def delete_admin(self, user, **kwargs):
 
         :rtype: :class:`canvasapi.account.Admin`
         """
-        from canvasapi.user import User
-
         user_id = obj_or_id(user, "user", (User,))
         kwargs["user_id"] = user_id
 
@@ -616,8 +604,6 @@ def delete_user(self, user, **kwargs):
 
         :rtype: :class:`canvasapi.user.User`
         """
-        from canvasapi.user import User
-
         user_id = obj_or_id(user, "user", (User,))
 
         response = self._requester.request(
@@ -627,6 +613,24 @@ def delete_user(self, user, **kwargs):
         )
         return User(self._requester, response.json())
 
+    def get_account_calendar(self, **kwargs):
+        """
+        Returns information about a single account calendar.
+
+        :calls: `GET /api/v1/account_calendars/:account_id \
+        <https://canvas.instructure.com/doc/api/account_calendars.html#method.account_calendars_api.show>`_
+
+        :rtype: :class:`canvasapi.account_calendar.AccountCalendar`
+        """
+
+        response = self._requester.request(
+            "GET",
+            "account_calendars/{}".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return AccountCalendar(self._requester, response.json())
+
     def get_admins(self, **kwargs):
         """
         Get the paginated list of admins for the current account.
@@ -645,6 +649,25 @@ def get_admins(self, **kwargs):
             _kwargs=combine_kwargs(**kwargs),
         )
 
+    def get_all_account_calendars(self, **kwargs):
+        """
+        Lists all account calendars available to the account given.
+
+        :calls: `GET /api/v1/accounts/:account_id/account_calendars \
+        <https://canvas.instructure.com/doc/api/account_calendars.html#method.account_calendars_api.all_calendars>`_
+
+        :returns: Paginated list of all account calendars for the provided account.
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.account_calendar.AccountCalendar`
+        """
+        return PaginatedList(
+            AccountCalendar,
+            self._requester,
+            "GET",
+            "accounts/{}/account_calendars".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_all_outcome_links_in_context(self, **kwargs):
         """
         Get all outcome links for context - BETA
@@ -656,8 +679,6 @@ def get_all_outcome_links_in_context(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.outcome.OutcomeLink`
         """
-        from canvasapi.outcome import OutcomeLink
-
         return PaginatedList(
             OutcomeLink,
             self._requester,
@@ -676,8 +697,6 @@ def get_authentication_events(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
                 :class:`canvasapi.authentication_event.AuthenticationEvent`
         """
-        from canvasapi.authentication_event import AuthenticationEvent
-
         return PaginatedList(
             AuthenticationEvent,
             self._requester,
@@ -699,8 +718,6 @@ def get_authentication_provider(self, authentication_provider, **kwargs):
 
         :rtype: :class:`canvasapi.authentication_provider.AuthenticationProvider`
         """
-        from canvasapi.authentication_provider import AuthenticationProvider
-
         authentication_providers_id = obj_or_id(
             authentication_provider,
             "authentication provider",
@@ -727,8 +744,6 @@ def get_authentication_providers(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.authentication_provider.AuthenticationProvider`
         """
-        from canvasapi.authentication_provider import AuthenticationProvider
-
         return PaginatedList(
             AuthenticationProvider,
             self._requester,
@@ -750,8 +765,6 @@ def get_content_migration(self, content_migration, **kwargs):
 
         :rtype: :class:`canvasapi.content_migration.ContentMigration`
         """
-        from canvasapi.content_migration import ContentMigration
-
         migration_id = obj_or_id(
             content_migration, "content_migration", (ContentMigration,)
         )
@@ -777,8 +790,6 @@ def get_content_migrations(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.content_migration.ContentMigration`
         """
-        from canvasapi.content_migration import ContentMigration
-
         return PaginatedList(
             ContentMigration,
             self._requester,
@@ -798,8 +809,6 @@ def get_courses(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.course.Course`
         """
-        from canvasapi.course import Course
-
         return PaginatedList(
             Course,
             self._requester,
@@ -817,7 +826,6 @@ def get_department_level_grade_data_completed(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/completed/grades".format(self.id),
@@ -834,7 +842,6 @@ def get_department_level_grade_data_current(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/current/grades".format(self.id),
@@ -854,7 +861,6 @@ def get_department_level_grade_data_with_given_term(self, term_id, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/terms/{}/grades".format(self.id, term_id),
@@ -871,7 +877,6 @@ def get_department_level_participation_data_completed(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/completed/activity".format(self.id),
@@ -888,7 +893,6 @@ def get_department_level_participation_data_current(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/current/activity".format(self.id),
@@ -910,7 +914,6 @@ def get_department_level_participation_data_with_given_term(
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/terms/{}/activity".format(self.id, term_id),
@@ -927,7 +930,6 @@ def get_department_level_statistics_completed(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/completed/statistics".format(self.id),
@@ -944,7 +946,6 @@ def get_department_level_statistics_current(self, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/current/statistics".format(self.id),
@@ -964,7 +965,6 @@ def get_department_level_statistics_with_given_term(self, term_id, **kwargs):
 
         :rtype: dict
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/analytics/terms/{}/statistics".format(self.id, term_id),
@@ -979,18 +979,16 @@ def get_enabled_features(self, **kwargs):
         :calls: `GET /api/v1/accounts/:account_id/features/enabled \
         <https://canvas.instructure.com/doc/api/feature_flags.html#method.feature_flags.enabled_features>`_
 
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.feature.Feature`
+        :rtype: `list` of `str`
         """
-        return PaginatedList(
-            Feature,
-            self._requester,
+        response = self._requester.request(
             "GET",
             "accounts/{}/features/enabled".format(self.id),
-            {"account_id": self.id},
             _kwargs=combine_kwargs(**kwargs),
         )
 
+        return response.json()
+
     def get_enrollment(self, enrollment, **kwargs):
         """
         Get an enrollment object by ID.
@@ -1003,8 +1001,6 @@ def get_enrollment(self, enrollment, **kwargs):
 
         :rtype: :class:`canvasapi.enrollment.Enrollment`
         """
-        from canvasapi.enrollment import Enrollment
-
         enrollment_id = obj_or_id(enrollment, "enrollment", (Enrollment,))
 
         response = self._requester.request(
@@ -1026,8 +1022,6 @@ def get_enrollment_term(self, term, **kwargs):
 
         :rtype: :class:`canvasapi.enrollment_term.EnrollmentTerm`
         """
-        from canvasapi.enrollment_term import EnrollmentTerm
-
         term_id = obj_or_id(term, "term", (EnrollmentTerm,))
 
         response = self._requester.request(
@@ -1045,8 +1039,6 @@ def get_enrollment_terms(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.enrollment_term.EnrollmentTerm`
         """
-        from canvasapi.enrollment_term import EnrollmentTerm
-
         return PaginatedList(
             EnrollmentTerm,
             self._requester,
@@ -1067,8 +1059,6 @@ def get_external_tool(self, tool, **kwargs):
 
         :rtype: :class:`canvasapi.external_tool.ExternalTool`
         """
-        from canvasapi.external_tool import ExternalTool
-
         tool_id = obj_or_id(tool, "tool", (ExternalTool,))
 
         response = self._requester.request(
@@ -1089,8 +1079,6 @@ def get_external_tools(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.external_tool.ExternalTool`
         """
-        from canvasapi.external_tool import ExternalTool
-
         return PaginatedList(
             ExternalTool,
             self._requester,
@@ -1152,7 +1140,6 @@ def get_global_notification(self, notification_id, **kwargs):
 
         :rtype: :class:`canvasapi.account.AccountNotification`
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/account_notifications/{}".format(self.id, notification_id),
@@ -1174,7 +1161,6 @@ def get_grading_periods(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.grading_period.GradingPeriod`
         """
-
         return PaginatedList(
             GradingPeriod,
             self._requester,
@@ -1195,7 +1181,6 @@ def get_grading_standards(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.grading_standards.GradingStandard`
         """
-
         return PaginatedList(
             GradingStandard,
             self._requester,
@@ -1214,8 +1199,6 @@ def get_group_categories(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.group.GroupCategory`
         """
-        from canvasapi.group import GroupCategory
-
         return PaginatedList(
             GroupCategory,
             self._requester,
@@ -1233,8 +1216,6 @@ def get_groups(self, **kwargs):
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group`
         """
-        from canvasapi.group import Group
-
         return PaginatedList(
             Group,
             self._requester,
@@ -1274,8 +1255,6 @@ def get_migration_systems(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.content_migration.Migrator`
         """
-        from canvasapi.content_migration import Migrator
-
         return PaginatedList(
             Migrator,
             self._requester,
@@ -1298,8 +1277,6 @@ def get_outcome_group(self, group, **kwargs):
         :returns: An outcome group object.
         :rtype: :class:`canvasapi.outcome.OutcomeGroup`
         """
-        from canvasapi.outcome import OutcomeGroup
-
         outcome_group_id = obj_or_id(group, "outcome group", (OutcomeGroup,))
         response = self._requester.request(
             "GET",
@@ -1320,8 +1297,6 @@ def get_outcome_groups_in_context(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.outcome.OutcomeGroups`
         """
-        from canvasapi.outcome import OutcomeGroup
-
         return PaginatedList(
             OutcomeGroup,
             self._requester,
@@ -1438,7 +1413,6 @@ def get_roles(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.account.Role`
         """
-
         return PaginatedList(
             Role,
             self._requester,
@@ -1457,8 +1431,6 @@ def get_root_outcome_group(self, **kwargs):
         :returns: The OutcomeGroup of the context.
         :rtype: :class:`canvasapi.outcome.OutcomeGroup`
         """
-        from canvasapi.outcome import OutcomeGroup
-
         response = self._requester.request(
             "GET",
             "accounts/{}/root_outcome_group".format(self.id),
@@ -1512,8 +1484,6 @@ def get_scopes(self, **kwargs):
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.scope.Scope`
         """
-        from canvasapi.scope import Scope
-
         return PaginatedList(
             Scope,
             self._requester,
@@ -1533,7 +1503,6 @@ def get_single_grading_standard(self, grading_standard_id, **kwargs):
         :type grading_standard_id: int
         :rtype: :class:`canvasapi.grading_standards.GradingStandard`
         """
-
         response = self._requester.request(
             "GET",
             "accounts/%s/grading_standards/%d" % (self.id, grading_standard_id),
@@ -1576,7 +1545,6 @@ def get_sis_imports(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.sis_import.SisImport`
         """
-
         return PaginatedList(
             SisImport,
             self._requester,
@@ -1597,7 +1565,6 @@ def get_sis_imports_running(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList`
             of :class:`canvasapi.sis_import.SisImport`
         """
-
         return PaginatedList(
             SisImport,
             self._requester,
@@ -1640,8 +1607,6 @@ def get_user_logins(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.login.Login`
         """
-        from canvasapi.login import Login
-
         return PaginatedList(
             Login,
             self._requester,
@@ -1665,8 +1630,6 @@ def get_user_notifications(self, user, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.account.AccountNotification`
         """
-        from canvasapi.user import User
-
         user_id = obj_or_id(user, "user", (User,))
 
         return PaginatedList(
@@ -1686,8 +1649,6 @@ def get_users(self, **kwargs):
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.user.User`
         """
-        from canvasapi.user import User
-
         return PaginatedList(
             User,
             self._requester,
@@ -1708,7 +1669,6 @@ def import_outcome(self, attachment, **kwargs):
 
         :rtype: :class:`canvasapi.outcome_import.OutcomeImport`
         """
-
         attachment, is_path = file_or_path(attachment)
 
         try:
@@ -1727,6 +1687,24 @@ def import_outcome(self, attachment, **kwargs):
             if is_path:
                 attachment.close()
 
+    def query_audit_by_account(self, **kwargs):
+        """
+        List course change events for a specific account.
+
+        :calls: `GET /api/v1/audit/course/accounts/:account_id \
+        <https://canvas.instructure.com/doc/api/course_audit_log.html#method.course_audit_api.for_account>`_
+
+        :rtype: list of :class:`canvasapi.course_event.CourseEvent`
+        """
+
+        return PaginatedList(
+            CourseEvent,
+            self._requester,
+            "GET",
+            "audit/course/accounts/{}".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def show_account_auth_settings(self, **kwargs):
         """
         Return the current state of each account level setting
@@ -1736,7 +1714,6 @@ def show_account_auth_settings(self, **kwargs):
 
         :rtype: :class:`canvasapi.account.SSOSettings`
         """
-
         response = self._requester.request(
             "GET",
             "accounts/{}/sso_settings".format(self.id),
@@ -1774,7 +1751,6 @@ def update_account_auth_settings(self, **kwargs):
 
         :rtype: :class:`canvasapi.account.SSOSettings`
         """
-
         response = self._requester.request(
             "PUT",
             "accounts/{}/sso_settings".format(self.id),
@@ -1783,6 +1759,40 @@ def update_account_auth_settings(self, **kwargs):
 
         return SSOSettings(self._requester, response.json())
 
+    def update_account_calendar_visibility(self, **kwargs):
+        """
+        Update one account calendar's visibility.
+
+        :calls: `PUT /api/v1/account_calendars/:account_id \
+        <https://canvas.instructure.com/doc/api/account_calendars.html#method.account_calendars_api.update>`_
+
+        :rtype: :class:`canvasapi.account_calendar.AccountCalendar`
+        """
+        response = self._requester.request(
+            "PUT",
+            "account_calendars/{}".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return AccountCalendar(self._requester, response.json())
+
+    def update_many_account_calendars_visibility(self, **kwargs):
+        """
+        Update many account calendars visibility at once.
+
+        :calls: `PUT /api/v1/accounts/:account_id/account_calendars \
+        <https://canvas.instructure.com/doc/api/account_calendars.html#method.account_calendars_api.bulk_update>`_
+
+        :rtype: :class:`canvasapi.account_calendar.AccountCalendar`
+        """
+        response = self._requester.request(
+            "PUT",
+            "accounts/{}/account_calendars".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return AccountCalendar(self._requester, response.json())
+
     def update_role(self, role, **kwargs):
         """
         Update permissions for an existing role.
@@ -1830,10 +1840,8 @@ def update_global_notification(self, account_notification, **kwargs):
             kwargs["account_notification"] = account_notification
         else:
             raise RequiredFieldMissing(
-                (
-                    "account_notification must be a dictionary with keys "
-                    "'subject', 'message', 'start_at', and 'end_at'."
-                )
+                "account_notification must be a dictionary with keys "
+                "'subject', 'message', 'start_at', and 'end_at'."
             )
 
         response = self._requester.request(
diff --git a/canvasapi/account_calendar.py b/canvasapi/account_calendar.py
new file mode 100644
index 00000000..3ae3e535
--- /dev/null
+++ b/canvasapi/account_calendar.py
@@ -0,0 +1,6 @@
+from canvasapi.canvas_object import CanvasObject
+
+
+class AccountCalendar(CanvasObject):
+    def __str__(self):
+        return "{} {} ({})".format(self.name, self.visible, self.calendar_event_url)
diff --git a/canvasapi/appointment_group.py b/canvasapi/appointment_group.py
index 27a5d72d..5091987d 100644
--- a/canvasapi/appointment_group.py
+++ b/canvasapi/appointment_group.py
@@ -39,7 +39,7 @@ def edit(self, appointment_group, **kwargs):
             kwargs["appointment_group"] = appointment_group
         else:
             raise RequiredFieldMissing(
-                "Dictionary with key 'context_codes' is required."
+                "Dictionary with key 'context_code' is required."
             )
 
         response = self._requester.request(
diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py
index 325be72d..c09a4208 100644
--- a/canvasapi/assignment.py
+++ b/canvasapi/assignment.py
@@ -207,6 +207,26 @@ def get_provisional_grades_status(self, student_id, **kwargs):
 
         return request_json.get("needs_provisional_grade")
 
+    def get_students_selected_for_moderation(self, **kwargs):
+        """
+        Get a list of students selected for moderation.
+
+        :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/moderated_students \
+        <https://canvas.instructure.com/doc/api/moderated_grading.html#method.moderation_set.index>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.user.User`
+        """
+        return PaginatedList(
+            User,
+            self._requester,
+            "GET",
+            "courses/{}/assignments/{}/moderated_students".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_submission(self, user, **kwargs):
         """
         Get a single submission, based on user id.
@@ -277,6 +297,27 @@ def publish_provisional_grades(self, **kwargs):
         )
         return response.json()
 
+    def select_students_for_moderation(self, **kwargs):
+        """
+        Select student(s) for moderation.
+
+        :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/moderated_students \
+        <https://canvas.instructure.com/doc/api/moderated_grading.html#method.moderation_set.create>`_
+
+        :returns: The list of users that were selected
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.user.User`
+        """
+        return PaginatedList(
+            User,
+            self._requester,
+            "POST",
+            "courses/{}/assignments/{}/moderated_students".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def selected_provisional_grade(self, provisional_grade_id, **kwargs):
         """
         Choose which provisional grade the student should receive for a submission.
@@ -522,7 +563,7 @@ def delete(self, **kwargs):
         """
         Delete this assignment override.
 
-        :calls: `DELETE /api/v1/courses/:course_id/assignments/:assignment_id/overrides/:id
+        :calls: `DELETE /api/v1/courses/:course_id/assignments/:assignment_id/overrides/:id \
         <https://canvas.instructure.com/doc/api/assignments.html#method.assignment_overrides.destroy>`_
 
         :returns: The previous content of the now-deleted assignment override.
@@ -547,7 +588,7 @@ def edit(self, **kwargs):
 
         Note: All current overridden values must be supplied if they are to be retained.
 
-        :calls: `PUT /api/v1/courses/:course_id/assignments/:assignment_id/overrides/:id
+        :calls: `PUT /api/v1/courses/:course_id/assignments/:assignment_id/overrides/:id \
         <https://canvas.instructure.com/doc/api/assignments.html#method.assignment_overrides.update>`_
 
         :rtype: :class:`canvasapi.assignment.AssignmentOverride`
diff --git a/canvasapi/authentication_provider.py b/canvasapi/authentication_provider.py
index 5ffd5c36..f74a7994 100644
--- a/canvasapi/authentication_provider.py
+++ b/canvasapi/authentication_provider.py
@@ -6,6 +6,22 @@ class AuthenticationProvider(CanvasObject):
     def __str__(self):  # pragma: no cover
         return "{} ({})".format(self.auth_type, self.position)
 
+    def delete(self, **kwargs):
+        """
+        Delete the config
+
+        :calls: `DELETE /api/v1/accounts/:account_id/authentication_providers/:id \
+        <https://canvas.instructure.com/doc/api/authentication_providers.html#method.account_authorization_configs.destroy>`_
+
+        :rtype: :class:`canvasapi.authentication_provider.AuthenticationProvider`
+        """
+        response = self._requester.request(
+            "DELETE",
+            "accounts/{}/authentication_providers/{}".format(self.account_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return AuthenticationProvider(self._requester, response.json())
+
     def update(self, **kwargs):
         """
         Update an authentication provider using the same options as the create endpoint
@@ -25,18 +41,3 @@ def update(self, **kwargs):
             super(AuthenticationProvider, self).set_attributes(response.json())
 
         return response.json().get("auth_type")
-
-    def delete(self):
-        """
-        Delete the config
-
-        :calls: `DELETE /api/v1/accounts/:account_id/authentication_providers/:id \
-        <https://canvas.instructure.com/doc/api/authentication_providers.html#method.account_authorization_configs.destroy>`_
-
-        :rtype: :class:`canvasapi.authentication_provider.AuthenticationProvider`
-        """
-        response = self._requester.request(
-            "DELETE",
-            "accounts/{}/authentication_providers/{}".format(self.account_id, self.id),
-        )
-        return AuthenticationProvider(self._requester, response.json())
diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py
index ede61940..aaa23e0d 100644
--- a/canvasapi/canvas.py
+++ b/canvasapi/canvas.py
@@ -1,16 +1,26 @@
 import warnings
 
 from canvasapi.account import Account
+from canvasapi.account_calendar import AccountCalendar
+from canvasapi.appointment_group import AppointmentGroup
+from canvasapi.calendar_event import CalendarEvent
 from canvasapi.comm_message import CommMessage
-from canvasapi.course import Course
+from canvasapi.conversation import Conversation
+from canvasapi.course import Course, CourseNickname
 from canvasapi.course_epub_export import CourseEpubExport
 from canvasapi.current_user import CurrentUser
+from canvasapi.discussion_topic import DiscussionTopic
 from canvasapi.eportfolio import EPortfolio
 from canvasapi.exceptions import RequiredFieldMissing
 from canvasapi.file import File
 from canvasapi.folder import Folder
 from canvasapi.group import Group, GroupCategory
+from canvasapi.jwt import JWT
+from canvasapi.outcome import Outcome, OutcomeGroup
 from canvasapi.paginated_list import PaginatedList
+from canvasapi.planner import PlannerNote, PlannerOverride
+from canvasapi.poll import Poll
+from canvasapi.progress import Progress
 from canvasapi.requester import Requester
 from canvasapi.section import Section
 from canvasapi.todo import Todo
@@ -93,9 +103,6 @@ def conversations_batch_update(self, conversation_ids, event, **kwargs):
         :type event: `str`
         :rtype: :class:`canvasapi.progress.Progress`
         """
-
-        from canvasapi.progress import Progress
-
         ALLOWED_EVENTS = [
             "mark_as_read",
             "mark_as_unread",
@@ -206,8 +213,6 @@ def create_appointment_group(self, appointment_group, **kwargs):
         :type title: `str`
         :rtype: :class:`canvasapi.appointment_group.AppointmentGroup`
         """
-        from canvasapi.appointment_group import AppointmentGroup
-
         if (
             isinstance(appointment_group, dict)
             and "context_codes" in appointment_group
@@ -243,13 +248,11 @@ def create_calendar_event(self, calendar_event, **kwargs):
         :type calendar_event: `dict`
         :rtype: :class:`canvasapi.calendar_event.CalendarEvent`
         """
-        from canvasapi.calendar_event import CalendarEvent
-
         if isinstance(calendar_event, dict) and "context_code" in calendar_event:
             kwargs["calendar_event"] = calendar_event
         else:
             raise RequiredFieldMissing(
-                "Dictionary with key 'context_codes' is required."
+                "Dictionary with key 'context_code' is required."
             )
 
         response = self.__requester.request(
@@ -274,8 +277,6 @@ def create_conversation(self, recipients, body, **kwargs):
         :type body: `str`
         :rtype: list of :class:`canvasapi.conversation.Conversation`
         """
-        from canvasapi.conversation import Conversation
-
         kwargs["recipients"] = recipients
         kwargs["body"] = body
 
@@ -298,6 +299,21 @@ def create_group(self, **kwargs):
         )
         return Group(self.__requester, response.json())
 
+    def create_jwt(self, **kwargs):
+        """
+        Creates a unique JWT to use with other Canvas services.
+
+        :calls: `POST /api/v1/jwts \
+        <https://canvas.instructure.com/doc/api/jw_ts.html#method.jwts.create>`_
+
+        :rtype: list of :class:`canvasapi.jwt.JWT`
+        """
+        response = self.__requester.request(
+            "POST", "jwts", _kwargs=combine_kwargs(**kwargs)
+        )
+
+        return JWT(self.__requester, response.json())
+
     def create_planner_note(self, **kwargs):
         """
         Create a planner note for the current user
@@ -307,8 +323,6 @@ def create_planner_note(self, **kwargs):
 
         :rtype: :class:`canvasapi.planner.PlannerNote`
         """
-        from canvasapi.planner import PlannerNote
-
         response = self.__requester.request(
             "POST", "planner_notes", _kwargs=combine_kwargs(**kwargs)
         )
@@ -329,8 +343,6 @@ def create_planner_override(self, plannable_type, plannable_id, **kwargs):
 
         :rtype: :class:`canvasapi.planner.PlannerOverride`
         """
-        from canvasapi.planner import PlannerOverride
-
         if isinstance(plannable_type, str):
             kwargs["plannable_type"] = plannable_type
         else:
@@ -356,8 +368,6 @@ def create_poll(self, polls, **kwargs):
         :type polls: list of dict
         :rtype: :class:`canvasapi.poll.Poll`
         """
-        from canvasapi.poll import Poll
-
         if (
             isinstance(polls, list)
             and isinstance(polls[0], dict)
@@ -401,6 +411,24 @@ def get_account(self, account, use_sis_id=False, **kwargs):
         )
         return Account(self.__requester, response.json())
 
+    def get_account_calendars(self, **kwargs):
+        """
+        Returns a paginated list of account calendars available to the user.
+
+        :calls: `GET /api/v1/account_calendars \
+        <https://canvas.instructure.com/doc/api/account_calendars.html#method.account_calendars_api.index>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.account_calendar.AccountCalendar`
+        """
+        return PaginatedList(
+            AccountCalendar,
+            self.__requester,
+            "GET",
+            "account_calendars",
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def get_accounts(self, **kwargs):
         """
         List accounts that the current user can view or manage.
@@ -452,8 +480,6 @@ def get_announcements(self, context_codes, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
                 :class:`canvasapi.discussion_topic.DiscussionTopic`
         """
-        from canvasapi.discussion_topic import DiscussionTopic
-
         if type(context_codes) is not list or len(context_codes) == 0:
             raise RequiredFieldMissing("context_codes need to be passed as a list")
 
@@ -493,8 +519,6 @@ def get_appointment_group(self, appointment_group, **kwargs):
 
         :rtype: :class:`canvasapi.appointment_group.AppointmentGroup`
         """
-        from canvasapi.appointment_group import AppointmentGroup
-
         appointment_group_id = obj_or_id(
             appointment_group, "appointment_group", (AppointmentGroup,)
         )
@@ -516,8 +540,6 @@ def get_appointment_groups(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.appointment_group.AppointmentGroup`
         """
-        from canvasapi.appointment_group import AppointmentGroup
-
         return PaginatedList(
             AppointmentGroup,
             self.__requester,
@@ -553,8 +575,6 @@ def get_calendar_event(self, calendar_event, **kwargs):
 
         :rtype: :class:`canvasapi.calendar_event.CalendarEvent`
         """
-        from canvasapi.calendar_event import CalendarEvent
-
         calendar_event_id = obj_or_id(
             calendar_event, "calendar_event", (CalendarEvent,)
         )
@@ -576,8 +596,6 @@ def get_calendar_events(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.calendar_event.CalendarEvent`
         """
-        from canvasapi.calendar_event import CalendarEvent
-
         return PaginatedList(
             CalendarEvent,
             self.__requester,
@@ -624,8 +642,6 @@ def get_conversation(self, conversation, **kwargs):
 
         :rtype: :class:`canvasapi.conversation.Conversation`
         """
-        from canvasapi.conversation import Conversation
-
         conversation_id = obj_or_id(conversation, "conversation", (Conversation,))
 
         response = self.__requester.request(
@@ -645,8 +661,6 @@ def get_conversations(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of \
         :class:`canvasapi.conversation.Conversation`
         """
-        from canvasapi.conversation import Conversation
-
         return PaginatedList(
             Conversation,
             self.__requester,
@@ -716,8 +730,6 @@ def get_course_nickname(self, course, **kwargs):
 
         :rtype: :class:`canvasapi.course.CourseNickname`
         """
-        from canvasapi.course import CourseNickname
-
         course_id = obj_or_id(course, "course", (Course,))
 
         response = self.__requester.request(
@@ -737,8 +749,6 @@ def get_course_nicknames(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.course.CourseNickname`
         """
-        from canvasapi.course import CourseNickname
-
         return PaginatedList(
             CourseNickname,
             self.__requester,
@@ -914,9 +924,6 @@ def get_group_participants(self, appointment_group, **kwargs):
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group`
         """
-        from canvasapi.appointment_group import AppointmentGroup
-        from canvasapi.group import Group
-
         appointment_group_id = obj_or_id(
             appointment_group, "appointment_group", (AppointmentGroup,)
         )
@@ -942,8 +949,6 @@ def get_outcome(self, outcome, **kwargs):
         :returns: An Outcome object.
         :rtype: :class:`canvasapi.outcome.Outcome`
         """
-        from canvasapi.outcome import Outcome
-
         outcome_id = obj_or_id(outcome, "outcome", (Outcome,))
         response = self.__requester.request(
             "GET", "outcomes/{}".format(outcome_id), _kwargs=combine_kwargs(**kwargs)
@@ -963,8 +968,6 @@ def get_outcome_group(self, group, **kwargs):
         :returns: An outcome group object.
         :rtype: :class:`canvasapi.outcome.OutcomeGroup`
         """
-        from canvasapi.outcome import OutcomeGroup
-
         outcome_group_id = obj_or_id(group, "group", (OutcomeGroup,))
 
         response = self.__requester.request(
@@ -987,8 +990,6 @@ def get_planner_note(self, planner_note, **kwargs):
 
         :rtype: :class:`canvasapi.planner.PlannerNote`
         """
-        from canvasapi.planner import PlannerNote
-
         if isinstance(planner_note, int) or isinstance(planner_note, PlannerNote):
             planner_note_id = obj_or_id(planner_note, "planner_note", (PlannerNote,))
         else:
@@ -1014,8 +1015,6 @@ def get_planner_notes(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.planner.PlannerNote`
         """
-        from canvasapi.planner import PlannerNote
-
         return PaginatedList(
             PlannerNote,
             self.__requester,
@@ -1036,8 +1035,6 @@ def get_planner_override(self, planner_override, **kwargs):
 
         :rtype: :class:`canvasapi.planner.PlannerOverride`
         """
-        from canvasapi.planner import PlannerOverride
-
         if isinstance(planner_override, int) or isinstance(
             planner_override, PlannerOverride
         ):
@@ -1067,8 +1064,6 @@ def get_planner_overrides(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.planner.PlannerOverride`
         """
-        from canvasapi.planner import PlannerOverride
-
         return PaginatedList(
             PlannerOverride,
             self.__requester,
@@ -1088,8 +1083,6 @@ def get_poll(self, poll, **kwargs):
         :type poll: int
         :rtype: :class:`canvasapi.poll.Poll`
         """
-        from canvasapi.poll import Poll
-
         poll_id = obj_or_id(poll, "poll", (Poll,))
 
         response = self.__requester.request(
@@ -1107,8 +1100,6 @@ def get_polls(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.poll.Poll`
         """
-        from canvasapi.poll import Poll
-
         return PaginatedList(
             Poll,
             self.__requester,
@@ -1130,9 +1121,6 @@ def get_progress(self, progress, **kwargs):
 
         :rtype: :class:`canvasapi.progress.Progress`
         """
-
-        from canvasapi.progress import Progress
-
         progress_id = obj_or_id(progress, "progress", (Progress,))
 
         response = self.__requester.request(
@@ -1150,8 +1138,6 @@ def get_root_outcome_group(self, **kwargs):
         :returns: The OutcomeGroup of the context.
         :rtype: :class:`canvasapi.outcome.OutcomeGroup`
         """
-        from canvasapi.outcome import OutcomeGroup
-
         response = self.__requester.request(
             "GET", "global/root_outcome_group", _kwargs=combine_kwargs(**kwargs)
         )
@@ -1260,9 +1246,6 @@ def get_user_participants(self, appointment_group, **kwargs):
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.user.User`
         """
-        from canvasapi.appointment_group import AppointmentGroup
-        from canvasapi.user import User
-
         appointment_group_id = obj_or_id(
             appointment_group, "appointment_group", (AppointmentGroup,)
         )
@@ -1302,6 +1285,27 @@ def graphql(self, query, variables=None, **kwargs):
 
         return response.json()
 
+    def refresh_jwt(self, jwt, **kwargs):
+        """
+        Refreshes a JWT for reuse with other canvas services. It generates a
+        different JWT each time it's called; expires after one hour.
+
+        :calls: `POST /api/v1/jwts/refresh \
+        <https://canvas.instructure.com/doc/api/jw_ts.html#method.jwts.refresh>`_
+
+        :param jwt: An existing JWT to refresh.
+        :type jwt: str or :class:`canvasapi.jwt.JWT`
+        :rtype: :class:`canvasapi.jwt.JWT`
+        """
+        if isinstance(jwt, JWT):
+            jwt = jwt.token
+
+        response = self.__requester.request(
+            "POST", "jwts/refresh", jwt=jwt, _kwargs=combine_kwargs(**kwargs)
+        )
+
+        return JWT(self.__requester, response.json())
+
     def reserve_time_slot(self, calendar_event, participant_id=None, **kwargs):
         """
         Return single Calendar Event by id
@@ -1317,8 +1321,6 @@ def reserve_time_slot(self, calendar_event, participant_id=None, **kwargs):
 
         :rtype: :class:`canvasapi.calendar_event.CalendarEvent`
         """
-        from canvasapi.calendar_event import CalendarEvent
-
         calendar_event_id = obj_or_id(
             calendar_event, "calendar_event", (CalendarEvent,)
         )
@@ -1400,8 +1402,6 @@ def set_course_nickname(self, course, nickname, **kwargs):
 
         :rtype: :class:`canvasapi.course.CourseNickname`
         """
-        from canvasapi.course import CourseNickname
-
         course_id = obj_or_id(course, "course", (Course,))
 
         kwargs["nickname"] = nickname
diff --git a/canvasapi/communication_channel.py b/canvasapi/communication_channel.py
index 91975374..acc86105 100644
--- a/canvasapi/communication_channel.py
+++ b/canvasapi/communication_channel.py
@@ -109,7 +109,6 @@ def update_multiple_preferences(self, notification_preferences, **kwargs):
         :rtype: :class:`canvasapi.notification_preference.NotificationPreference`
         """
         if isinstance(notification_preferences, dict) and notification_preferences:
-
             for key, value in notification_preferences.items():
                 try:
                     if not value["frequency"]:
diff --git a/canvasapi/conversation.py b/canvasapi/conversation.py
index d1714b49..bb59f7db 100644
--- a/canvasapi/conversation.py
+++ b/canvasapi/conversation.py
@@ -6,43 +6,27 @@ class Conversation(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.subject, self.id)
 
-    def edit(self, **kwargs):
+    def add_message(self, body, **kwargs):
         """
-        Update a conversation.
+        Add a message to a conversation.
 
-        :calls: `PUT /api/v1/conversations/:id \
-        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.update>`_
+        :calls: `POST /api/v1/conversations/:id/add_message \
+        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.add_message>`_
 
-        :rtype: `bool`
+        :param body: The body of the conversation.
+        :type body: str
+        :returns: A conversation with only the most recent message.
+        :rtype: :class:`canvasapi.account.Conversation`
         """
         response = self._requester.request(
-            "PUT", "conversations/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
+            "POST",
+            "conversations/{}/add_message".format(self.id),
+            body=body,
+            _kwargs=combine_kwargs(**kwargs),
         )
+        return Conversation(self._requester, response.json())
 
-        if response.json().get("id"):
-            super(Conversation, self).set_attributes(response.json())
-            return True
-        else:
-            return False
-
-    def delete(self):
-        """
-        Delete a conversation.
-
-        :calls: `DELETE /api/v1/conversations/:id \
-        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.destroy>`_
-
-        :rtype: `bool`
-        """
-        response = self._requester.request("DELETE", "conversations/{}".format(self.id))
-
-        if response.json().get("id"):
-            super(Conversation, self).set_attributes(response.json())
-            return True
-        else:
-            return False
-
-    def add_recipients(self, recipients):
+    def add_recipients(self, recipients, **kwargs):
         """
         Add a recipient to a conversation.
 
@@ -60,30 +44,32 @@ def add_recipients(self, recipients):
             "POST",
             "conversations/{}/add_recipients".format(self.id),
             recipients=recipients,
+            _kwargs=combine_kwargs(**kwargs),
         )
         return Conversation(self._requester, response.json())
 
-    def add_message(self, body, **kwargs):
+    def delete(self, **kwargs):
         """
-        Add a message to a conversation.
+        Delete a conversation.
 
-        :calls: `POST /api/v1/conversations/:id/add_message \
-        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.add_message>`_
+        :calls: `DELETE /api/v1/conversations/:id \
+        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.destroy>`_
 
-        :param body: The body of the conversation.
-        :type body: str
-        :returns: A conversation with only the most recent message.
-        :rtype: :class:`canvasapi.account.Conversation`
+        :rtype: `bool`
         """
         response = self._requester.request(
-            "POST",
-            "conversations/{}/add_message".format(self.id),
-            body=body,
+            "DELETE",
+            "conversations/{}".format(self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-        return Conversation(self._requester, response.json())
 
-    def delete_messages(self, remove):
+        if response.json().get("id"):
+            super(Conversation, self).set_attributes(response.json())
+            return True
+        else:
+            return False
+
+    def delete_messages(self, remove, **kwargs):
         """
         Delete messages from this conversation.
 
@@ -98,6 +84,28 @@ def delete_messages(self, remove):
         :rtype: `dict`
         """
         response = self._requester.request(
-            "POST", "conversations/{}/remove_messages".format(self.id), remove=remove
+            "POST",
+            "conversations/{}/remove_messages".format(self.id),
+            remove=remove,
+            _kwargs=combine_kwargs(**kwargs),
         )
         return response.json()
+
+    def edit(self, **kwargs):
+        """
+        Update a conversation.
+
+        :calls: `PUT /api/v1/conversations/:id \
+        <https://canvas.instructure.com/doc/api/conversations.html#method.conversations.update>`_
+
+        :rtype: `bool`
+        """
+        response = self._requester.request(
+            "PUT", "conversations/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
+        )
+
+        if response.json().get("id"):
+            super(Conversation, self).set_attributes(response.json())
+            return True
+        else:
+            return False
diff --git a/canvasapi/course.py b/canvasapi/course.py
index 04351e0c..0c3e227c 100644
--- a/canvasapi/course.py
+++ b/canvasapi/course.py
@@ -4,10 +4,13 @@
 from canvasapi.blueprint import BlueprintSubscription
 from canvasapi.canvas_object import CanvasObject
 from canvasapi.collaboration import Collaboration
+from canvasapi.content_export import ContentExport
 from canvasapi.course_epub_export import CourseEpubExport
+from canvasapi.course_event import CourseEvent
 from canvasapi.custom_gradebook_columns import CustomGradebookColumn
 from canvasapi.discussion_topic import DiscussionTopic
 from canvasapi.exceptions import RequiredFieldMissing
+from canvasapi.external_feed import ExternalFeed
 from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.folder import Folder
 from canvasapi.grade_change_log import GradeChangeEvent
@@ -20,6 +23,7 @@
 from canvasapi.grading_period import GradingPeriod
 from canvasapi.grading_standard import GradingStandard
 from canvasapi.license import License
+from canvasapi.module import Module
 from canvasapi.outcome_import import OutcomeImport
 from canvasapi.page import Page
 from canvasapi.paginated_list import PaginatedList
@@ -322,8 +326,6 @@ def create_external_feed(self, url, **kwargs):
         :type url: str
         :rtype: :class:`canvasapi.external_feed.ExternalFeed`
         """
-        from canvasapi.external_feed import ExternalFeed
-
         response = self._requester.request(
             "POST",
             "courses/{}/external_feeds".format(self.id),
@@ -435,8 +437,6 @@ def create_module(self, module, **kwargs):
         :returns: The created module.
         :rtype: :class:`canvasapi.module.Module`
         """
-        from canvasapi.module import Module
-
         if isinstance(module, dict) and "name" in module:
             kwargs["module"] = module
         else:
@@ -596,8 +596,6 @@ def delete_external_feed(self, feed, **kwargs):
 
         :rtype: :class:`canvasapi.external_feed.ExternalFeed`
         """
-        from canvasapi.external_feed import ExternalFeed
-
         feed_id = obj_or_id(feed, "feed", (ExternalFeed,))
 
         response = self._requester.request(
@@ -695,8 +693,6 @@ def export_content(self, export_type, **kwargs):
 
         :rtype: :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         kwargs["export_type"] = export_type
 
         response = self._requester.request(
@@ -927,8 +923,6 @@ def get_content_export(self, content_export, **kwargs):
 
         :rtype: :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         export_id = obj_or_id(content_export, "content_export", (ContentExport,))
 
         response = self._requester.request(
@@ -949,8 +943,6 @@ def get_content_exports(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         return PaginatedList(
             ContentExport,
             self._requester,
@@ -1135,18 +1127,16 @@ def get_enabled_features(self, **kwargs):
         :calls: `GET /api/v1/courses/:course_id/features/enabled \
         <https://canvas.instructure.com/doc/api/feature_flags.html#method.feature_flags.enabled_features>`_
 
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.feature.Feature`
+        :rtype: `list` of `str`
         """
-        return PaginatedList(
-            Feature,
-            self._requester,
+        response = self._requester.request(
             "GET",
             "courses/{}/features/enabled".format(self.id),
-            {"course_id": self.id},
             _kwargs=combine_kwargs(**kwargs),
         )
 
+        return response.json()
+
     def get_enrollments(self, **kwargs):
         """
         List all of the enrollments in this course.
@@ -1200,8 +1190,6 @@ def get_external_feeds(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.external_feed.ExternalFeed`
         """
-        from canvasapi.external_feed import ExternalFeed
-
         return PaginatedList(
             ExternalFeed,
             self._requester,
@@ -1653,13 +1641,14 @@ def get_module(self, module, **kwargs):
 
         :rtype: :class:`canvasapi.module.Module`
         """
-        from canvasapi.module import Module
-
         module_id = obj_or_id(module, "module", (Module,))
 
         response = self._requester.request(
-            "GET", "courses/{}/modules/{}".format(self.id, module_id)
+            "GET",
+            "courses/{}/modules/{}".format(self.id, module_id),
+            _kwargs=combine_kwargs(**kwargs),
         )
+
         module_json = response.json()
         module_json.update({"course_id": self.id})
 
@@ -1675,8 +1664,6 @@ def get_modules(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.module.Module`
         """
-        from canvasapi.module import Module
-
         return PaginatedList(
             Module,
             self._requester,
@@ -2382,6 +2369,24 @@ def preview_html(self, html, **kwargs):
         )
         return response.json().get("html", "")
 
+    def query_audit_by_course(self, **kwargs):
+        """
+        Lists course change events for a specific course.
+
+        :calls: `GET /api/v1/audit/course/courses/:course_id \
+        <https://canvas.instructure.com/doc/api/course_audit_log.html#method.course_audit_api.for_course>`_
+
+        :rtype: list of :class:`canvasapi.course_event.CourseEvent`
+        """
+
+        return PaginatedList(
+            CourseEvent,
+            self._requester,
+            "GET",
+            "audit/course/courses/{}".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
     def remove_usage_rights(self, **kwargs):
         """
         Removes the usage rights for specified files that are under the current course scope
diff --git a/canvasapi/course_event.py b/canvasapi/course_event.py
new file mode 100644
index 00000000..344cbd1e
--- /dev/null
+++ b/canvasapi/course_event.py
@@ -0,0 +1,6 @@
+from canvasapi.canvas_object import CanvasObject
+
+
+class CourseEvent(CanvasObject):
+    def __str__(self):
+        return "{} {} ({})".format(self.name, self.start_at, self.conclude_at)
diff --git a/canvasapi/enrollment.py b/canvasapi/enrollment.py
index 476c8f7c..bd4ab84f 100644
--- a/canvasapi/enrollment.py
+++ b/canvasapi/enrollment.py
@@ -6,6 +6,23 @@ class Enrollment(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.type, self.id)
 
+    def accept(self, **kwargs):
+        """
+        Accept a pending course invitation.
+
+        :calls: `POST /api/v1/courses/:course_id/enrollments/:id/accept \
+        <https://canvas.instructure.com/doc/api/enrollments.html#method.enrollments_api.accept>`_
+
+        :returns: True if the course invitation was accepted.
+        :rtype: bool
+        """
+        response = self._requester.request(
+            "POST",
+            "courses/{}/enrollments/{}/accept".format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return response.json().get("success", False)
+
     def deactivate(self, task, **kwargs):
         """
         Delete, conclude, or deactivate an enrollment.
@@ -53,3 +70,20 @@ def reactivate(self, **kwargs):
             _kwargs=combine_kwargs(**kwargs),
         )
         return Enrollment(self._requester, response.json())
+
+    def reject(self, **kwargs):
+        """
+        Reject a pending course invitation.
+
+        :calls: `POST /api/v1/courses/:course_id/enrollments/:id/reject \
+        <https://canvas.instructure.com/doc/api/enrollments.html#method.enrollments_api.reject>`_
+
+        :returns: True if the course invitation was rejected.
+        :rtype: bool
+        """
+        response = self._requester.request(
+            "POST",
+            "courses/{}/enrollments/{}/reject".format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return response.json().get("success", False)
diff --git a/canvasapi/enrollment_term.py b/canvasapi/enrollment_term.py
index a2a4d148..8de88e80 100644
--- a/canvasapi/enrollment_term.py
+++ b/canvasapi/enrollment_term.py
@@ -6,7 +6,7 @@ class EnrollmentTerm(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.name, self.id)
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete this Enrollment Term.
 
@@ -16,7 +16,9 @@ def delete(self):
         :rtype: :class:`canvasapi.enrollment_term.EnrollmentTerm`
         """
         response = self._requester.request(
-            "DELETE", "accounts/{}/terms/{}".format(self.account_id, self.id)
+            "DELETE",
+            "accounts/{}/terms/{}".format(self.account_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
         return EnrollmentTerm(self._requester, response.json())
 
diff --git a/canvasapi/external_tool.py b/canvasapi/external_tool.py
index 41d5616c..9d8afa24 100644
--- a/canvasapi/external_tool.py
+++ b/canvasapi/external_tool.py
@@ -35,25 +35,7 @@ def parent_type(self):
         else:
             raise ValueError("ExternalTool does not have a course_id or account_id")
 
-    def get_parent(self):
-        """
-        Return the object that spawned this tool.
-
-        :rtype: :class:`canvasapi.account.Account` or :class:`canvasapi.account.Course`
-        """
-        from canvasapi.account import Account
-        from canvasapi.course import Course
-
-        response = self._requester.request(
-            "GET", "{}s/{}".format(self.parent_type, self.parent_id)
-        )
-
-        if self.parent_type == "account":
-            return Account(self._requester, response.json())
-        elif self.parent_type == "course":
-            return Course(self._requester, response.json())
-
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Remove the specified external tool.
 
@@ -69,6 +51,7 @@ def delete(self):
             "{}s/{}/external_tools/{}".format(
                 self.parent_type, self.parent_id, self.id
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
 
         return ExternalTool(self._requester, response.json())
@@ -98,6 +81,26 @@ def edit(self, **kwargs):
 
         return ExternalTool(self._requester, response_json)
 
+    def get_parent(self, **kwargs):
+        """
+        Return the object that spawned this tool.
+
+        :rtype: :class:`canvasapi.account.Account` or :class:`canvasapi.account.Course`
+        """
+        from canvasapi.account import Account
+        from canvasapi.course import Course
+
+        response = self._requester.request(
+            "GET",
+            "{}s/{}".format(self.parent_type, self.parent_id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        if self.parent_type == "account":
+            return Account(self._requester, response.json())
+        elif self.parent_type == "course":
+            return Course(self._requester, response.json())
+
     def get_sessionless_launch_url(self, **kwargs):
         """
         Return a sessionless launch url for an external tool.
diff --git a/canvasapi/file.py b/canvasapi/file.py
index 81439f6a..e4acbf36 100644
--- a/canvasapi/file.py
+++ b/canvasapi/file.py
@@ -44,3 +44,17 @@ def get_contents(self, binary=False):
             return response.content
         else:
             return response.text
+
+    def update(self, **kwargs):
+        """
+        Update some settings on the specified file.
+
+        :calls: `PUT /api/v1/files/:id \
+        <https://canvas.instructure.com/doc/api/files.html#method.files.api_update>`_
+
+        :rtype: :class:`canvasapi.file.File`
+        """
+        response = self._requester.request(
+            "PUT", "files/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
+        )
+        return File(self._requester, response.json())
diff --git a/canvasapi/group.py b/canvasapi/group.py
index 1acb6666..b9aaad41 100644
--- a/canvasapi/group.py
+++ b/canvasapi/group.py
@@ -2,6 +2,7 @@
 from canvasapi.collaboration import Collaboration
 from canvasapi.discussion_topic import DiscussionTopic
 from canvasapi.exceptions import RequiredFieldMissing
+from canvasapi.external_feed import ExternalFeed
 from canvasapi.folder import Folder
 from canvasapi.license import License
 from canvasapi.paginated_list import PaginatedList
@@ -78,8 +79,6 @@ def create_external_feed(self, url, **kwargs):
         :type url: str
         :rtype: :class:`canvasapi.external_feed.ExternalFeed`
         """
-        from canvasapi.external_feed import ExternalFeed
-
         response = self._requester.request(
             "POST",
             "groups/{}/external_feeds".format(self.id),
diff --git a/canvasapi/jwt.py b/canvasapi/jwt.py
new file mode 100644
index 00000000..72988fdd
--- /dev/null
+++ b/canvasapi/jwt.py
@@ -0,0 +1,6 @@
+from canvasapi.canvas_object import CanvasObject
+
+
+class JWT(CanvasObject):
+    def __str__(self):
+        return "{}".format(self.token)
diff --git a/canvasapi/login.py b/canvasapi/login.py
index e24f8442..5089e9bd 100644
--- a/canvasapi/login.py
+++ b/canvasapi/login.py
@@ -7,7 +7,7 @@ class Login(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.id, self.unique_id)
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete an existing login.
 
@@ -17,7 +17,9 @@ def delete(self):
         :rtype: :class:`canvasapi.login.Login`
         """
         response = self._requester.request(
-            "DELETE", "users/{}/logins/{}".format(self.user_id, self.id)
+            "DELETE",
+            "users/{}/logins/{}".format(self.user_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
         return Login(self._requester, response.json())
 
diff --git a/canvasapi/module.py b/canvasapi/module.py
index fe6b3b28..4403af4a 100644
--- a/canvasapi/module.py
+++ b/canvasapi/module.py
@@ -8,26 +8,43 @@ class Module(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.name, self.id)
 
-    def edit(self, **kwargs):
+    def create_module_item(self, module_item, **kwargs):
         """
-        Update this module.
+        Create a module item.
 
-        :calls: `PUT /api/v1/courses/:course_id/modules/:id \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_modules_api.update>`_
+        :calls: `POST /api/v1/courses/:course_id/modules/:module_id/items \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.create>`_
 
-        :rtype: :class:`canvasapi.module.Module`
+        :param module_item: The attributes to create the module item with.
+        :type module_item: dict
+        :returns: The created module item.
+        :rtype: :class:`canvasapi.module.ModuleItem`
         """
+
+        unrequired_types = ["ExternalUrl", "Page", "SubHeader"]
+
+        if isinstance(module_item, dict) and "type" in module_item:
+            # content_id is not required for unrequired_types
+            if module_item["type"] in unrequired_types or "content_id" in module_item:
+                kwargs["module_item"] = module_item
+            else:
+                raise RequiredFieldMissing(
+                    "Dictionary with key 'content_id' is required."
+                )
+        else:
+            raise RequiredFieldMissing("Dictionary with key 'type' is required.")
+
         response = self._requester.request(
-            "PUT",
-            "courses/{}/modules/{}".format(self.course_id, self.id),
+            "POST",
+            "courses/{}/modules/{}/items".format(self.course_id, self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-        module_json = response.json()
-        module_json.update({"course_id": self.course_id})
+        module_item_json = response.json()
+        module_item_json.update({"course_id": self.course_id})
 
-        return Module(self._requester, module_json)
+        return ModuleItem(self._requester, module_item_json)
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete this module.
 
@@ -37,53 +54,34 @@ def delete(self):
         :rtype: :class:`canvasapi.module.Module`
         """
         response = self._requester.request(
-            "DELETE", "courses/{}/modules/{}".format(self.course_id, self.id)
+            "DELETE",
+            "courses/{}/modules/{}".format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
         module_json = response.json()
         module_json.update({"course_id": self.course_id})
 
         return Module(self._requester, module_json)
 
-    def relock(self):
+    def edit(self, **kwargs):
         """
-        Reset module progressions to their default locked state and recalculates
-        them based on the current requirements.
-
-        Adding progression requirements to an active course will not lock students
-        out of modules they have already unlocked unless this action is called.
+        Update this module.
 
-        :calls: `PUT /api/v1/courses/:course_id/modules/:id/relock \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_modules_api.relock>`_
+        :calls: `PUT /api/v1/courses/:course_id/modules/:id \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_modules_api.update>`_
 
         :rtype: :class:`canvasapi.module.Module`
         """
         response = self._requester.request(
-            "PUT", "courses/{}/modules/{}/relock".format(self.course_id, self.id)
+            "PUT",
+            "courses/{}/modules/{}".format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
         module_json = response.json()
         module_json.update({"course_id": self.course_id})
 
         return Module(self._requester, module_json)
 
-    def get_module_items(self, **kwargs):
-        """
-        List all of the items in this module.
-
-        :calls: `GET /api/v1/courses/:course_id/modules/:module_id/items \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.index>`_
-
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.module.ModuleItem`
-        """
-        return PaginatedList(
-            ModuleItem,
-            self._requester,
-            "GET",
-            "courses/{}/modules/{}/items".format(self.course_id, self.id),
-            {"course_id": self.course_id},
-            _kwargs=combine_kwargs(**kwargs),
-        )
-
     def get_module_item(self, module_item, **kwargs):
         """
         Retrieve a module item by ID.
@@ -110,60 +108,65 @@ def get_module_item(self, module_item, **kwargs):
 
         return ModuleItem(self._requester, module_item_json)
 
-    def create_module_item(self, module_item, **kwargs):
+    def get_module_items(self, **kwargs):
         """
-        Create a module item.
+        List all of the items in this module.
 
-        :calls: `POST /api/v1/courses/:course_id/modules/:module_id/items \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.create>`_
+        :calls: `GET /api/v1/courses/:course_id/modules/:module_id/items \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.index>`_
 
-        :param module_item: The attributes to create the module item with.
-        :type module_item: dict
-        :returns: The created module item.
-        :rtype: :class:`canvasapi.module.ModuleItem`
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.module.ModuleItem`
         """
+        return PaginatedList(
+            ModuleItem,
+            self._requester,
+            "GET",
+            "courses/{}/modules/{}/items".format(self.course_id, self.id),
+            {"course_id": self.course_id},
+            _kwargs=combine_kwargs(**kwargs),
+        )
 
-        unrequired_types = ["ExternalUrl", "Page", "SubHeader"]
+    def relock(self, **kwargs):
+        """
+        Reset module progressions to their default locked state and recalculates
+        them based on the current requirements.
 
-        if isinstance(module_item, dict) and "type" in module_item:
-            # content_id is not required for unrequired_types
-            if module_item["type"] in unrequired_types or "content_id" in module_item:
-                kwargs["module_item"] = module_item
-            else:
-                raise RequiredFieldMissing(
-                    "Dictionary with key 'content_id' is required."
-                )
-        else:
-            raise RequiredFieldMissing("Dictionary with key 'type' is required.")
+        Adding progression requirements to an active course will not lock students
+        out of modules they have already unlocked unless this action is called.
 
+        :calls: `PUT /api/v1/courses/:course_id/modules/:id/relock \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_modules_api.relock>`_
+
+        :rtype: :class:`canvasapi.module.Module`
+        """
         response = self._requester.request(
-            "POST",
-            "courses/{}/modules/{}/items".format(self.course_id, self.id),
+            "PUT",
+            "courses/{}/modules/{}/relock".format(self.course_id, self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-        module_item_json = response.json()
-        module_item_json.update({"course_id": self.course_id})
+        module_json = response.json()
+        module_json.update({"course_id": self.course_id})
 
-        return ModuleItem(self._requester, module_item_json)
+        return Module(self._requester, module_json)
 
 
 class ModuleItem(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.title, self.id)
 
-    def edit(self, **kwargs):
+    def complete(self, **kwargs):
         """
-        Update this module item.
+        Mark this module item as done.
 
-        :calls: `PUT /api/v1/courses/:course_id/modules/:module_id/items/:id \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.update>`_
+        :calls: `PUT /api/v1/courses/:course_id/modules/:module_id/items/:id/done \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.mark_as_done>`_
 
-        :returns: The updated module item.
         :rtype: :class:`canvasapi.module.ModuleItem`
         """
         response = self._requester.request(
             "PUT",
-            "courses/{}/modules/{}/items/{}".format(
+            "courses/{}/modules/{}/items/{}/done".format(
                 self.course_id, self.module_id, self.id
             ),
             _kwargs=combine_kwargs(**kwargs),
@@ -173,7 +176,7 @@ def edit(self, **kwargs):
 
         return ModuleItem(self._requester, module_item_json)
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete this module item.
 
@@ -187,33 +190,36 @@ def delete(self):
             "courses/{}/modules/{}/items/{}".format(
                 self.course_id, self.module_id, self.id
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
         module_item_json = response.json()
         module_item_json.update({"course_id": self.course_id})
 
         return ModuleItem(self._requester, module_item_json)
 
-    def complete(self):
+    def edit(self, **kwargs):
         """
-        Mark this module item as done.
+        Update this module item.
 
-        :calls: `PUT /api/v1/courses/:course_id/modules/:module_id/items/:id/done \
-        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.mark_as_done>`_
+        :calls: `PUT /api/v1/courses/:course_id/modules/:module_id/items/:id \
+        <https://canvas.instructure.com/doc/api/modules.html#method.context_module_items_api.update>`_
 
+        :returns: The updated module item.
         :rtype: :class:`canvasapi.module.ModuleItem`
         """
         response = self._requester.request(
             "PUT",
-            "courses/{}/modules/{}/items/{}/done".format(
+            "courses/{}/modules/{}/items/{}".format(
                 self.course_id, self.module_id, self.id
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
         module_item_json = response.json()
         module_item_json.update({"course_id": self.course_id})
 
         return ModuleItem(self._requester, module_item_json)
 
-    def uncomplete(self):
+    def uncomplete(self, **kwargs):
         """
         Mark this module item as not done.
 
@@ -227,6 +233,7 @@ def uncomplete(self):
             "courses/{}/modules/{}/items/{}/done".format(
                 self.course_id, self.module_id, self.id
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
         module_item_json = response.json()
         module_item_json.update({"course_id": self.course_id})
diff --git a/canvasapi/outcome.py b/canvasapi/outcome.py
index 9adf2910..4baa9553 100644
--- a/canvasapi/outcome.py
+++ b/canvasapi/outcome.py
@@ -39,7 +39,7 @@ def context_ref(self):
         elif self.context_type == "Account":
             return "accounts/{}".format(self.context_id)
 
-    def get_outcome(self):
+    def get_outcome(self, **kwargs):
         """
         Return the linked outcome
 
@@ -50,11 +50,13 @@ def get_outcome(self):
         :rtype: :class:`canvasapi.outcome.Outcome`
         """
         oid = self.outcome["id"]
-        response = self._requester.request("GET", "outcomes/{}".format(oid))
+        response = self._requester.request(
+            "GET", "outcomes/{}".format(oid), _kwargs=combine_kwargs(**kwargs)
+        )
 
         return Outcome(self._requester, response.json())
 
-    def get_outcome_group(self):
+    def get_outcome_group(self, **kwargs):
         """
         Return the linked outcome group
 
@@ -70,7 +72,9 @@ def get_outcome_group(self):
         """
         ogid = self.outcome_group["id"]
         response = self._requester.request(
-            "GET", "{}/outcome_groups/{}".format(self.context_ref(), ogid)
+            "GET",
+            "{}/outcome_groups/{}".format(self.context_ref(), ogid),
+            _kwargs=combine_kwargs(**kwargs),
         )
 
         return OutcomeGroup(self._requester, response.json())
@@ -88,32 +92,33 @@ def context_ref(self):
         elif self.context_type is None:
             return "global"
 
-    def update(self, **kwargs):
+    def create_subgroup(self, title, **kwargs):
         """
-        Update an outcome group.
+        Create a subgroup of the current group
 
-        :calls: `PUT /api/v1/global/outcome_groups/:id \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
-            or `PUT /api/v1/accounts/:account_id/outcome_groups/:id \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
-            or `PUT /api/v1/courses/:course_id/outcome_groups/:id \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
+        :calls: `POST /api/v1/global/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
+            or `POST /api/v1/accounts/:account_id/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
+            or `POST /api/v1/courses/:course_id/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
 
-        :returns: True if updated, False otherwise.
-        :rtype: bool
+        :param title: The title of the subgroup.
+        :type title: str
+
+        :returns: Itself as an OutcomeGroup object.
+        :rtype: :class:`canvasapi.outcome.OutcomeGroup`
         """
         response = self._requester.request(
-            "PUT",
-            "{}/outcome_groups/{}".format(self.context_ref(), self.id),
+            "POST",
+            "{}/outcome_groups/{}/subgroups".format(self.context_ref(), self.id),
+            title=title,
             _kwargs=combine_kwargs(**kwargs),
         )
 
-        if "id" in response.json():
-            super(OutcomeGroup, self).set_attributes(response.json())
-
-        return "id" in response.json()
+        return OutcomeGroup(self._requester, response.json())
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete an outcome group.
 
@@ -128,7 +133,9 @@ def delete(self):
         :rtype: bool
         """
         response = self._requester.request(
-            "DELETE", "{}/outcome_groups/{}".format(self.context_ref(), self.id)
+            "DELETE",
+            "{}/outcome_groups/{}".format(self.context_ref(), self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
 
         if "id" in response.json():
@@ -159,7 +166,61 @@ def get_linked_outcomes(self, **kwargs):
             _kwargs=combine_kwargs(**kwargs),
         )
 
-    def link_existing(self, outcome):
+    def get_subgroups(self, **kwargs):
+        """
+        List subgroups.
+
+        :calls: `GET /api/v1/global/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
+            or `GET /api/v1/accounts/:account_id/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
+            or `GET /api/v1/courses/:course_id/outcome_groups/:id/subgroups \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
+
+        :returns: Paginated List of OutcomeGroups linked to the current group.
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.outcome.OutcomeGroup`
+        """
+        return PaginatedList(
+            OutcomeGroup,
+            self._requester,
+            "GET",
+            "{}/outcome_groups/{}/subgroups".format(self.context_ref(), self.id),
+            {"context_type": self.context_type, "context_id": self.context_id},
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+    def import_outcome_group(self, outcome_group, **kwargs):
+        """
+        Import an outcome group as a subgroup into the current outcome group
+
+        :calls: `POST /api/v1/global/outcome_groups/:id/import \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
+            or `POST /api/v1/accounts/:account_id/outcome_groups/:id/import \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
+            or `POST /api/v1/courses/:course_id/outcome_groups/:id/import \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
+
+        :param outcome: The object or ID of the outcome group to import.
+        :type outcome: :class:`canvasapi.outcome.OutcomeGroup` or int
+
+        :returns: Itself as an OutcomeGroup object.
+        :rtype: :class:`canvasapi.outcome.OutcomeGroup`
+        """
+        source_outcome_group_id = obj_or_id(
+            outcome_group, "outcome_group", (OutcomeGroup,)
+        )
+
+        response = self._requester.request(
+            "POST",
+            "{}/outcome_groups/{}/import".format(self.context_ref(), self.id),
+            source_outcome_group_id=source_outcome_group_id,
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return OutcomeGroup(self._requester, response.json())
+
+    def link_existing(self, outcome, **kwargs):
         """
         Link to an existing Outcome.
 
@@ -181,8 +242,11 @@ def link_existing(self, outcome):
         response = self._requester.request(
             "PUT",
             "{}/outcome_groups/{}/outcomes/{}".format(
-                self.context_ref(), self.id, outcome_id
+                self.context_ref(),
+                self.id,
+                outcome_id,
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
 
         return OutcomeLink(self._requester, response.json())
@@ -213,7 +277,7 @@ def link_new(self, title, **kwargs):
 
         return OutcomeLink(self._requester, response.json())
 
-    def unlink_outcome(self, outcome):
+    def unlink_outcome(self, outcome, **kwargs):
         """
         Remove an Outcome from and OutcomeLink
 
@@ -235,8 +299,11 @@ def unlink_outcome(self, outcome):
         response = self._requester.request(
             "DELETE",
             "{}/outcome_groups/{}/outcomes/{}".format(
-                self.context_ref(), self.id, outcome_id
+                self.context_ref(),
+                self.id,
+                outcome_id,
             ),
+            _kwargs=combine_kwargs(**kwargs),
         )
 
         if "context_id" in response.json():
@@ -244,84 +311,30 @@ def unlink_outcome(self, outcome):
 
         return "context_id" in response.json()
 
-    def get_subgroups(self, **kwargs):
-        """
-        List subgroups.
-
-        :calls: `GET /api/v1/global/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
-            or `GET /api/v1/accounts/:account_id/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
-            or `GET /api/v1/courses/:course_id/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.subgroups>`_
-
-        :returns: Paginated List of OutcomeGroups linked to the current group.
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.outcome.OutcomeGroup`
-        """
-        return PaginatedList(
-            OutcomeGroup,
-            self._requester,
-            "GET",
-            "{}/outcome_groups/{}/subgroups".format(self.context_ref(), self.id),
-            {"context_type": self.context_type, "context_id": self.context_id},
-            _kwargs=combine_kwargs(**kwargs),
-        )
-
-    def create_subgroup(self, title, **kwargs):
+    def update(self, **kwargs):
         """
-        Create a subgroup of the current group
-
-        :calls: `POST /api/v1/global/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
-            or `POST /api/v1/accounts/:account_id/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
-            or `POST /api/v1/courses/:course_id/outcome_groups/:id/subgroups \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.create>`_
+        Update an outcome group.
 
-        :param title: The title of the subgroup.
-        :type title: str
+        :calls: `PUT /api/v1/global/outcome_groups/:id \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
+            or `PUT /api/v1/accounts/:account_id/outcome_groups/:id \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
+            or `PUT /api/v1/courses/:course_id/outcome_groups/:id \
+            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.update>`_
 
-        :returns: Itself as an OutcomeGroup object.
-        :rtype: :class:`canvasapi.outcome.OutcomeGroup`
+        :returns: True if updated, False otherwise.
+        :rtype: bool
         """
         response = self._requester.request(
-            "POST",
-            "{}/outcome_groups/{}/subgroups".format(self.context_ref(), self.id),
-            title=title,
+            "PUT",
+            "{}/outcome_groups/{}".format(self.context_ref(), self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
 
-        return OutcomeGroup(self._requester, response.json())
-
-    def import_outcome_group(self, outcome_group):
-        """
-        Import an outcome group as a subgroup into the current outcome group
-
-        :calls: `POST /api/v1/global/outcome_groups/:id/import \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
-            or `POST /api/v1/accounts/:account_id/outcome_groups/:id/import \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
-            or `POST /api/v1/courses/:course_id/outcome_groups/:id/import \
-            <https://canvas.instructure.com/doc/api/outcome_groups.html#method.outcome_groups_api.import>`_
-
-        :param outcome: The object or ID of the outcome group to import.
-        :type outcome: :class:`canvasapi.outcome.OutcomeGroup` or int
-
-        :returns: Itself as an OutcomeGroup object.
-        :rtype: :class:`canvasapi.outcome.OutcomeGroup`
-        """
-        source_outcome_group_id = obj_or_id(
-            outcome_group, "outcome_group", (OutcomeGroup,)
-        )
-
-        response = self._requester.request(
-            "POST",
-            "{}/outcome_groups/{}/import".format(self.context_ref(), self.id),
-            source_outcome_group_id=source_outcome_group_id,
-        )
+        if "id" in response.json():
+            super(OutcomeGroup, self).set_attributes(response.json())
 
-        return OutcomeGroup(self._requester, response.json())
+        return "id" in response.json()
 
 
 class OutcomeResult(CanvasObject):
diff --git a/canvasapi/paginated_list.py b/canvasapi/paginated_list.py
index 9be9fc3e..ce14420d 100644
--- a/canvasapi/paginated_list.py
+++ b/canvasapi/paginated_list.py
@@ -27,7 +27,6 @@ def __init__(
         _root=None,
         **kwargs
     ):
-
         self._elements = list()
 
         self._requester = requester
diff --git a/canvasapi/planner.py b/canvasapi/planner.py
index 32946afe..a4b688e7 100644
--- a/canvasapi/planner.py
+++ b/canvasapi/planner.py
@@ -6,36 +6,36 @@ class PlannerNote(CanvasObject):
     def __str__(self):
         return "{} {} ({})".format(self.title, self.todo_date, self.id)
 
-    def update(self, **kwargs):
+    def delete(self, **kwargs):
         """
-        Update a planner note for the current user
+        Delete a planner note for the current user
 
-        :calls: `PUT /api/v1/planner_notes/:id \
-        <https://canvas.instructure.com/doc/api/planner.html#method.planner_notes.update>`_
+        :calls: `DELETE /api/v1/planner_notes/:id \
+        <https://canvas.instructure.com/doc/api/planner.html#method.planner_notes.destroy>`_
 
         :rtype: :class:`canvasapi.planner.PlannerNote`
         """
-
         response = self._requester.request(
-            "PUT", "planner_notes/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
+            "DELETE",
+            "planner_notes/{}".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
+
         return PlannerNote(self._requester, response.json())
 
-    def delete(self, **kwargs):
+    def update(self, **kwargs):
         """
-        Delete a planner note for the current user
+        Update a planner note for the current user
 
-        :calls: `DELETE /api/v1/planner_notes/:id \
-        <https://canvas.instructure.com/doc/api/planner.html#method.planner_notes.destroy>`_
+        :calls: `PUT /api/v1/planner_notes/:id \
+        <https://canvas.instructure.com/doc/api/planner.html#method.planner_notes.update>`_
 
         :rtype: :class:`canvasapi.planner.PlannerNote`
         """
+
         response = self._requester.request(
-            "DELETE",
-            "planner_notes/{}".format(self.id),
-            _kwargs=combine_kwargs(**kwargs),
+            "PUT", "planner_notes/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
         )
-
         return PlannerNote(self._requester, response.json())
 
 
@@ -43,36 +43,36 @@ class PlannerOverride(CanvasObject):
     def __str__(self):
         return "{} {} ({})".format(self.plannable_id, self.marked_complete, self.id)
 
-    def update(self, **kwargs):
+    def delete(self, **kwargs):
         """
-        Update a planner override's visibilty for the current user
+        Delete a planner override for the current user
 
-        :calls: `PUT /api/v1/planner/overrides/:id \
-        <https://canvas.instructure.com/doc/api/planner.html#method.planner_overrides.update>`_
+        :calls: `DELETE /api/v1/planner/overrides/:id \
+        <https://canvas.instructure.com/doc/api/planner.html#method.planner_overrides.destroy>`_
 
         :rtype: :class:`canvasapi.planner.PlannerOverride`
         """
-
         response = self._requester.request(
-            "PUT",
+            "DELETE",
             "planner/overrides/{}".format(self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
+
         return PlannerOverride(self._requester, response.json())
 
-    def delete(self, **kwargs):
+    def update(self, **kwargs):
         """
-        Delete a planner override for the current user
+        Update a planner override's visibilty for the current user
 
-        :calls: `DELETE /api/v1/planner/overrides/:id \
-        <https://canvas.instructure.com/doc/api/planner.html#method.planner_overrides.destroy>`_
+        :calls: `PUT /api/v1/planner/overrides/:id \
+        <https://canvas.instructure.com/doc/api/planner.html#method.planner_overrides.update>`_
 
         :rtype: :class:`canvasapi.planner.PlannerOverride`
         """
+
         response = self._requester.request(
-            "DELETE",
+            "PUT",
             "planner/overrides/{}".format(self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-
         return PlannerOverride(self._requester, response.json())
diff --git a/canvasapi/poll.py b/canvasapi/poll.py
index da365741..622ef3fe 100644
--- a/canvasapi/poll.py
+++ b/canvasapi/poll.py
@@ -10,30 +10,62 @@ class Poll(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.question, self.id)
 
-    def update(self, poll, **kwargs):
+    def create_choice(self, poll_choice, **kwargs):
         """
-        Update an existing poll belonging to the current user.
+        Create a new choice for the current poll.
 
-        :calls: `PUT /api/v1/polls/:id \
-        <https://canvas.instructure.com/doc/api/polls.html#method.polling/polls.update>`_
+        :calls: `POST /api/v1/polls/:poll_id/poll_choices \
+        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.create>`_
 
-        :param poll: List of arguments. 'Question' is required and 'Description' is optional
-        :type poll: list
-        :rtype: :class:`canvasapi.poll.Poll`
+        :param poll_choice: 'text' is required, 'is_correct' and 'position' are optional.
+        :type poll_choice: list
+        :rtype: :class:`canvasapi.poll_choice.PollChoice`
         """
         if (
-            isinstance(poll, list)
-            and isinstance(poll[0], dict)
-            and "question" in poll[0]
+            isinstance(poll_choice, list)
+            and isinstance(poll_choice[0], dict)
+            and "text" in poll_choice[0]
         ):
-            kwargs["poll"] = poll
+            kwargs["poll_choice"] = poll_choice
         else:
-            raise RequiredFieldMissing("Dictionary with key 'question' is required.")
+            raise RequiredFieldMissing("Dictionary with key 'text' is required.")
 
         response = self._requester.request(
-            "PUT", "polls/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
+            "POST",
+            "polls/{}/poll_choices".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
         )
-        return Poll(self._requester, response.json()["polls"][0])
+        return PollChoice(self._requester, response.json()["poll_choices"][0])
+
+    def create_session(self, poll_session, **kwargs):
+        """
+        Create a new poll session for this poll
+
+        :calls: `POST /api/v1/polls/:poll_id/poll_sessions \
+        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.create>`_
+
+        :param poll_session: List of arguments. course_id (required): id of the course for the
+            session, course_section_id (optional): id of the course section for this session,
+            has_public_results (optional): whether the results are viewable by students.
+        :type poll_session: list
+
+        :rtype: :class:`canvasapi.poll_session.PollSession`
+        """
+        if (
+            isinstance(poll_session, list)
+            and isinstance(poll_session[0], dict)
+            and "course_id" in poll_session[0]
+        ):
+            kwargs["poll_session"] = poll_session
+        else:
+            raise RequiredFieldMissing("Dictionary with key 'course_id' is required.")
+
+        response = self._requester.request(
+            "POST",
+            "polls/{}/poll_sessions".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return PollSession(self._requester, response.json()["poll_sessions"][0])
 
     def delete(self, **kwargs):
         """
@@ -51,25 +83,6 @@ def delete(self, **kwargs):
         )
         return response.status_code == 204
 
-    def get_choices(self, **kwargs):
-        """
-        Returns a paginated list of PollChoices of a poll, based on poll id.
-
-        :calls: `GET /api/v1/polls/:poll_id/poll_choices \
-        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.index>`_
-
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.poll_choice.PollChoice`
-        """
-        return PaginatedList(
-            PollChoice,
-            self._requester,
-            "GET",
-            "polls/{}/poll_choices".format(self.id),
-            _root="poll_choices",
-            _kwargs=combine_kwargs(**kwargs),
-        )
-
     def get_choice(self, poll_choice, **kwargs):
         """
         Returns the poll choice with the given id.
@@ -88,49 +101,22 @@ def get_choice(self, poll_choice, **kwargs):
         )
         return PollChoice(self._requester, response.json()["poll_choices"][0])
 
-    def create_choice(self, poll_choice, **kwargs):
-        """
-        Create a new choice for the current poll.
-
-        :calls: `POST /api/v1/polls/:poll_id/poll_choices \
-        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.create>`_
-
-        :param poll_choice: 'text' is required, 'is_correct' and 'position' are optional.
-        :type poll_choice: list
-        :rtype: :class:`canvasapi.poll_choice.PollChoice`
-        """
-        if (
-            isinstance(poll_choice, list)
-            and isinstance(poll_choice[0], dict)
-            and "text" in poll_choice[0]
-        ):
-            kwargs["poll_choice"] = poll_choice
-        else:
-            raise RequiredFieldMissing("Dictionary with key 'text' is required.")
-
-        response = self._requester.request(
-            "POST",
-            "polls/{}/poll_choices".format(self.id),
-            _kwargs=combine_kwargs(**kwargs),
-        )
-        return PollChoice(self._requester, response.json()["poll_choices"][0])
-
-    def get_sessions(self, **kwargs):
+    def get_choices(self, **kwargs):
         """
-        Returns the paginated list of PollSessions in a poll.
+        Returns a paginated list of PollChoices of a poll, based on poll id.
 
-        :calls: `GET /api/v1/polls/:poll_id/poll_sessions \
-        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.index>`_
+        :calls: `GET /api/v1/polls/:poll_id/poll_choices \
+        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.index>`_
 
-        :rtype: :class:`canvasapi.paginated_lsit.Paginated List` of
-            :class:`canvasapi.poll_session.PollSession`
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.poll_choice.PollChoice`
         """
         return PaginatedList(
-            PollSession,
+            PollChoice,
             self._requester,
             "GET",
-            "polls/{}/poll_sessions".format(self.id),
-            _root="poll_sessions",
+            "polls/{}/poll_choices".format(self.id),
+            _root="poll_choices",
             _kwargs=combine_kwargs(**kwargs),
         )
 
@@ -155,32 +141,46 @@ def get_session(self, poll_session, **kwargs):
         )
         return PollSession(self._requester, response.json()["poll_sessions"][0])
 
-    def create_session(self, poll_session, **kwargs):
+    def get_sessions(self, **kwargs):
         """
-        Create a new poll session for this poll
+        Returns the paginated list of PollSessions in a poll.
 
-        :calls: `POST /api/v1/polls/:poll_id/poll_sessions \
-        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.create>`_
+        :calls: `GET /api/v1/polls/:poll_id/poll_sessions \
+        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.index>`_
 
-        :param poll_session: List of arguments. course_id (required): id of the course for the
-            session, course_section_id (optional): id of the course section for this session,
-            has_public_results (optional): whether the results are viewable by students.
-        :type poll_session: list
+        :rtype: :class:`canvasapi.paginated_lsit.Paginated List` of
+            :class:`canvasapi.poll_session.PollSession`
+        """
+        return PaginatedList(
+            PollSession,
+            self._requester,
+            "GET",
+            "polls/{}/poll_sessions".format(self.id),
+            _root="poll_sessions",
+            _kwargs=combine_kwargs(**kwargs),
+        )
 
-        :rtype: :class:`canvasapi.poll_session.PollSession`
+    def update(self, poll, **kwargs):
+        """
+        Update an existing poll belonging to the current user.
+
+        :calls: `PUT /api/v1/polls/:id \
+        <https://canvas.instructure.com/doc/api/polls.html#method.polling/polls.update>`_
+
+        :param poll: List of arguments. 'Question' is required and 'Description' is optional
+        :type poll: list
+        :rtype: :class:`canvasapi.poll.Poll`
         """
         if (
-            isinstance(poll_session, list)
-            and isinstance(poll_session[0], dict)
-            and "course_id" in poll_session[0]
+            isinstance(poll, list)
+            and isinstance(poll[0], dict)
+            and "question" in poll[0]
         ):
-            kwargs["poll_session"] = poll_session
+            kwargs["poll"] = poll
         else:
-            raise RequiredFieldMissing("Dictionary with key 'course_id' is required.")
+            raise RequiredFieldMissing("Dictionary with key 'question' is required.")
 
         response = self._requester.request(
-            "POST",
-            "polls/{}/poll_sessions".format(self.id),
-            _kwargs=combine_kwargs(**kwargs),
+            "PUT", "polls/{}".format(self.id), _kwargs=combine_kwargs(**kwargs)
         )
-        return PollSession(self._requester, response.json()["poll_sessions"][0])
+        return Poll(self._requester, response.json()["polls"][0])
diff --git a/canvasapi/poll_choice.py b/canvasapi/poll_choice.py
index ccf64df0..90985d13 100644
--- a/canvasapi/poll_choice.py
+++ b/canvasapi/poll_choice.py
@@ -7,6 +7,24 @@ class PollChoice(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.text, self.id)
 
+    def delete(self, **kwargs):
+        """
+        Delete a single poll, based on the poll id.
+
+        :calls: `DELETE /api/v1/polls/:poll_id/poll_choices/:id \
+        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.destroy>`_
+
+        :returns: True if the deletion was successful, false otherwise.
+
+        :rtype: bool
+        """
+        response = self._requester.request(
+            "DELETE",
+            "polls/{}/poll_choices/{}".format(self.poll_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return response.status_code == 204
+
     def update(self, poll_choice, **kwargs):
         """
         Update an existing choice for this poll.
@@ -34,21 +52,3 @@ def update(self, poll_choice, **kwargs):
             _kwargs=combine_kwargs(**kwargs),
         )
         return PollChoice(self._requester, response.json()["poll_choices"][0])
-
-    def delete(self, **kwargs):
-        """
-        Delete a single poll, based on the poll id.
-
-        :calls: `DELETE /api/v1/polls/:poll_id/poll_choices/:id \
-        <https://canvas.instructure.com/doc/api/poll_choices.html#method.polling/poll_choices.destroy>`_
-
-        :returns: True if the deletion was successful, false otherwise.
-
-        :rtype: bool
-        """
-        response = self._requester.request(
-            "DELETE",
-            "polls/{}/poll_choices/{}".format(self.poll_id, self.id),
-            _kwargs=combine_kwargs(**kwargs),
-        )
-        return response.status_code == 204
diff --git a/canvasapi/poll_session.py b/canvasapi/poll_session.py
index e03b435b..718497fd 100644
--- a/canvasapi/poll_session.py
+++ b/canvasapi/poll_session.py
@@ -8,35 +8,52 @@ class PollSession(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.poll_id, self.id)
 
-    def update(self, poll_session, **kwargs):
+    def close(self, **kwargs):
         """
-        Update an existing poll session for a poll based on poll id.
+        Close a poll session to answers based on the poll id.
 
-        :calls: `PUT /api/v1/polls/:poll_id/poll_sessions/:id \
-        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.update>`_
+        :calls: `GET /api/v1/polls/:poll_id/poll_sessions/:id/close \
+        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.close>`_
 
-        :param poll_session: List of arguments. course_id (required): id of the course for the
-            session, course_section_id (optional): id of the course section for this session,
-            has_public_results (optional): whether the results are viewable by students.
-        :type poll_session: list
+        :returns: :class:`canvasapi.poll_session.PollSession`
+        """
+        response = self._requester.request(
+            "GET",
+            "polls/{}/poll_sessions/{}/close".format(self.poll_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return PollSession(self._requester, response.json()["poll_sessions"][0])
 
-        :rtype: :class:`canvasapi.poll_session.PollSession`
+    def create_submission(self, poll_submissions, **kwargs):
+        """
+        Create a new poll submission for this poll session.
+
+        :calls: `POST /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions \
+        <https://canvas.instructure.com/doc/api/poll_submissions.html#method.polling/poll_submissions.create>`_
+
+        :param poll_submissions: List of arguments. poll_choice_id (required int): Chosen poll \
+        choice for this submission.
+        :type poll_submissions: list
+
+        :rtype: :class:`canvasapi.poll_submission.PollSubmission`
         """
         if (
-            isinstance(poll_session, list)
-            and isinstance(poll_session[0], dict)
-            and "course_id" in poll_session[0]
+            isinstance(poll_submissions, list)
+            and isinstance(poll_submissions[0], dict)
+            and "poll_choice_id" in poll_submissions[0]
         ):
-            kwargs["poll_session"] = poll_session
+            kwargs["poll_submissions"] = poll_submissions
         else:
-            raise RequiredFieldMissing("Dictionary with key 'course_id' is required.")
+            raise RequiredFieldMissing(
+                "Dictionary with key 'poll_choice_id is required."
+            )
 
         response = self._requester.request(
-            "PUT",
-            "polls/{}/poll_sessions/{}".format(self.poll_id, self.id),
+            "POST",
+            "polls/{}/poll_sessions/{}/poll_submissions".format(self.poll_id, self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-        return PollSession(self._requester, response.json()["poll_sessions"][0])
+        return PollSubmission(self._requester, response.json()["poll_submissions"][0])
 
     def delete(self, **kwargs):
         """
@@ -56,38 +73,6 @@ def delete(self, **kwargs):
         )
         return response.status_code == 204
 
-    def open(self, **kwargs):
-        """
-        Open a poll session to answers based on the poll id.
-
-        :calls: `GET /api/v1/polls/:poll_id/poll_sessions/:id/open \
-        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.open>`_
-
-        :returns: :class:`canvasapi.poll_session.PollSession`
-        """
-        response = self._requester.request(
-            "GET",
-            "polls/{}/poll_sessions/{}/open".format(self.poll_id, self.id),
-            _kwargs=combine_kwargs(**kwargs),
-        )
-        return PollSession(self._requester, response.json()["poll_sessions"][0])
-
-    def close(self, **kwargs):
-        """
-        Close a poll session to answers based on the poll id.
-
-        :calls: `GET /api/v1/polls/:poll_id/poll_sessions/:id/close \
-        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.close>`_
-
-        :returns: :class:`canvasapi.poll_session.PollSession`
-        """
-        response = self._requester.request(
-            "GET",
-            "polls/{}/poll_sessions/{}/close".format(self.poll_id, self.id),
-            _kwargs=combine_kwargs(**kwargs),
-        )
-        return PollSession(self._requester, response.json()["poll_sessions"][0])
-
     def get_submission(self, poll_submission, **kwargs):
         """
         Returns the poll submission with the given id.
@@ -113,33 +98,48 @@ def get_submission(self, poll_submission, **kwargs):
         )
         return PollSubmission(self._requester, response.json()["poll_submissions"][0])
 
-    def create_submission(self, poll_submissions, **kwargs):
+    def open(self, **kwargs):
         """
-        Create a new poll submission for this poll session.
+        Open a poll session to answers based on the poll id.
 
-        :calls: `POST /api/v1/polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions \
-        <https://canvas.instructure.com/doc/api/poll_submissions.html#method.polling/poll_submissions.create>`_
+        :calls: `GET /api/v1/polls/:poll_id/poll_sessions/:id/open \
+        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.open>`_
 
-        :param poll_submissions: List of arguments. poll_choice_id (required int): Chosen poll \
-        choice for this submission.
-        :type poll_submissions: list
+        :returns: :class:`canvasapi.poll_session.PollSession`
+        """
+        response = self._requester.request(
+            "GET",
+            "polls/{}/poll_sessions/{}/open".format(self.poll_id, self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return PollSession(self._requester, response.json()["poll_sessions"][0])
 
-        :rtype: :class:`canvasapi.poll_submission.PollSubmission`
+    def update(self, poll_session, **kwargs):
+        """
+        Update an existing poll session for a poll based on poll id.
+
+        :calls: `PUT /api/v1/polls/:poll_id/poll_sessions/:id \
+        <https://canvas.instructure.com/doc/api/poll_sessions.html#method.polling/poll_sessions.update>`_
+
+        :param poll_session: List of arguments. course_id (required): id of the course for the
+            session, course_section_id (optional): id of the course section for this session,
+            has_public_results (optional): whether the results are viewable by students.
+        :type poll_session: list
+
+        :rtype: :class:`canvasapi.poll_session.PollSession`
         """
         if (
-            isinstance(poll_submissions, list)
-            and isinstance(poll_submissions[0], dict)
-            and "poll_choice_id" in poll_submissions[0]
+            isinstance(poll_session, list)
+            and isinstance(poll_session[0], dict)
+            and "course_id" in poll_session[0]
         ):
-            kwargs["poll_submissions"] = poll_submissions
+            kwargs["poll_session"] = poll_session
         else:
-            raise RequiredFieldMissing(
-                "Dictionary with key 'poll_choice_id is required."
-            )
+            raise RequiredFieldMissing("Dictionary with key 'course_id' is required.")
 
         response = self._requester.request(
-            "POST",
-            "polls/{}/poll_sessions/{}/poll_submissions".format(self.poll_id, self.id),
+            "PUT",
+            "polls/{}/poll_sessions/{}".format(self.poll_id, self.id),
             _kwargs=combine_kwargs(**kwargs),
         )
-        return PollSubmission(self._requester, response.json()["poll_submissions"][0])
+        return PollSession(self._requester, response.json()["poll_sessions"][0])
diff --git a/canvasapi/rubric.py b/canvasapi/rubric.py
index 5ee003fe..e566cf4e 100644
--- a/canvasapi/rubric.py
+++ b/canvasapi/rubric.py
@@ -26,10 +26,84 @@ def delete(self, **kwargs):
         return Rubric(self._requester, response.json())
 
 
+class RubricAssessment(CanvasObject):
+    def __str__(self):
+        return "{}, {}".format(self.id, self.artifact_type)
+
+    def delete(self, **kwargs):
+        """
+        Delete a single RubricAssessment.
+
+        :calls: `DELETE /api/v1/courses/:course_id/rubric_associations\
+        /:rubric_association_id/rubric_assessments/:id \
+        <https://canvas.instructure.com/doc/api/rubrics.html#method.rubric_assessments.destroy>`_
+
+        :rtype: :class:`canvasapi.rubric.RubricAssessment`
+        """
+        from canvasapi.rubric import RubricAssessment
+
+        response = self._requester.request(
+            "DELETE",
+            "courses/{}/rubric_associations/{}/rubric_assessments/{}".format(
+                self.course_id, self.rubric_association_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return RubricAssessment(self._requester, response.json())
+
+    def update(self, **kwargs):
+        """
+        Update a single RubricAssessment.
+
+        :calls: `PUT /api/v1/courses/:course_id/rubric_associations\
+        /:rubric_association_id/rubric_assessments/:id \
+        <https://canvas.instructure.com/doc/api/rubrics.html#method.rubric_assessments.update>`_
+
+        :rtype: :class:`canvasapi.rubric.RubricAssessment`
+        """
+        from canvasapi.rubric import RubricAssessment
+
+        response = self._requester.request(
+            "PUT",
+            "courses/{}/rubric_associations/{}/rubric_assessments/{}".format(
+                self.course_id, self.rubric_association_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return RubricAssessment(self._requester, response.json())
+
+
 class RubricAssociation(CanvasObject):
     def __str__(self):
         return "{}, {}".format(self.id, self.association_type)
 
+    def create_rubric_assessment(self, **kwargs):
+        """
+        Create a single RubricAssessment.
+
+        :calls: `POST /api/v1/courses/:course_id/rubric_associations\
+        /:rubric_association_id/rubric_assessments \
+        <https://canvas.instructure.com/doc/api/rubrics.html#method.rubric_assessments.create>`_
+
+        :rtype: :class:`canvasapi.rubric.RubricAssessment`
+        """
+        from canvasapi.rubric import RubricAssessment
+
+        response = self._requester.request(
+            "POST",
+            "courses/{}/rubric_associations/{}/rubric_assessments".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        assessment_json = response.json()
+        assessment_json.update({"course_id": self.id})
+
+        return RubricAssessment(self._requester, assessment_json)
+
     def delete(self, **kwargs):
         """
         Delete a RubricAssociation.
diff --git a/canvasapi/user.py b/canvasapi/user.py
index 3faafac4..6f9adabf 100644
--- a/canvasapi/user.py
+++ b/canvasapi/user.py
@@ -1,10 +1,15 @@
+from canvasapi.authentication_event import AuthenticationEvent
+from canvasapi.avatar import Avatar
 from canvasapi.calendar_event import CalendarEvent
 from canvasapi.canvas_object import CanvasObject
 from canvasapi.communication_channel import CommunicationChannel
+from canvasapi.content_export import ContentExport
+from canvasapi.content_migration import ContentMigration, Migrator
 from canvasapi.feature import Feature, FeatureFlag
 from canvasapi.folder import Folder
 from canvasapi.grade_change_log import GradeChangeEvent
 from canvasapi.license import License
+from canvasapi.page_view import PageView
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.pairing_code import PairingCode
 from canvasapi.upload import FileOrPathLike, Uploader
@@ -81,8 +86,6 @@ def create_content_migration(self, migration_type, **kwargs):
 
         :rtype: :class:`canvasapi.content_migration.ContentMigration`
         """
-        from canvasapi.content_migration import ContentMigration, Migrator
-
         if isinstance(migration_type, Migrator):
             kwargs["migration_type"] = migration_type.type
         elif isinstance(migration_type, str):
@@ -165,8 +168,6 @@ def export_content(self, export_type, **kwargs):
 
         :rtype: :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         kwargs["export_type"] = export_type
 
         response = self._requester.request(
@@ -213,8 +214,6 @@ def get_authentication_events(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
                 :class:`canvasapi.authentication_event.AuthenticationEvent`
         """
-        from canvasapi.authentication_event import AuthenticationEvent
-
         return PaginatedList(
             AuthenticationEvent,
             self._requester,
@@ -233,8 +232,6 @@ def get_avatars(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.avatar.Avatar`
         """
-        from canvasapi.avatar import Avatar
-
         return PaginatedList(
             Avatar,
             self._requester,
@@ -349,8 +346,6 @@ def get_content_export(self, content_export, **kwargs):
 
         :rtype: :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         export_id = obj_or_id(content_export, "content_export", (ContentExport,))
 
         response = self._requester.request(
@@ -371,8 +366,6 @@ def get_content_exports(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.content_export.ContentExport`
         """
-        from canvasapi.content_export import ContentExport
-
         return PaginatedList(
             ContentExport,
             self._requester,
@@ -458,18 +451,16 @@ def get_enabled_features(self, **kwargs):
         :calls: `GET /api/v1/users/:user_id/features/enabled \
         <https://canvas.instructure.com/doc/api/feature_flags.html#method.feature_flags.enabled_features>`_
 
-        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.feature.Feature`
+        :rtype: `list` of `str`
         """
-        return PaginatedList(
-            Feature,
-            self._requester,
+        response = self._requester.request(
             "GET",
             "users/{}/features/enabled".format(self.id),
-            {"user_id": self.id},
             _kwargs=combine_kwargs(**kwargs),
         )
 
+        return response.json()
+
     def get_enrollments(self, **kwargs):
         """
         List all of the enrollments for this user.
@@ -821,8 +812,6 @@ def get_page_views(self, **kwargs):
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
             :class:`canvasapi.course.PageView`
         """
-        from canvasapi.page_view import PageView
-
         return PaginatedList(
             PageView,
             self._requester,
@@ -1019,6 +1008,26 @@ def show_observee(self, observee_id, **kwargs):
         )
         return User(self._requester, response.json())
 
+    def terminate_sessions(self, **kwargs):
+        """
+        Terminate all sessions for a user.
+
+        This includes all browser-based sessions and all access tokens,
+        including manually generated ones.
+
+        :calls: `DELETE /api/v1/users/:id/sessions \
+        <https://canvas.instructure.com/doc/api/users.html#method.users.terminate_sessions>`_
+
+        :rtype: str
+        """
+
+        response = self._requester.request(
+            "DELETE",
+            "users/{}/sessions".format(self.id),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return response.json()
+
     def update_color(self, asset_string, hexcode, **kwargs):
         """
         Update a custom color for this user for a given context.
diff --git a/docs/account-calendar-ref.rst b/docs/account-calendar-ref.rst
new file mode 100644
index 00000000..4e213f49
--- /dev/null
+++ b/docs/account-calendar-ref.rst
@@ -0,0 +1,6 @@
+===============
+AccountCalendar
+===============
+
+.. autoclass:: canvasapi.account_calendar.AccountCalendar
+    :members:
diff --git a/docs/assignment-ref.rst b/docs/assignment-ref.rst
index dd1e5eac..bd8f21d8 100644
--- a/docs/assignment-ref.rst
+++ b/docs/assignment-ref.rst
@@ -15,4 +15,10 @@ AssignmentExtension
 AssignmentGroup
 ===============
 .. autoclass:: canvasapi.assignment.AssignmentGroup
-    :members:
\ No newline at end of file
+    :members:
+
+==================
+AssignmentOverride
+==================
+.. autoclass:: canvasapi.assignment.AssignmentOverride
+    :members:
diff --git a/docs/class-reference.rst b/docs/class-reference.rst
index 21c0f2c7..7420abff 100644
--- a/docs/class-reference.rst
+++ b/docs/class-reference.rst
@@ -5,6 +5,7 @@ Class Reference
 
     canvas-ref
     account-ref
+    account-calendar-ref
     appointment-group-ref
     assignment-ref
     authentication-event-ref
@@ -21,6 +22,7 @@ Class Reference
     conversation-ref
     course-ref
     course-epub-export-ref
+    course-event-ref
     current-user-ref
     custom-gradebook-columns-ref
     discussion-entry-ref
@@ -36,6 +38,7 @@ Class Reference
     grading-period-ref
     grade-change-log-ref
     group-ref
+    jwt-ref
     login-ref
     license-ref
     module-ref
diff --git a/docs/conf.py b/docs/conf.py
index 9d54463c..a05eaed1 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,7 @@
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath('../'))
+sys.path.insert(0, os.path.abspath("../"))
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -28,37 +28,34 @@
 
 # -- General configuration ------------------------------------------------
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Include class __init__ in autodoc
-autoclass_content = 'both'
+autoclass_content = "both"
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = [
-    'sphinx.ext.autodoc',
-    'versionwarning.extension'
-]
+extensions = ["sphinx.ext.autodoc", "versionwarning.extension"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix(es) of source filenames.
 # You can specify multiple suffix as a list of string:
 # source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'canvasapi'
-copyright = u'2017, University of Central Florida (Center for Distributed Learning)'
-author = u'University of Central Florida (Center for Distributed Learning)'
+project = "canvasapi"
+copyright = "2017, University of Central Florida (Center for Distributed Learning)"
+author = "University of Central Florida (Center for Distributed Learning)"
 
 
 # The short X.Y version.
@@ -68,7 +65,7 @@
 
 # sphinx-version-warning configuration
 versionwarning_messages = {
-    'develop': 'You are viewing a development build of the canvasapi docs. <a href="/en/stable">Visit the stable release docs here.</a>',
+    "develop": 'You are viewing a development build of the canvasapi docs. <a href="/en/stable">Visit the stable release docs here.</a>',
 }
 versionwarning_body_selector = "ul.wy-breadcrumbs"
 
@@ -81,38 +78,38 @@
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # This patterns also effect to html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 # If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
 
 # If true, `todo` and `todoList` produce output, else they produce nothing.
 todo_include_todos = False
@@ -122,31 +119,31 @@
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'sphinx_rtd_theme'
+html_theme = "sphinx_rtd_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.
 # "<project> v<release> documentation" by default.
-#html_title = u'canvasapi v1.0'
+# html_title = u'canvasapi v1.0'
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (relative to this directory) to use as a favicon of
 # the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
@@ -156,124 +153,123 @@
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
 # directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
 
 # If not None, a 'Last updated on:' timestamp is inserted at every page
 # bottom, using the given strftime format.
 # The empty string is equivalent to '%b %d, %Y'.
-#html_last_updated_fmt = None
+# html_last_updated_fmt = None
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Language to be used for generating the HTML full-text search index.
 # Sphinx supports the following languages:
 #   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
 #   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
-#html_search_language = 'en'
+# html_search_language = 'en'
 
 # A dictionary with options for the search language support, empty by default.
 # 'ja' uses this config value.
 # 'zh' user can custom change `jieba` dictionary path.
-#html_search_options = {'type': 'default'}
+# html_search_options = {'type': 'default'}
 
 # The name of a javascript file (relative to the configuration directory) that
 # implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
+# html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'canvasapidoc'
+htmlhelp_basename = "canvasapidoc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
 latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
+    # The paper size ('letterpaper' or 'a4paper').
+    #'papersize': 'letterpaper',
+    # The font size ('10pt', '11pt' or '12pt').
+    #'pointsize': '10pt',
+    # Additional stuff for the LaTeX preamble.
+    #'preamble': '',
+    # Latex figure (float) alignment
+    #'figure_align': 'htbp',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    (master_doc, 'canvasapi.tex', u'canvasapi Documentation',
-     u'University of Central Florida (Center for Distributed Learning)', 'manual'),
+    (
+        master_doc,
+        "canvasapi.tex",
+        "canvasapi Documentation",
+        "University of Central Florida (Center for Distributed Learning)",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output ---------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    (master_doc, 'canvasapi', u'canvasapi Documentation',
-     [author], 1)
-]
+man_pages = [(master_doc, "canvasapi", "canvasapi Documentation", [author], 1)]
 
 # If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
 
 
 # -- Options for Texinfo output -------------------------------------------
@@ -282,19 +278,25 @@
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    (master_doc, 'canvasapi', u'canvasapi Documentation',
-     author, 'canvasapi', 'One line description of project.',
-     'Miscellaneous'),
+    (
+        master_doc,
+        "canvasapi",
+        "canvasapi Documentation",
+        author,
+        "canvasapi",
+        "One line description of project.",
+        "Miscellaneous",
+    ),
 ]
 
 # Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
 
 # If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
 
 # If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
diff --git a/docs/course-event-ref.rst b/docs/course-event-ref.rst
new file mode 100644
index 00000000..750fd273
--- /dev/null
+++ b/docs/course-event-ref.rst
@@ -0,0 +1,6 @@
+===========
+CourseEvent
+===========
+
+.. autoclass:: canvasapi.course_event.CourseEvent
+    :members:
diff --git a/docs/jwt-ref.rst b/docs/jwt-ref.rst
new file mode 100644
index 00000000..0718442b
--- /dev/null
+++ b/docs/jwt-ref.rst
@@ -0,0 +1,6 @@
+===
+JWT
+===
+
+.. autoclass:: canvasapi.jwt.JWT
+    :members:
diff --git a/docs/rubric-ref.rst b/docs/rubric-ref.rst
index b02aaa22..14c36d3b 100644
--- a/docs/rubric-ref.rst
+++ b/docs/rubric-ref.rst
@@ -5,6 +5,13 @@ Rubric
 .. autoclass:: canvasapi.rubric.Rubric
     :members:
 
+================
+RubricAssessment
+================
+
+.. autoclass:: canvasapi.rubric.RubricAssessment
+    :members:
+
 ==================
 Rubric Association
 ==================
diff --git a/scripts/find_missing_kwargs.py b/scripts/find_missing_kwargs.py
index 92969a8b..a40b3b76 100644
--- a/scripts/find_missing_kwargs.py
+++ b/scripts/find_missing_kwargs.py
@@ -15,6 +15,8 @@
     "Uploader.request_upload_token",
     "Uploader.start",
     "Uploader.upload",
+    "OutcomeGroup.context_ref",
+    "OutcomeLink.context_ref",
 )
 
 
diff --git a/scripts/find_missing_modules.py b/scripts/find_missing_modules.py
new file mode 100644
index 00000000..37c205aa
--- /dev/null
+++ b/scripts/find_missing_modules.py
@@ -0,0 +1,49 @@
+import inspect
+import os
+import sys
+
+sys.path.append(os.path.join(sys.path[0], ".."))
+
+import canvasapi  # noqa
+
+exempt_files = ("__init__",)
+
+
+def find_missing_modules():
+    # get all modules visile to inspect from `canvasapi.__init__`
+    module_names = [
+        module_name
+        for module_name, module in inspect.getmembers(canvasapi, inspect.ismodule)
+    ]
+
+    # get all .py files in canvasapi dir (without .py extension)
+    path = "canvasapi"
+    filenames = [fname[:-3] for fname in os.listdir(path) if fname.endswith(".py")]
+
+    missing_modules = list()
+
+    for filename in filenames:
+        # ignore exempt files
+        if filename in exempt_files:
+            continue
+
+        # check for missing files against module names
+        if filename not in module_names:
+            missing_modules.append(filename)
+
+    return missing_modules
+
+
+if __name__ == "__main__":
+    missing_modules = find_missing_modules()
+    num_missing = len(missing_modules)
+    missing_module_str = ", ".join(missing_modules)
+
+    if missing_modules:
+        print(f"Missing {num_missing} modules from inspect. 💥")
+        for module in missing_modules:
+            print(f"  - {module}")
+        print("Ensure the above modules are imported (even indirectly) to __init__")
+    else:
+        print("All modules accounted for! 👍")
+    sys.exit(num_missing > 0)
diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py
index 4cf4b2eb..9f223ada 100644
--- a/scripts/validate_docstrings.py
+++ b/scripts/validate_docstrings.py
@@ -5,7 +5,7 @@
 
 import requests
 
-sys.path.append(os.path.join(sys.path[0], '..'))
+sys.path.append(os.path.join(sys.path[0], ".."))
 
 import canvasapi  # noqa
 
@@ -16,21 +16,20 @@ def validate_method(themethod, quiet=False):
         return True
 
     # Docstrings without API calls can't be checked this way
-    if not re.search(r':calls:', inspect.getdoc(themethod)):
+    if not re.search(r":calls:", inspect.getdoc(themethod)):
         return True
-    if not re.search(r'<\S*>', inspect.getdoc(themethod)):
+    if not re.search(r"<\S*>", inspect.getdoc(themethod)):
         return True
 
     method_string = inspect.getfile(themethod) + " " + themethod.__name__
     call_lines = re.findall(
-        '`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_',
-        inspect.getdoc(themethod)
+        "`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(themethod)
     )
     if not call_lines:
         if not quiet:
             # Docstring exists, has a :calls: line, contains a URL, but could
             # not be parsed;
-            print('{} Failed to parse :calls: line.'.format(method_string))
+            print("{} Failed to parse :calls: line.".format(method_string))
         return False
     for call_line in call_lines:
         if not validate_docstring(method_string, call_line, quiet):
@@ -40,65 +39,62 @@ def validate_method(themethod, quiet=False):
 
 def validate_docstring(method_string, call_line, quiet):
     docstring_verb, api_URL, doc_URL = call_line
-    api_URL = ''.join(api_URL.split())
-    if api_URL[-1] == '/':
+    api_URL = "".join(api_URL.split())
+    if api_URL[-1] == "/":
         api_URL = api_URL[0:-1]
-    docfile_URL, endpoint_name = re.search('([^#]*)#?(.*)', doc_URL).groups()
+    docfile_URL, endpoint_name = re.search("([^#]*)#?(.*)", doc_URL).groups()
     html_doc_response = requests.get(docfile_URL)
     if html_doc_response.status_code != requests.codes.ok:
         if not quiet:
-            print('{} docstring URL request returned {}'.format(
-                method_string,
-                html_doc_response.status_code
-            ))
+            print(
+                "{} docstring URL request returned {}".format(
+                    method_string, html_doc_response.status_code
+                )
+            )
         return False
 
     endpoint_h2 = re.search(
-        r'<h2[^>]*name=[\'\"]{}[\'\"]'.format(endpoint_name),
-        html_doc_response.text
+        r"<h2[^>]*name=[\'\"]{}[\'\"]".format(endpoint_name), html_doc_response.text
     )
     if not endpoint_name:
         if not quiet:
-            print((
-                '{} docstring URL does not contain an endpoint name in link'
-                ' to API documentation'
-            ).format(method_string))
+            print(
+                (
+                    "{} docstring URL does not contain an endpoint name in link"
+                    " to API documentation"
+                ).format(method_string)
+            )
         return False
     if not endpoint_h2:
         if not quiet:
-            print('{} docstring refers to {} in {}, not found'.format(
-                method_string, endpoint_name, docfile_URL
-            ))
+            print(
+                "{} docstring refers to {} in {}, not found".format(
+                    method_string, endpoint_name, docfile_URL
+                )
+            )
         return False
 
-    endpoint_element_re = re.compile(
-        r'<h3 class=[\"\']endpoint[\"\']>[^<]*<\/h3>'
-    )
+    endpoint_element_re = re.compile(r"<h3 class=[\"\']endpoint[\"\']>[^<]*<\/h3>")
 
     endpoint_search_start_match = endpoint_element_re.search(
-        html_doc_response.text,
-        endpoint_h2.end()
+        html_doc_response.text, endpoint_h2.end()
     )
     if not endpoint_search_start_match:
         if not quiet:
-            print('Found no endpoint after {} in {}'.format(
-                endpoint_name,
-                docfile_URL
-            ))
+            print("Found no endpoint after {} in {}".format(endpoint_name, docfile_URL))
         return False
 
     endpoint_search_start_pos = endpoint_search_start_match.start()
-    after_endpoint_re = re.compile(r'<[^h\/]')
-    endpoint_search_end = after_endpoint_re.search(html_doc_response.text,
-                                                   endpoint_search_start_pos)
+    after_endpoint_re = re.compile(r"<[^h\/]")
+    endpoint_search_end = after_endpoint_re.search(
+        html_doc_response.text, endpoint_search_start_pos
+    )
     if not endpoint_search_end:
         endpoint_search_stop_pos = len(html_doc_response.text)
     else:
         endpoint_search_stop_pos = endpoint_search_end.start()
     endpoint_element_match = endpoint_element_re.search(
-        html_doc_response.text,
-        endpoint_search_start_pos,
-        endpoint_search_stop_pos
+        html_doc_response.text, endpoint_search_start_pos, endpoint_search_stop_pos
     )
     endpoint_element_list = []
     while endpoint_element_match:
@@ -106,21 +102,17 @@ def validate_docstring(method_string, call_line, quiet):
         endpoint_element_match = endpoint_element_re.search(
             html_doc_response.text,
             endpoint_element_match.end(),
-            endpoint_search_stop_pos
+            endpoint_search_stop_pos,
         )
 
     if not endpoint_element_list:
         if not quiet:
-            print('Found no endpoint after {} in {}'.format(
-                endpoint_name,
-                docfile_URL
-            ))
+            print("Found no endpoint after {} in {}".format(endpoint_name, docfile_URL))
         return False
     docfile_lines = []
     for endpoint_element_str in endpoint_element_list:
         docfile_match = re.search(
-            '(POST|GET|PUT|PATCH|DELETE) (.*)',
-            endpoint_element_str
+            "(POST|GET|PUT|PATCH|DELETE) (.*)", endpoint_element_str
         )
         docfile_lines.append(docfile_match.group())
         docfile_verb, docfile_API_URL = docfile_match.groups()
@@ -130,12 +122,14 @@ def validate_docstring(method_string, call_line, quiet):
             continue
         return True
     if not quiet:
-        print('{} docstring {} not found in {} (found {})'.format(
-            method_string,
-            docstring_verb + ' ' + api_URL,
-            doc_URL,
-            str(docfile_lines)
-        ))
+        print(
+            "{} docstring {} not found in {} (found {})".format(
+                method_string,
+                docstring_verb + " " + api_URL,
+                doc_URL,
+                str(docfile_lines),
+            )
+        )
     return False
 
 
@@ -149,5 +143,5 @@ def test_methods():
         validate_method(method_to_test)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     test_methods()


Test patch:
diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 76c1c575..6a22947c 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -33,11 +33,14 @@ jobs:
         run: coverage run -m unittest discover
       - name: Lint markdown files
         uses: bewuethr/mdl-action@v1
+      - name: Check if all modules are visible to inspect
+        run: python scripts/find_missing_modules.py
       - name: Check if methods are alphabetical
         run: python scripts/alphabetic.py
       - name: Check for missing kwargs
         run: python scripts/find_missing_kwargs.py
       - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v1
+        uses: codecov/codecov-action@v3
         with:
           fail_ci_if_error: true
+          token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh
index c8f3809e..00ae4cac 100755
--- a/scripts/run_tests.sh
+++ b/scripts/run_tests.sh
@@ -7,5 +7,6 @@ black --check canvasapi tests
 isort --check canvasapi tests
 flake8 canvasapi tests
 mdl . .github
+python scripts/find_missing_modules.py
 python scripts/alphabetic.py
 python scripts/find_missing_kwargs.py
diff --git a/tests/fixtures/account.json b/tests/fixtures/account.json
index a9feaeff..673581ea 100644
--- a/tests/fixtures/account.json
+++ b/tests/fixtures/account.json
@@ -8,7 +8,7 @@
 			"start_at": "2015-04-01T00:00:00Z",
 			"end_at": "2018-04-01T00:00:00Z"
 		},
-		"status_code":  200
+		"status_code": 200
 	},
 	"create": {
 		"method": "POST",
@@ -150,6 +150,68 @@
 		},
 		"status_code": 200
 	},
+	"get_account_calendars": {
+		"method": "GET",
+		"endpoint": "account_calendars",
+		"data": [
+			{
+				"id": 1,
+				"name": "CDL",
+				"calendar_event_url": "calendarURL",
+				"visible": true
+			},
+			{
+				"id": 2,
+				"name": "DDL",
+				"calendar_event_url": "calendarURL2",
+				"visible": true
+			}
+		]
+	},
+	"get_account_calendar": {
+		"method": "GET",
+		"endpoint": "account_calendars/1",
+		"data": {
+			"id": 10,
+			"name": "Department of Computer Science",
+			"calendar_event_url": "calendarURL",
+			"visible": true
+		}
+	},
+	"get_all_account_calendars": {
+		"method": "GET",
+		"endpoint": "accounts/1/account_calendars",
+		"data": [
+			{
+				"id": 1,
+				"name": "FSU"
+			},
+			{
+				"id": 2,
+				"name": "Full Sail"
+			}
+		]
+	},
+	"update_account_calendar_visibility": {
+		"method": "PUT",
+		"endpoint": "account_calendars/1",
+		"data": {
+			"id": 102,
+			"name": "Department of Aerospace Engineering",
+			"calendar_event_url": "calendarURL",
+			"visible": true
+		}
+	},
+	"update_many_account_calendars_visibility": {
+		"method": "PUT",
+		"endpoint": "accounts/1/account_calendars",
+		"data": {
+			"id": 1,
+			"name": "Biology Department",
+			"calendar_event_url": "calendarURL",
+			"visible": true
+		}
+	},
 	"get_by_id": {
 		"method": "GET",
 		"endpoint": "accounts/1",
@@ -329,48 +391,53 @@
 		"endpoint": "accounts/1/terms",
 		"data": {
 			"enrollment_terms": [
-			{
-				"id": 1,
-				"name": "Enrollment Term 1"
-			},
-			{
-				"id": 2,
-				"name": "Enrollment Term 2"
-			}]
+				{
+					"id": 1,
+					"name": "Enrollment Term 1"
+				},
+				{
+					"id": 2,
+					"name": "Enrollment Term 2"
+				}
+			]
 		},
 		"status_code": 200
 	},
 	"get_notification": {
 		"method": "GET",
 		"endpoint": "accounts/1/account_notifications/1",
-		"data":{
-  			"subject": "Attention Students",
-  			"message": "This is a test of the notification system.",
-  			"start_at": "2013-08-28T23:59:00-06:00",
-  			"end_at": "2013-08-29T23:59:00-06:00",
-  			"icon": "information",
-  			"roles": ["StudentEnrollment"],
-  			"role_ids": [1]
+		"data": {
+			"subject": "Attention Students",
+			"message": "This is a test of the notification system.",
+			"start_at": "2013-08-28T23:59:00-06:00",
+			"end_at": "2013-08-29T23:59:00-06:00",
+			"icon": "information",
+			"roles": [
+				"StudentEnrollment"
+			],
+			"role_ids": [
+				1
+			]
 		},
 		"status_code": 200
 	},
 	"get_grading_periods": {
-        "method": "GET",
-        "endpoint": "accounts/1/grading_periods",
-        "data": {
-            "grading_periods": [
-                {
-                    "title": "Grading period 1",
-                    "id": 1
-                },
-                {
-                    "title": "Grading period 2",
-                    "id": 2
-                }
-            ]
-        },
-        "status_code": 200
-    },
+		"method": "GET",
+		"endpoint": "accounts/1/grading_periods",
+		"data": {
+			"grading_periods": [
+				{
+					"title": "Grading period 1",
+					"id": 1
+				},
+				{
+					"title": "Grading period 2",
+					"id": 2
+				}
+			]
+		},
+		"status_code": 200
+	},
 	"get_groups_context": {
 		"method": "GET",
 		"endpoint": "accounts/1/groups",
@@ -404,6 +471,24 @@
 		],
 		"status_code": 200
 	},
+	"query_audit_by_account": {
+		"method": "GET",
+		"endpoint": "audit/course/accounts/1",
+		"data": [
+			{
+				"id": 1,
+				"name": "UCF",
+				"start_at": "2022-11-18T05:00:00-06:00",
+				"conclude_at": "2022-11-18T15:00:00-06:00"
+			},
+			{
+				"id": 2,
+				"name": "FSU",
+				"start_at": "2022-11-18T05:00:00-06:00",
+				"conclude_at": "2022-11-18T15:00:00-06:00"
+			}
+		]
+	},
 	"multiple": {
 		"method": "GET",
 		"endpoint": "accounts",
@@ -458,9 +543,9 @@
 		],
 		"status_code": 200
 	},
-	"get_outcome_import_status":{
-		"method" : "GET",
-		"endpoint" : "accounts/1/outcome_imports/1",
+	"get_outcome_import_status": {
+		"method": "GET",
+		"endpoint": "accounts/1/outcome_imports/1",
 		"data": {
 			"id": 1,
 			"created_at": "2013-12-01T23:59:00-06:00",
@@ -475,9 +560,9 @@
 		},
 		"status_code": 200
 	},
-	"get_outcome_import_status_latest":{
-		"method" : "GET",
-		"endpoint" : "accounts/1/outcome_imports/latest",
+	"get_outcome_import_status_latest": {
+		"method": "GET",
+		"endpoint": "accounts/1/outcome_imports/latest",
 		"data": {
 			"id": 1,
 			"created_at": "2013-12-01T23:59:00-06:00",
@@ -492,93 +577,93 @@
 		},
 		"status_code": 200
 	},
-	"create_report":{
+	"create_report": {
 		"method": "POST",
 		"endpoint": "accounts/1/reports/zero_activity_csv",
 		"data": {
-        	"title": "Zero Activity",
-        	"parameters": {
-            	"enrollment_term_id": {
-                	"required": false,
-                	"description": "The canvas id of the term to get grades from"
-            	},
-            	"start_at": {
-                	"required": false,
-                	"description": "The first date in the date range, the second date is the time the report is run."
-            	},
-            	"course_id": {
-                	"required": false,
-                	"description": "The course to report on"
-            	}
-        },
-        "report": "zero_activity_csv",
-        "last_run": null
+			"title": "Zero Activity",
+			"parameters": {
+				"enrollment_term_id": {
+					"required": false,
+					"description": "The canvas id of the term to get grades from"
+				},
+				"start_at": {
+					"required": false,
+					"description": "The first date in the date range, the second date is the time the report is run."
+				},
+				"course_id": {
+					"required": false,
+					"description": "The course to report on"
+				}
+			},
+			"report": "zero_activity_csv",
+			"last_run": null
 		},
 		"status_code": 200
 	},
-	"delete_report":{
+	"delete_report": {
 		"method": "DELETE",
 		"endpoint": "accounts/1/reports/zero_activity_csv/1",
 		"data": {
-    		"id": 1,
-    		"progress": 100,
-    		"parameters": {
-    		    "extra_text": "Term: All Terms;"
-    		},
-    		"current_line": 1,
-    		"status": "deleted",
-    		"report": "zero_activity_csv",
-    		"created_at": "2019-08-13T15:23:15-04:00",
-    		"started_at": "2019-08-13T15:23:17-04:00",
-    		"ended_at": "2019-08-13T15:23:17-04:00",
-    		"file_url": "xxxxxxxxxxxxxxxxxxxxxxx",
-    		"attachment": {
-    		    "id": 1,
-    		    "uuid": "CCVh71fQEPsDqc4VOoGBr5llCbzAeLEpBcWcAe88",
-    		    "folder_id": 1,
-    		    "display_name": "zero_activity_csv_xxxxxxxxxxxxxxxxxxxxxxx.csv",
-    		    "filename": "zero_activity_csv_xxxxxxxxxxxxxxxxxxxxxxx.csv",
-    		    "workflow_state": "processed",
-    		    "upload_status": "success",
-    		    "content-type": "text/csv",
-    		    "url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
-    		    "size": 100,
-    		    "created_at": "2019-08-13T19:23:17Z",
-    		    "updated_at": "2019-08-13T19:23:17Z",
-    		    "unlock_at": null,
-    		    "locked": false,
-    		    "hidden": false,
-    		    "lock_at": null,
-    		    "hidden_for_user": false,
-    		    "thumbnail_url": null,
-    		    "modified_at": "2019-08-13T19:23:17Z",
-    		    "mime_class": "file",
-    		    "media_entry_id": null,
-    		    "locked_for_user": false
-    			}
+			"id": 1,
+			"progress": 100,
+			"parameters": {
+				"extra_text": "Term: All Terms;"
+			},
+			"current_line": 1,
+			"status": "deleted",
+			"report": "zero_activity_csv",
+			"created_at": "2019-08-13T15:23:15-04:00",
+			"started_at": "2019-08-13T15:23:17-04:00",
+			"ended_at": "2019-08-13T15:23:17-04:00",
+			"file_url": "xxxxxxxxxxxxxxxxxxxxxxx",
+			"attachment": {
+				"id": 1,
+				"uuid": "CCVh71fQEPsDqc4VOoGBr5llCbzAeLEpBcWcAe88",
+				"folder_id": 1,
+				"display_name": "zero_activity_csv_xxxxxxxxxxxxxxxxxxxxxxx.csv",
+				"filename": "zero_activity_csv_xxxxxxxxxxxxxxxxxxxxxxx.csv",
+				"workflow_state": "processed",
+				"upload_status": "success",
+				"content-type": "text/csv",
+				"url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+				"size": 100,
+				"created_at": "2019-08-13T19:23:17Z",
+				"updated_at": "2019-08-13T19:23:17Z",
+				"unlock_at": null,
+				"locked": false,
+				"hidden": false,
+				"lock_at": null,
+				"hidden_for_user": false,
+				"thumbnail_url": null,
+				"modified_at": "2019-08-13T19:23:17Z",
+				"mime_class": "file",
+				"media_entry_id": null,
+				"locked_for_user": false
+			}
 		}
 	},
-	"get_report":{
+	"get_report": {
 		"method": "GET",
 		"endpoint": "accounts/1/reports/zero_activity_csv/1",
-		"data":{
-        	"title": "Zero Activity",
-        	"parameters": {
-            	"enrollment_term_id": {
-                	"required": false,
-                	"description": "The canvas id of the term to get grades from"
-            	},
-            	"start_at": {
-                	"required": false,
-                	"description": "The first date in the date range, the second date is the time the report is run."
-            	},
-            	"course_id": {
-                	"required": false,
-                	"description": "The course to report on"
-            	}
-        },
-        "report": "zero_activity_csv",
-        "last_run": null
+		"data": {
+			"title": "Zero Activity",
+			"parameters": {
+				"enrollment_term_id": {
+					"required": false,
+					"description": "The canvas id of the term to get grades from"
+				},
+				"start_at": {
+					"required": false,
+					"description": "The first date in the date range, the second date is the time the report is run."
+				},
+				"course_id": {
+					"required": false,
+					"description": "The course to report on"
+				}
+			},
+			"report": "zero_activity_csv",
+			"last_run": null
 		},
 		"status_code": 200
 	},
@@ -780,10 +865,10 @@
 		"method": "PUT",
 		"endpoint": "accounts/1/account_notifications/1",
 		"data": {
-            "subject": "subject",
-            "message": "Message",
-            "start_at": "2015-04-01T00:00:00Z",
-            "end_at": "2018-04-01T00:00:00Z"
+			"subject": "subject",
+			"message": "Message",
+			"start_at": "2015-04-01T00:00:00Z",
+			"end_at": "2018-04-01T00:00:00Z"
 		},
 		"status_code": 200
 	},
@@ -872,7 +957,7 @@
 	"create_role": {
 		"method": "POST",
 		"endpoint": "accounts/1/roles",
-		"data":{
+		"data": {
 			"id": 1,
 			"role": "StudentEnrollment",
 			"label": "Student"
@@ -1030,22 +1115,22 @@
 		"endpoint": "accounts/1/analytics/terms/1/grades",
 		"data": [
 			{
-			  "0": "13435",
-			  "1": "41",
-			  "2": "58",
-			  "3": "27",
-			  "68": "1387",
-			  "69": "1412",
-			  "70": "2199",
-			  "85": "5575",
-			  "86": "6543",
-			  "87": "6144",
-			  "88": "7198",
-			  "89": "6561",
-			  "90": "8854",
-			  "91": "7745",
-			  "92": "8800",
-			  "93": "7798"
+				"0": "13435",
+				"1": "41",
+				"2": "58",
+				"3": "27",
+				"68": "1387",
+				"69": "1412",
+				"70": "2199",
+				"85": "5575",
+				"86": "6543",
+				"87": "6144",
+				"88": "7198",
+				"89": "6561",
+				"90": "8854",
+				"91": "7745",
+				"92": "8800",
+				"93": "7798"
 			}
 		]
 	},
@@ -1054,22 +1139,22 @@
 		"endpoint": "accounts/1/analytics/current/grades",
 		"data": [
 			{
-			  "0": "13435",
-			  "1": "41",
-			  "2": "58",
-			  "3": "27",
-			  "68": "1387",
-			  "69": "1412",
-			  "70": "2199",
-			  "85": "5575",
-			  "86": "6543",
-			  "87": "6144",
-			  "88": "7198",
-			  "89": "6561",
-			  "90": "8854",
-			  "91": "7745",
-			  "92": "8800",
-			  "93": "7798"
+				"0": "13435",
+				"1": "41",
+				"2": "58",
+				"3": "27",
+				"68": "1387",
+				"69": "1412",
+				"70": "2199",
+				"85": "5575",
+				"86": "6543",
+				"87": "6144",
+				"88": "7198",
+				"89": "6561",
+				"90": "8854",
+				"91": "7745",
+				"92": "8800",
+				"93": "7798"
 			}
 		]
 	},
@@ -1078,22 +1163,22 @@
 		"endpoint": "accounts/1/analytics/completed/grades",
 		"data": [
 			{
-			  "0": "13435",
-			  "1": "41",
-			  "2": "58",
-			  "3": "27",
-			  "68": "1387",
-			  "69": "1412",
-			  "70": "2199",
-			  "85": "5575",
-			  "86": "6543",
-			  "87": "6144",
-			  "88": "7198",
-			  "89": "6561",
-			  "90": "8854",
-			  "91": "7745",
-			  "92": "8800",
-			  "93": "7798"
+				"0": "13435",
+				"1": "41",
+				"2": "58",
+				"3": "27",
+				"68": "1387",
+				"69": "1412",
+				"70": "2199",
+				"85": "5575",
+				"86": "6543",
+				"87": "6144",
+				"88": "7198",
+				"89": "6561",
+				"90": "8854",
+				"91": "7745",
+				"92": "8800",
+				"93": "7798"
 			}
 		]
 	},
@@ -1185,7 +1270,7 @@
 	"add_authentication_providers": {
 		"method": "POST",
 		"endpoint": "accounts/1/authentication_providers",
-		"data":{
+		"data": {
 			"id": 1,
 			"auth_type": "saml",
 			"position": 1
@@ -1227,11 +1312,20 @@
 			"context_id": 1,
 			"context_type": "Account",
 			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
+				{
+					"name": "A",
+					"value": 0.9
+				},
+				{
+					"name": "B",
+					"value": 0.8
+				},
+				{
+					"name": "C",
+					"value": 0.7
+				}
 			]
-		  },
+		},
 		"status_code": 200
 	},
 	"get_grading_standards": {
@@ -1239,38 +1333,53 @@
 		"endpoint": "accounts/1/grading_standards",
 		"data": [
 			{
-			"title": "Grading Standard 1",
-			"id": 1,
-			"context_id": 1,
-			"context_type": "Account",
-			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
-			]
-		  },
-		  {
-			"title": "Grading Standard 2",
-			"id": 2,
-			"context_id": 1,
-			"context_type": "Account",
-			"grading_scheme": [
-			  {"name": "D", "value": 0.6},
-			  {"name": "F", "value": 0.5}
-			]
-		  }
+				"title": "Grading Standard 1",
+				"id": 1,
+				"context_id": 1,
+				"context_type": "Account",
+				"grading_scheme": [
+					{
+						"name": "A",
+						"value": 0.9
+					},
+					{
+						"name": "B",
+						"value": 0.8
+					},
+					{
+						"name": "C",
+						"value": 0.7
+					}
+				]
+			},
+			{
+				"title": "Grading Standard 2",
+				"id": 2,
+				"context_id": 1,
+				"context_type": "Account",
+				"grading_scheme": [
+					{
+						"name": "D",
+						"value": 0.6
+					},
+					{
+						"name": "F",
+						"value": 0.5
+					}
+				]
+			}
 		],
 		"status_code": 200
 	},
 	"delete_grading_period": {
-        "method": "DELETE",
-        "endpoint": "accounts/1/grading_periods/1",
-        "data": {
-            "delete": true
-        },
-        "status_code": 200
-    },
-		"get_rubric_single": {
+		"method": "DELETE",
+		"endpoint": "accounts/1/grading_periods/1",
+		"data": {
+			"delete": true
+		},
+		"status_code": 200
+	},
+	"get_rubric_single": {
 		"method": "GET",
 		"endpoint": "accounts/1/rubrics/1",
 		"data": {
@@ -1324,37 +1433,46 @@
 			"context_id": 1,
 			"context_type": "Account",
 			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
+				{
+					"name": "A",
+					"value": 0.9
+				},
+				{
+					"name": "B",
+					"value": 0.8
+				},
+				{
+					"name": "C",
+					"value": 0.7
+				}
 			]
-		  },
+		},
 		"status_code": 200
 	},
 	"get_scopes": {
-	"method": "GET",
-	"endpoint": "accounts/1/scopes",
-	"data": [
-		{
-        	"controller": "users",
-        	"action": "settings",
-        	"verb": "PUT",
-        	"path": "/api/v1/users/:id/settings",
-        	"scope": "url:PUT|/api/v1/users/:id/settings",
-        	"resource": "users",
-        	"resource_name": "Users"
-		},
-		{
-        	"controller": "course_nicknames",
-        	"action": "show",
-        	"verb": "GET",
-        	"path": "/api/v1/users/self/course_nicknames/:course_id",
-        	"scope": "url:GET|/api/v1/users/self/course_nicknames/:course_id",
-        	"resource": "users",
-        	"resource_name": "Users"
-		}
-	],
-	"status_code": 200
+		"method": "GET",
+		"endpoint": "accounts/1/scopes",
+		"data": [
+			{
+				"controller": "users",
+				"action": "settings",
+				"verb": "PUT",
+				"path": "/api/v1/users/:id/settings",
+				"scope": "url:PUT|/api/v1/users/:id/settings",
+				"resource": "users",
+				"resource_name": "Users"
+			},
+			{
+				"controller": "course_nicknames",
+				"action": "show",
+				"verb": "GET",
+				"path": "/api/v1/users/self/course_nicknames/:course_id",
+				"scope": "url:GET|/api/v1/users/self/course_nicknames/:course_id",
+				"resource": "users",
+				"resource_name": "Users"
+			}
+		],
+		"status_code": 200
 	},
 	"create_content_migration": {
 		"method": "POST",
@@ -1663,8 +1781,7 @@
 	"abort_sis_imports_pending_blank": {
 		"method": "PUT",
 		"endpoint": "accounts/1/sis_imports/abort_all_pending",
-		"data": {
-		},
+		"data": {},
 		"status_code": 200
 	},
 	"create_admin": {
@@ -1728,16 +1845,8 @@
 		"method": "GET",
 		"endpoint": "accounts/1/features/enabled",
 		"data": [
-			{
-				"name": "feature_one",
-				"display_name": "Feature One",
-				"applies_to": "Account"
-			},
-			{
-				"name": "feature_two",
-				"display_name": "Feature Two",
-				"applies_to": "Account"
-			}
+			"feature_one",
+			"feature_two"
 		],
 		"status_code": 200
 	},
diff --git a/tests/fixtures/assignment.json b/tests/fixtures/assignment.json
index 95d8cc34..cfcbd2f9 100644
--- a/tests/fixtures/assignment.json
+++ b/tests/fixtures/assignment.json
@@ -490,5 +490,33 @@
 		"data": {
 			"message": "OK"
 		}
+	},
+	"get_students_selected_moderation": {
+		"method": "GET",
+		"endpoint": "courses/1/assignments/1/moderated_students",
+		"data": [
+			{
+				"id": 1,
+				"name": "John Doe"
+			},
+			{
+				"id": 2,
+				"name": "John Smith"
+			}
+		]
+	},
+	"select_students_for_moderation": {
+		"method": "POST",
+		"endpoint": "courses/1/assignments/1/moderated_students",
+		"data": [
+			{
+				"id": 11,
+				"name": "Joyce Smith"
+			},
+			{
+				"id": 12,
+				"name": "Jane Doe"
+			}
+		]
 	}
 }
diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json
index cc750c9e..81b31308 100644
--- a/tests/fixtures/course.json
+++ b/tests/fixtures/course.json
@@ -287,19 +287,20 @@
 		"method": "GET",
 		"endpoint": "courses/1/epub_exports/1",
 		"data": {
+			"id": 1,
+			"name": "course1",
+			"epub_export": {
 				"id": 1,
-				"name": "course1",
-				"epub_export": {
-					"id": 1,
-					"created_at": "2019-01-01T00:00:00Z",
-					"attachment": {"url":"https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"},
-					"progress_url": "https://dummyurl.com/api/v1/progress/4",
-					"user_id": 4,
-					"workflow_state": "exported"
-					}
+				"created_at": "2019-01-01T00:00:00Z",
+				"attachment": {
+					"url": "https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"
+				},
+				"progress_url": "https://dummyurl.com/api/v1/progress/4",
+				"user_id": 4,
+				"workflow_state": "exported"
+			}
 		},
 		"status_code": 200
-
 	},
 	"get_epub_exports": {
 		"method": "GET",
@@ -307,30 +308,33 @@
 		"data": {
 			"courses": [
 				{
-				"id": 1,
-				"name": "course1",
-				"epub_export": {
 					"id": 1,
-					"created_at": "2019-01-01T00:00:00Z",
-					"attachment": {"url":"https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"},
-					"progress_url": "https://dummyurl.com/api/v1/progress/4",
-					"user_id": 4,
-					"workflow_state": "exported"
+					"name": "course1",
+					"epub_export": {
+						"id": 1,
+						"created_at": "2019-01-01T00:00:00Z",
+						"attachment": {
+							"url": "https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"
+						},
+						"progress_url": "https://dummyurl.com/api/v1/progress/4",
+						"user_id": 4,
+						"workflow_state": "exported"
 					}
 				},
 				{
-				"id": 2,
-				"name": "course2",
-				"epub_export": {
 					"id": 2,
-					"created_at": "2019-06-01T00:00:00Z",
-					"attachment": {"url":"https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"},
-					"progress_url": "https://dummyurl.com/api/v1/progress/4",
-					"user_id": 5,
-					"workflow_state": "exported"
+					"name": "course2",
+					"epub_export": {
+						"id": 2,
+						"created_at": "2019-06-01T00:00:00Z",
+						"attachment": {
+							"url": "https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"
+						},
+						"progress_url": "https://dummyurl.com/api/v1/progress/4",
+						"user_id": 5,
+						"workflow_state": "exported"
 					}
 				}
-
 			]
 		},
 		"status_code": 200
@@ -339,16 +343,18 @@
 		"method": "POST",
 		"endpoint": "courses/1/epub_exports/",
 		"data": {
+			"id": 1,
+			"name": "course1",
+			"epub_export": {
 				"id": 1,
-				"name": "course1",
-				"epub_export": {
-					"id": 1,
-					"created_at": "2019-01-01T00:00:00Z",
-					"attachment": {"url":"https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"},
-					"progress_url": "https://dummyurl.com/api/v1/progress/4",
-					"user_id": 4,
-					"workflow_state": "exported"
-					}
+				"created_at": "2019-01-01T00:00:00Z",
+				"attachment": {
+					"url": "https://dummyurl.com/api/v1/attachments/789?download_frd=1&verifier=bG9sY2F0cyEh"
+				},
+				"progress_url": "https://dummyurl.com/api/v1/progress/4",
+				"user_id": 4,
+				"workflow_state": "exported"
+			}
 		},
 		"status_code": 200
 	},
@@ -475,7 +481,9 @@
 						"grading_type": "points",
 						"course_id": 1,
 						"name": "Test assignment",
-						"submission_types": ["online_text_entry"]
+						"submission_types": [
+							"online_text_entry"
+						]
 					}
 				],
 				"courses": [
@@ -498,9 +506,9 @@
 		},
 		"status_code": 200
 	},
-	"get_outcome_import_status":{
-		"method" : "GET",
-		"endpoint" : "courses/1/outcome_imports/1",
+	"get_outcome_import_status": {
+		"method": "GET",
+		"endpoint": "courses/1/outcome_imports/1",
 		"data": {
 			"id": 1,
 			"created_at": "2013-12-01T23:59:00-06:00",
@@ -515,9 +523,9 @@
 		},
 		"status_code": 200
 	},
-	"get_outcome_import_status_latest":{
-		"method" : "GET",
-		"endpoint" : "courses/1/outcome_imports/latest",
+	"get_outcome_import_status_latest": {
+		"method": "GET",
+		"endpoint": "courses/1/outcome_imports/latest",
 		"data": {
 			"id": 1,
 			"created_at": "2013-12-01T23:59:00-06:00",
@@ -544,43 +552,54 @@
 		"method": "GET",
 		"endpoint": "courses/1/quizzes/assignment_overrides",
 		"data": {
-			"quiz_assignment_overrides": [{
-				"quiz_id": "1",
-				"due_dates": [{
-					"id": 1,
-					"due_at": "2014-02-21T06:59:59Z",
-					"unlock_at": null,
-					"lock_at": "2014-02-21T06:59:59Z",
-					"title": "Project X",
-					"base": true
-				}],
-				"all_dates": [{
-					"id": 1,
-					"due_at": "2014-02-21T06:59:59Z",
-					"unlock_at": null,
-					"lock_at": "2014-02-21T06:59:59Z",
-					"title": "Project X",
-					"base": true
-				}]
-			},{
-				"quiz_id": "2",
-				"due_dates": [{
-					"id": 1,
-					"due_at": "2014-02-21T06:59:59Z",
-					"unlock_at": null,
-					"lock_at": "2014-02-21T06:59:59Z",
-					"title": "Project X",
-					"base": true
-				}],
-				"all_dates": [{
-					"id": 1,
-					"due_at": "2014-02-21T06:59:59Z",
-					"unlock_at": null,
-					"lock_at": "2014-02-21T06:59:59Z",
-					"title": "Project X",
-					"base": true
-				}]
-			}]
+			"quiz_assignment_overrides": [
+				{
+					"quiz_id": "1",
+					"due_dates": [
+						{
+							"id": 1,
+							"due_at": "2014-02-21T06:59:59Z",
+							"unlock_at": null,
+							"lock_at": "2014-02-21T06:59:59Z",
+							"title": "Project X",
+							"base": true
+						}
+					],
+					"all_dates": [
+						{
+							"id": 1,
+							"due_at": "2014-02-21T06:59:59Z",
+							"unlock_at": null,
+							"lock_at": "2014-02-21T06:59:59Z",
+							"title": "Project X",
+							"base": true
+						}
+					]
+				},
+				{
+					"quiz_id": "2",
+					"due_dates": [
+						{
+							"id": 1,
+							"due_at": "2014-02-21T06:59:59Z",
+							"unlock_at": null,
+							"lock_at": "2014-02-21T06:59:59Z",
+							"title": "Project X",
+							"base": true
+						}
+					],
+					"all_dates": [
+						{
+							"id": 1,
+							"due_at": "2014-02-21T06:59:59Z",
+							"unlock_at": null,
+							"lock_at": "2014-02-21T06:59:59Z",
+							"title": "Project X",
+							"base": true
+						}
+					]
+				}
+			]
 		},
 		"status_code": 200
 	},
@@ -661,7 +680,7 @@
 		],
 		"status_code": 200
 	},
-	"get_section":{
+	"get_section": {
 		"method": "GET",
 		"endpoint": "courses/1/sections/1",
 		"data": {
@@ -713,15 +732,15 @@
 		"endpoint": "courses/1/gradebook_history/08-23-2019/graders/1/assignments/1/submissions",
 		"data": [
 			{
-                "submission_id": 4,
-                "versions": null
-            },
-            {
-                "submission_id": 7,
-                "versions": null
-            }
-        ]
-    },
+				"submission_id": 4,
+				"versions": null
+			},
+			{
+				"submission_id": 7,
+				"versions": null
+			}
+		]
+	},
 	"todo_items": {
 		"method": "GET",
 		"endpoint": "courses/1/todo",
@@ -1215,7 +1234,7 @@
 	"show_front_page": {
 		"method": "GET",
 		"endpoint": "courses/1/front_page",
-		"data":{
+		"data": {
 			"id": 1,
 			"url": "front-page",
 			"title": "Front Page"
@@ -1399,11 +1418,10 @@
 	"get_discussion_topic": {
 		"method": "GET",
 		"endpoint": "courses/1/discussion_topics/1",
-		"data":
-			{
-				"id": 1,
-				"title": "Topic 1"
-			},
+		"data": {
+			"id": 1,
+			"title": "Topic 1"
+		},
 		"status_code": 200
 	},
 	"get_file": {
@@ -1415,16 +1433,16 @@
 			"size": 2048
 		},
 		"status_code": 200
-    },
-    "get_file_quota": {
-        "method": "GET",
-        "endpoint": "courses/1/files/quota",
-        "data": {
-            "quota": 524288000,
-            "quota_used": 402653184
-        },
-        "status_code": 200
-    },
+	},
+	"get_file_quota": {
+		"method": "GET",
+		"endpoint": "courses/1/files/quota",
+		"data": {
+			"quota": 524288000,
+			"quota_used": 402653184
+		},
+		"status_code": 200
+	},
 	"get_full_discussion_topic": {
 		"method": "GET",
 		"endpoint": "courses/1/discussion_topics/1/view",
@@ -1472,7 +1490,11 @@
 		"endpoint": "courses/1/discussion_topics/reorder",
 		"data": {
 			"reorder": true,
-			"order": [1, 2, 3]
+			"order": [
+				1,
+				2,
+				3
+			]
 		},
 		"status_code": 200
 	},
@@ -1614,7 +1636,7 @@
 					"2012-01-24T13:00:00-00:00": 19,
 					"2012-01-24T14:00:00-00:00": 13,
 					"2012-01-27T09:00:00-00:00": 23
-			},
+				},
 				"participations": [
 					{
 						"created_at": "2012-01-21T22:00:00-06:00",
@@ -1679,16 +1701,34 @@
 		"endpoint": "courses/1/analytics/users/1/communication",
 		"data": [
 			{
-				"2012-01-24":{
-					"instructorMessages":1,
-					"studentMessages":2
+				"2012-01-24": {
+					"instructorMessages": 1,
+					"studentMessages": 2
 				},
-				"2012-01-27":{
-					"studentMessages":1
+				"2012-01-27": {
+					"studentMessages": 1
 				}
 			}
 		]
 	},
+	"query_audit_by_course": {
+		"method": "GET",
+		"endpoint": "audit/course/courses/1",
+		"data": [
+			{
+				"id": 1,
+				"name": "FIU",
+				"start_at": "2022-11-18T05:00:00-06:00",
+				"conclude_at": "2022-11-18T15:00:00-06:00"
+			},
+			{
+				"id": 2,
+				"name": "UNF",
+				"start_at": "2022-11-18T05:00:00-06:00",
+				"conclude_at": "2022-11-18T15:00:00-06:00"
+			}
+		]
+	},
 	"list_tabs": {
 		"method": "GET",
 		"endpoint": "courses/1/tabs",
@@ -1796,12 +1836,12 @@
 		"method": "PUT",
 		"endpoint": "courses/1/assignments/1/submissions/1",
 		"data": {
-				"id": 1,
-				"assignments_id": 1,
-				"user_id": 1,
-				"html_url": "https://example.com/courses/1/assignments/1/submissions/1",
-				"submission_type": "online_upload",
-				"excused": true
+			"id": 1,
+			"assignments_id": 1,
+			"user_id": 1,
+			"html_url": "https://example.com/courses/1/assignments/1/submissions/1",
+			"submission_type": "online_upload",
+			"excused": true
 		},
 		"status_code": 200
 	},
@@ -1820,15 +1860,13 @@
 		"endpoint": "search/all_courses",
 		"data": [
 			{
-				"course":
-				{
+				"course": {
 					"id": 1,
 					"name": "Course 1"
 				}
 			},
 			{
-				"course":
-				{
+				"course": {
 					"id": 2,
 					"name": "Course 2"
 				}
@@ -1845,11 +1883,20 @@
 			"context_id": 1,
 			"context_type": "Course",
 			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
+				{
+					"name": "A",
+					"value": 0.9
+				},
+				{
+					"name": "B",
+					"value": 0.8
+				},
+				{
+					"name": "C",
+					"value": 0.7
+				}
 			]
-		  },
+		},
 		"status_code": 200
 	},
 	"get_grading_periods": {
@@ -1890,26 +1937,41 @@
 		"endpoint": "courses/1/grading_standards",
 		"data": [
 			{
-			"title": "Grading Standard 1",
-			"id": 1,
-			"context_id": 1,
-			"context_type": "Course",
-			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
-			]
-		  },
-		  {
-			"title": "Grading Standard 2",
-			"id": 2,
-			"context_id": 1,
-			"context_type": "Course",
-			"grading_scheme": [
-			  {"name": "D", "value": 0.6},
-			  {"name": "F", "value": 0.5}
-			]
-		  }
+				"title": "Grading Standard 1",
+				"id": 1,
+				"context_id": 1,
+				"context_type": "Course",
+				"grading_scheme": [
+					{
+						"name": "A",
+						"value": 0.9
+					},
+					{
+						"name": "B",
+						"value": 0.8
+					},
+					{
+						"name": "C",
+						"value": 0.7
+					}
+				]
+			},
+			{
+				"title": "Grading Standard 2",
+				"id": 2,
+				"context_id": 1,
+				"context_type": "Course",
+				"grading_scheme": [
+					{
+						"name": "D",
+						"value": 0.6
+					},
+					{
+						"name": "F",
+						"value": 0.5
+					}
+				]
+			}
 		],
 		"status_code": 200
 	},
@@ -1923,7 +1985,7 @@
 					{
 						"name": "John Smith",
 						"id": 49,
-						"assignments":[
+						"assignments": [
 							{
 								"id": 44,
 								"description": "<p>homework1</p>"
@@ -1938,7 +2000,7 @@
 					{
 						"name": "Sara Hanks",
 						"id": 32,
-						"assignments":[
+						"assignments": [
 							{
 								"id": 19,
 								"description": "<p>homework2</p>"
@@ -1948,7 +2010,6 @@
 				]
 			}
 		]
-
 	},
 	"get_gradebook_history_details": {
 		"method": "GET",
@@ -1957,12 +2018,20 @@
 			{
 				"id": 27,
 				"name": "John Smith",
-				"assignments": [1, 2, 3]
+				"assignments": [
+					1,
+					2,
+					3
+				]
 			},
 			{
 				"id": 30,
 				"name": "Jenny Parker",
-				"assignments": [4, 5, 6]
+				"assignments": [
+					4,
+					5,
+					6
+				]
 			}
 		]
 	},
@@ -1975,11 +2044,20 @@
 			"context_id": 1,
 			"context_type": "Course",
 			"grading_scheme": [
-			  {"name": "A", "value": 0.9},
-			  {"name": "B", "value": 0.8},
-			  {"name": "C", "value": 0.7}
+				{
+					"name": "A",
+					"value": 0.9
+				},
+				{
+					"name": "B",
+					"value": 0.8
+				},
+				{
+					"name": "C",
+					"value": 0.7
+				}
 			]
-		  },
+		},
 		"status_code": 200
 	},
 	"create_content_migration": {
@@ -2136,16 +2214,8 @@
 		"method": "GET",
 		"endpoint": "courses/1/features/enabled",
 		"data": [
-			{
-				"name": "feature_test",
-				"display_name": "Feature Test",
-				"applies_to": "Course"
-			},
-			{
-				"name": "feature_second",
-				"display_name": "Feature Second",
-				"applies_to": "Course"
-			}
+			"feature_one",
+			"feature_two"
 		],
 		"status_code": 200
 	},
@@ -2241,7 +2311,10 @@
 			"use_justification": "fair_use",
 			"license": "private",
 			"message": "2 files updated",
-			"file_ids": [1,2]
+			"file_ids": [
+				1,
+				2
+			]
 		},
 		"status_code": 200
 	},
@@ -2250,7 +2323,10 @@
 		"endpoint": "courses/1/usage_rights",
 		"data": {
 			"message": "2 files updated",
-			"file_ids": [1,2]
+			"file_ids": [
+				1,
+				2
+			]
 		},
 		"status_code": 200
 	},
@@ -2316,11 +2392,11 @@
 		],
 		"status_code": 200
 	},
-    "resolve_path": {
+	"resolve_path": {
 		"method": "GET",
 		"endpoint": "courses/1/folders/by_path/Folder_Level_1/Folder_Level_2/Folder_Level_3",
 		"data": [
-            {
+			{
 				"id": 2,
 				"files_count": 0,
 				"folders_count": 1,
@@ -2351,18 +2427,18 @@
 		],
 		"status_code": 200
 	},
-    "resolve_path_null": {
+	"resolve_path_null": {
 		"method": "GET",
 		"endpoint": "courses/1/folders/by_path",
 		"data": [
-            {
+			{
 				"id": 2,
 				"files_count": 0,
 				"folders_count": 1,
 				"name": "course_files",
 				"full_name": "course_files"
 			}
-        ],
+		],
 		"status_code": 200
 	}
 }
diff --git a/tests/fixtures/enrollment.json b/tests/fixtures/enrollment.json
index d665daf6..c8b7527c 100644
--- a/tests/fixtures/enrollment.json
+++ b/tests/fixtures/enrollment.json
@@ -33,5 +33,21 @@
 			"state": "active"
 		},
 		"status_code": 200
+	},
+	"accept": {
+		"method": "POST",
+		"endpoint": "courses/1/enrollments/1/accept",
+		"data": {
+			"success": true
+		},
+		"status_code": 200
+	},
+	"reject": {
+		"method": "POST",
+		"endpoint": "courses/1/enrollments/1/reject",
+		"data": {
+			"success": true
+		},
+		"status_code": 200
 	}
-}
\ No newline at end of file
+}
diff --git a/tests/fixtures/file.json b/tests/fixtures/file.json
index f5aa344c..7d007135 100644
--- a/tests/fixtures/file.json
+++ b/tests/fixtures/file.json
@@ -32,5 +32,14 @@
         "endpoint": "ANY",
         "data": "file contents are here",
         "status_code": 200
+    },
+    "update_file": {
+        "method": "PUT",
+        "endpoint": "files/1",
+        "data": {
+            "id": 1,
+            "display_name": "New filename.docx"
+        },
+        "status_code": 200
     }
 }
diff --git a/tests/fixtures/jwt.json b/tests/fixtures/jwt.json
new file mode 100644
index 00000000..e4c414a7
--- /dev/null
+++ b/tests/fixtures/jwt.json
@@ -0,0 +1,17 @@
+{
+  "create_jwt": {
+    "method": "POST",
+    "endpoint": "jwts",
+    "data": {
+      "token": "ZjM0UTZmLyVNSjdqb10wLV9jQSxeUiogXUlCWUs7Tg=="
+    }
+  },
+
+  "refresh_jwt": {
+    "method": "POST",
+    "endpoint": "jwts/refresh",
+    "data": {
+      "token": "O3MzNjpPKWc+fmFfMXRJJiEoR1VbSDVDT1IzUF1IJUpjJ3JSe0lrMHw8OUlX"
+    }
+  }
+}
diff --git a/tests/fixtures/rubric.json b/tests/fixtures/rubric.json
index df4e0586..0eea19cb 100644
--- a/tests/fixtures/rubric.json
+++ b/tests/fixtures/rubric.json
@@ -1,4 +1,64 @@
 {
+	"create_rubric_assessment": {
+		"method": "POST",
+		"endpoint": "courses/1/rubric_associations/4/rubric_assessments",
+		"data": {
+			"id": 1,
+			"rubric_id": 1,
+			"rubric_association_id": 4,
+			"score": 5.0,
+			"artifact_type": "Submission",
+			"artifact_id": 3,
+			"artifact_attempt": 2,
+			"assessment_type": "grading",
+			"assessor_id": 6,
+			"provisional": "false"
+		},
+		"status_code": 200
+	},
+	"update_rubric_assessment": {
+		"method": "PUT",
+		"endpoint": "courses/4/rubric_associations/4/rubric_assessments/1",
+		"data": {
+			"id": 1,
+			"rubric_id": 1,
+			"rubric_association_id": 4,
+			"score": 5.0,
+			"artifact_type": "Submission",
+			"artifact_id": 3,
+			"artifact_attempt": 2,
+			"assessment_type": "grading",
+			"assessor_id": 6,
+			"provisional": "true"
+		},
+		"status_code": 200
+	},
+	"delete_rubric_assessment": {
+		"method": "DELETE",
+		"endpoint": "courses/4/rubric_associations/4/rubric_assessments/1",
+		"data": {
+			"id": 1,
+			"rubric_id": 1,
+			"rubric_association_id": 4,
+			"score": 5.0,
+			"artifact_type": "Submission",
+			"artifact_id": 3,
+			"artifact_attempt": 2,
+			"assessment_type": "grading",
+			"assessor_id": 6,
+			"provisional": "false"
+		},
+		"status_code": 200
+	},
+	"create_rubric_association": {
+		"method": "POST",
+		"endpoint": "courses/1/rubric_associations/",
+		"data": {
+			"id": 5,
+			"association_type": "Assignment"
+		},
+		"status_code": 200
+	},
 	"update_rubric_association": {
 		"method": "PUT",
 		"endpoint": "courses/1/rubric_associations/4",
diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json
index 557bf330..b3e67233 100644
--- a/tests/fixtures/user.json
+++ b/tests/fixtures/user.json
@@ -183,16 +183,16 @@
 		"status_code": 200
 	},
 	"create_folder": {
-        "method": "POST",
-        "endpoint": "users/1/folders",
-        "data": {
-            "id": 2,
-            "name": "Test String",
-            "locked": false,
-            "hidden": false
-        },
-        "status_code": 200
-    },
+		"method": "POST",
+		"endpoint": "users/1/folders",
+		"data": {
+			"id": 2,
+			"name": "Test String",
+			"locked": false,
+			"hidden": false
+		},
+		"status_code": 200
+	},
 	"edit": {
 		"method": "PUT",
 		"endpoint": "users/1",
@@ -243,39 +243,39 @@
 			"size": 1024
 		},
 		"status_code": 200
-    },
-    "get_file_quota": {
-        "method": "GET",
-        "endpoint": "users/1/files/quota",
-        "data": {
-            "quota": 889234510,
-            "quota_used": 476231098
-        },
-        "status_code": 200
-    },
+	},
+	"get_file_quota": {
+		"method": "GET",
+		"endpoint": "users/1/files/quota",
+		"data": {
+			"quota": 889234510,
+			"quota_used": 476231098
+		},
+		"status_code": 200
+	},
 	"get_folder": {
-        "method": "GET",
-        "endpoint": "users/1/folders/1",
-        "data": {
-            "id": 1,
-            "files_count": 10,
-            "folders_count": 2,
-            "name": "Folder 1",
-            "full_name": "Folder 1"
-        },
-        "status_code": 200
-    },
+		"method": "GET",
+		"endpoint": "users/1/folders/1",
+		"data": {
+			"id": 1,
+			"files_count": 10,
+			"folders_count": 2,
+			"name": "Folder 1",
+			"full_name": "Folder 1"
+		},
+		"status_code": 200
+	},
 	"get_folder_2": {
-        "method": "GET",
-        "endpoint": "users/1/folders/2",
-        "data": {
-            "id": 2,
-            "files_count": 10,
-            "folders_count": 2,
-            "name": "Folder 2",
-            "full_name": "Folder 1"
-        },
-        "status_code": 200
+		"method": "GET",
+		"endpoint": "users/1/folders/2",
+		"data": {
+			"id": 2,
+			"files_count": 10,
+			"folders_count": 2,
+			"name": "Folder 2",
+			"full_name": "Folder 1"
+		},
+		"status_code": 200
 	},
 	"observer_pairing_codes": {
 		"method": "POST",
@@ -479,26 +479,26 @@
 		"status_code": 200
 	},
 	"list_folders": {
-        "method": "GET",
-        "endpoint": "users/1/folders",
-        "data": [
-            {
-                "id": 2,
-                "files_count": 0,
-                "folders_count": 0,
-                "name": "Folder 2",
-                "full_name": "user_files/Folder 2"
-            },
-            {
-                "id": 3,
-                "files_count": 0,
-                "folders_count": 0,
-                "name": "Folder 3",
-                "full_name": "user_files/Folder 3"
-            }
-        ],
-        "status_code": 200
-    },
+		"method": "GET",
+		"endpoint": "users/1/folders",
+		"data": [
+			{
+				"id": 2,
+				"files_count": 0,
+				"folders_count": 0,
+				"name": "Folder 2",
+				"full_name": "user_files/Folder 2"
+			},
+			{
+				"id": 3,
+				"files_count": 0,
+				"folders_count": 0,
+				"name": "Folder 3",
+				"full_name": "user_files/Folder 3"
+			}
+		],
+		"status_code": 200
+	},
 	"merge": {
 		"method": "PUT",
 		"endpoint": "users/1/merge_into/2",
@@ -804,18 +804,22 @@
 		"method": "GET",
 		"endpoint": "search/recipients",
 		"data": [
-  			{
-  				"id": "group_1",
-  				"name": "the group",
-  				"type": "context",
-  				"user_count": 3
-  			},
-  			{
-  				"id": 2,
-  				"name": "greg",
-  				"common_courses": {},
-  				"common_groups": {"1": ["Member"]}
-  			}
+			{
+				"id": "group_1",
+				"name": "the group",
+				"type": "context",
+				"user_count": 3
+			},
+			{
+				"id": 2,
+				"name": "greg",
+				"common_courses": {},
+				"common_groups": {
+					"1": [
+						"Member"
+					]
+				}
+			}
 		],
 		"status_code": 200
 	},
@@ -915,7 +919,9 @@
 						"grading_type": "points",
 						"course_id": 1,
 						"name": "Test assignment",
-						"submission_types": ["online_text_entry"]
+						"submission_types": [
+							"online_text_entry"
+						]
 					}
 				],
 				"courses": [
@@ -996,7 +1002,9 @@
 						"grading_type": "points",
 						"course_id": 1,
 						"name": "Test assignment",
-						"submission_types": ["online_text_entry"]
+						"submission_types": [
+							"online_text_entry"
+						]
 					}
 				],
 				"courses": [
@@ -1132,16 +1140,8 @@
 		"method": "GET",
 		"endpoint": "users/1/features/enabled",
 		"data": [
-			{
-				"name": "feature_one",
-				"display_name": "Feature One",
-				"applies_to": "User"
-			},
-			{
-				"name": "feature_two",
-				"display_name": "Feature Two",
-				"applies_to": "User"
-			}
+			"feature_one",
+			"feature_two"
 		],
 		"status_code": 200
 	},
@@ -1196,7 +1196,10 @@
 			"use_justification": "fair_use",
 			"license": "private",
 			"message": "2 files updated",
-			"file_ids": [1,2]
+			"file_ids": [
+				1,
+				2
+			]
 		},
 		"status_code": 200
 	},
@@ -1205,7 +1208,10 @@
 		"endpoint": "users/1/usage_rights",
 		"data": {
 			"message": "2 files updated",
-			"file_ids": [1,2]
+			"file_ids": [
+				1,
+				2
+			]
 		},
 		"status_code": 200
 	},
@@ -1226,11 +1232,11 @@
 		],
 		"status_code": 200
 	},
-    "resolve_path": {
+	"resolve_path": {
 		"method": "GET",
 		"endpoint": "users/1/folders/by_path/Folder_Level_1/Folder_Level_2/Folder_Level_3",
 		"data": [
-            {
+			{
 				"id": 2,
 				"files_count": 0,
 				"folders_count": 1,
@@ -1261,18 +1267,24 @@
 		],
 		"status_code": 200
 	},
-    "resolve_path_null": {
+	"resolve_path_null": {
 		"method": "GET",
 		"endpoint": "users/1/folders/by_path",
 		"data": [
-            {
+			{
 				"id": 2,
 				"files_count": 0,
 				"folders_count": 1,
 				"name": "my_files",
 				"full_name": "my_files"
 			}
-        ],
+		],
+		"status_code": 200
+	},
+	"terminate_sessions": {
+		"method": "DELETE",
+		"endpoint": "users/1/sessions",
+		"data": "ok",
 		"status_code": 200
 	}
 }
diff --git a/tests/test_account.py b/tests/test_account.py
index ab23f0ac..9a0d04ef 100644
--- a/tests/test_account.py
+++ b/tests/test_account.py
@@ -13,10 +13,12 @@
     Role,
     SSOSettings,
 )
+from canvasapi.account_calendar import AccountCalendar
 from canvasapi.authentication_event import AuthenticationEvent
 from canvasapi.authentication_provider import AuthenticationProvider
 from canvasapi.content_migration import ContentMigration, Migrator
 from canvasapi.course import Course
+from canvasapi.course_event import CourseEvent
 from canvasapi.enrollment import Enrollment
 from canvasapi.enrollment_term import EnrollmentTerm
 from canvasapi.exceptions import CanvasException, RequiredFieldMissing
@@ -291,6 +293,39 @@ def test_delete_report(self, m):
         self.assertTrue(hasattr(report, "status"))
         self.assertEqual(report.status, "deleted")
 
+    # get account calendar
+    def test_get_account_calendar(self, m):
+        register_uris({"account": ["get_account_calendar"]}, m)
+
+        # Check object is of type AccountCalendar
+        account_calendar = self.account.get_account_calendar()
+        self.assertIsInstance(account_calendar, AccountCalendar)
+
+        # Verify contents of object
+        self.assertEqual(account_calendar.id, 10)
+        self.assertEqual(account_calendar.name, "Department of Computer Science")
+
+    # get all account calendars
+    def test_get_all_account_calendars(self, m):
+        register_uris({"account": ["get_all_account_calendars"]}, m)
+
+        # Get paginated list and convert to list
+        account_calendars = self.account.get_all_account_calendars()
+        account_calendars_list = list(account_calendars)
+
+        # Check that list contains objects of type AccountCalendar
+        self.assertEqual(len(account_calendars_list), 2)
+        self.assertIsInstance(account_calendars_list[0], AccountCalendar)
+        self.assertIsInstance(account_calendars_list[1], AccountCalendar)
+
+        # Verify contents of first object
+        self.assertEqual(account_calendars_list[0].id, 1)
+        self.assertEqual(account_calendars_list[0].name, "FSU")
+
+        # Verify contents of second object
+        self.assertEqual(account_calendars_list[1].id, 2)
+        self.assertEqual(account_calendars_list[1].name, "Full Sail")
+
     # get_report
     def test_get_report(self, m):
         required = {"account": ["get_report"]}
@@ -328,6 +363,27 @@ def test_get_reports(self, m):
         self.assertTrue(hasattr(reports_list[0], "id"))
         self.assertIsInstance(str(reports_list[0]), str)
 
+    # get query by account
+    def test_query_audit_by_account(self, m):
+        register_uris({"account": ["query_audit_by_account"]}, m)
+
+        # Get paginated list and convert to list
+        query = self.account.query_audit_by_account()
+        query_list = list(query)
+
+        # Check that list contains objects of type CourseEvent
+        self.assertEqual(len(query_list), 2)
+        self.assertIsInstance(query_list[0], CourseEvent)
+        self.assertIsInstance(query_list[1], CourseEvent)
+
+        # Verify contents of first object
+        self.assertEqual(query_list[0].id, 1)
+        self.assertEqual(query_list[0].name, "UCF")
+
+        # Verify contents of second object
+        self.assertEqual(query_list[1].id, 2)
+        self.assertEqual(query_list[1].name, "FSU")
+
     # get_subaccounts()
     def test_get_subaccounts(self, m):
         required = {"account": ["subaccounts", "subaccounts_page_2"]}
@@ -1123,7 +1179,6 @@ def test_import_outcome_binary(self, m):
         self.assertEqual(outcome_import.data["import_type"], "instructure_csv")
 
     def test_import_outcome_id(self, m):
-
         register_uris({"account": ["import_outcome"]}, m)
 
         outcome_import = self.account.import_outcome(1)
@@ -1224,6 +1279,32 @@ def test_update_global_notification(self, m):
         self.assertTrue(hasattr(updated_notif, "subject"))
         self.assertEqual(updated_notif.subject, "subject")
 
+    # update account calendar visibility
+    def test_update_account_calendar_visibility(self, m):
+        register_uris({"account": ["update_account_calendar_visibility"]}, m)
+
+        # verify object is of type AccountCalendar
+        account_calendar = self.account.update_account_calendar_visibility()
+        self.assertIsInstance(account_calendar, AccountCalendar)
+
+        # verify contents of object
+        self.assertEqual(account_calendar.id, 102)
+        self.assertEqual(account_calendar.name, "Department of Aerospace Engineering")
+        self.assertTrue(account_calendar.visible)
+
+    # update many account calendars' visibility
+    def test_update_many_account_calendars_visibility(self, m):
+        register_uris({"account": ["update_many_account_calendars_visibility"]}, m)
+
+        # verify the returned object is of type AccountCalendar
+        account_calendar = self.account.update_many_account_calendars_visibility()
+        self.assertIsInstance(account_calendar, AccountCalendar)
+
+        # Verify contents of object
+        self.assertEqual(account_calendar.id, 1)
+        self.assertEqual(account_calendar.name, "Biology Department")
+        self.assertTrue(account_calendar.visible)
+
     def test_update_global_notification_missing_field(self, m):
         register_uris({"account": ["update_notification"]}, m)
 
@@ -1335,8 +1416,8 @@ def test_get_enabled_features(self, m):
 
         features = self.account.get_enabled_features()
 
-        self.assertIsInstance(features, PaginatedList)
-        self.assertIsInstance(features[0], Feature)
+        self.assertIsInstance(features, list)
+        self.assertIsInstance(features[0], str)
 
     # get_feature_flag()
     def test_get_feature_flag(self, m):
diff --git a/tests/test_account_calendar.py b/tests/test_account_calendar.py
new file mode 100644
index 00000000..a660f92a
--- /dev/null
+++ b/tests/test_account_calendar.py
@@ -0,0 +1,24 @@
+import unittest
+
+import requests_mock
+
+from canvasapi import Canvas
+from tests import settings
+from tests.util import register_uris
+
+
+@requests_mock.Mocker()
+class TestAccountCalendar(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            register_uris({"account": ["get_account_calendar", "get_by_id"]}, m)
+
+            self.account = self.canvas.get_account(1)
+            self.account_calendar = self.account.get_account_calendar()
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.account_calendar)
+        self.assertIsInstance(string, str)
diff --git a/tests/test_assignment.py b/tests/test_assignment.py
index 28f36deb..8f59ac8d 100644
--- a/tests/test_assignment.py
+++ b/tests/test_assignment.py
@@ -17,7 +17,7 @@
 from canvasapi.peer_review import PeerReview
 from canvasapi.progress import Progress
 from canvasapi.submission import Submission
-from canvasapi.user import UserDisplay
+from canvasapi.user import User, UserDisplay
 from tests import settings
 from tests.util import cleanup_file, register_uris
 
@@ -137,6 +137,16 @@ def test_get_peer_reviews(self, m):
         self.assertEqual(len(peer_review_list), 2)
         self.assertIsInstance(peer_review_list[0], PeerReview)
 
+    # get_students_selected_for_moderation()
+    def test_get_students_selected_for_moderation(self, m):
+        register_uris({"assignment": ["get_students_selected_moderation"]}, m)
+
+        selected_students = self.assignment.get_students_selected_for_moderation()
+        selected_student_list = list(selected_students)
+
+        self.assertEqual(len(selected_student_list), 2)
+        self.assertIsInstance(selected_student_list[0], User)
+
     # get_submission()
     def test_get_submission(self, m):
         register_uris({"submission": ["get_by_id_course"], "user": ["get_by_id"]}, m)
@@ -333,6 +343,18 @@ def test_get_provisional_grades_status(self, m):
         self.assertIsInstance(status, bool)
         self.assertFalse(status)
 
+    # select_students_for_moderation()
+    def test_select_students_for_moderation(self, m):
+        register_uris({"assignment": ["select_students_for_moderation"]}, m)
+
+        selected_students = self.assignment.select_students_for_moderation(
+            student_ids=[11, 12]
+        )
+        selected_student_list = list(selected_students)
+
+        self.assertEqual(len(selected_student_list), 2)
+        self.assertIsInstance(selected_student_list[0], User)
+
     # selected_provisional_grade
     def test_selected_provisional_grade(self, m):
         register_uris({"assignment": ["selected_provisional_grade"]}, m)
diff --git a/tests/test_canvas.py b/tests/test_canvas.py
index 35ece62b..8533565e 100644
--- a/tests/test_canvas.py
+++ b/tests/test_canvas.py
@@ -7,6 +7,7 @@
 
 from canvasapi import Canvas
 from canvasapi.account import Account
+from canvasapi.account_calendar import AccountCalendar
 from canvasapi.appointment_group import AppointmentGroup
 from canvasapi.calendar_event import CalendarEvent
 from canvasapi.comm_message import CommMessage
@@ -18,6 +19,7 @@
 from canvasapi.exceptions import RequiredFieldMissing, ResourceDoesNotExist
 from canvasapi.file import File
 from canvasapi.group import Group, GroupCategory
+from canvasapi.jwt import JWT
 from canvasapi.outcome import Outcome, OutcomeGroup
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.poll import Poll
@@ -95,6 +97,47 @@ def test_create_account(self, m):
         self.assertTrue(hasattr(account, "name"))
         self.assertEqual(account.name, name)
 
+    # test token creation
+    def test_create_jwt(self, m):
+        register_uris({"jwt": ["create_jwt"]}, m)
+
+        # verify returned object is of type JWT
+        jwt = self.canvas.create_jwt()
+        self.assertIsInstance(jwt, JWT)
+
+        # check the token is correct and we received the right object
+        self.assertEqual(jwt.token, "ZjM0UTZmLyVNSjdqb10wLV9jQSxeUiogXUlCWUs7Tg==")
+
+    # refresh token
+    def test_refresh_jwt_str(self, m):
+        register_uris({"jwt": ["refresh_jwt"]}, m)
+
+        old_token = "ZjM0UTZmLyVNSjdqb10wLV9jQSxeUiogXUlCWUs7Tg=="
+        new_token = "O3MzNjpPKWc+fmFfMXRJJiEoR1VbSDVDT1IzUF1IJUpjJ3JSe0lrMHw8OUlX"
+
+        # verify returned object is of type JWT
+        jwt = self.canvas.refresh_jwt(jwt=old_token)
+        self.assertIsInstance(jwt, JWT)
+
+        # check the token is correct and we received the right object
+        self.assertEqual(jwt.token, new_token)
+
+    def test_refresh_jwt_obj(self, m):
+        register_uris({"jwt": ["create_jwt", "refresh_jwt"]}, m)
+
+        old_token = "ZjM0UTZmLyVNSjdqb10wLV9jQSxeUiogXUlCWUs7Tg=="
+        new_token = "O3MzNjpPKWc+fmFfMXRJJiEoR1VbSDVDT1IzUF1IJUpjJ3JSe0lrMHw8OUlX"
+
+        old_jwt = self.canvas.create_jwt()
+        self.assertEqual(old_jwt.token, old_token)
+
+        # verify returned object is of type JWT
+        jwt = self.canvas.refresh_jwt(jwt=old_jwt)
+        self.assertIsInstance(jwt, JWT)
+
+        # check the token is correct and we received the right object
+        self.assertEqual(jwt.token, new_token)
+
     # create_poll()
     def test_create_poll(self, m):
         register_uris({"poll": ["create_poll"]}, m)
@@ -124,6 +167,26 @@ def test_get_account(self, m):
         account_by_obj = self.canvas.get_account(account_by_id)
         self.assertIsInstance(account_by_obj, Account)
 
+    # get account calendars
+    def test_get_account_calendars(self, m):
+        register_uris({"account": ["get_account_calendars"]}, m)
+
+        # Convert to list for further testing
+        account_calendars = self.canvas.get_account_calendars()
+        account_calendars_list = list(account_calendars)
+
+        # Check that list contains objects of type AccountCalendar
+        self.assertEqual(len(account_calendars_list), 2)
+        self.assertIsInstance(account_calendars_list[0], AccountCalendar)
+
+        # Verify contents of first object
+        self.assertEqual(account_calendars_list[0].id, 1)
+        self.assertEqual(account_calendars_list[0].name, "CDL")
+
+        # Verify contents of second object
+        self.assertEqual(account_calendars_list[1].id, 2)
+        self.assertEqual(account_calendars_list[1].name, "DDL")
+
     def test_get_account_sis_id(self, m):
         register_uris({"account": ["get_by_sis_id"]}, m)
 
@@ -791,7 +854,6 @@ def test_get_eportfolio(self, m):
 
     # get_epub_exports()
     def test_get_epub_exports(self, m):
-
         register_uris({"course": ["get_epub_exports"]}, m)
 
         epub_export_list = self.canvas.get_epub_exports()
diff --git a/tests/test_course.py b/tests/test_course.py
index b9a3daef..21b483bb 100644
--- a/tests/test_course.py
+++ b/tests/test_course.py
@@ -13,6 +13,7 @@
 from canvasapi.content_migration import ContentMigration, Migrator
 from canvasapi.course import Course, CourseNickname, LatePolicy, Page
 from canvasapi.course_epub_export import CourseEpubExport
+from canvasapi.course_event import CourseEvent
 from canvasapi.custom_gradebook_columns import CustomGradebookColumn
 from canvasapi.discussion_topic import DiscussionTopic
 from canvasapi.enrollment import Enrollment
@@ -753,6 +754,27 @@ def test_get_discussion_topic(self, m):
         self.assertTrue(hasattr(discussion_by_obj, "course_id"))
         self.assertEqual(discussion_by_obj.course_id, 1)
 
+    # get query by course
+    def test_query_audit_by_course(self, m):
+        register_uris({"course": ["query_audit_by_course"]}, m)
+
+        # Get paginated list and convert to list
+        query = self.course.query_audit_by_course()
+        query_list = list(query)
+
+        # Check that list contains objects of type CourseEvent
+        self.assertEqual(len(query_list), 2)
+        self.assertIsInstance(query_list[0], CourseEvent)
+        self.assertIsInstance(query_list[1], CourseEvent)
+
+        # Verify contents of first object
+        self.assertEqual(query_list[0].id, 1)
+        self.assertEqual(query_list[0].name, "FIU")
+
+        # Verify contents of second object
+        self.assertEqual(query_list[1].id, 2)
+        self.assertEqual(query_list[1].name, "UNF")
+
     # get_file()
     def test_get_file(self, m):
         register_uris({"course": ["get_file"]}, m)
@@ -1542,7 +1564,6 @@ def test_import_outcome_binary(self, m):
         self.assertEqual(outcome_import.data["import_type"], "instructure_csv")
 
     def test_import_outcome_id(self, m):
-
         register_uris({"course": ["import_outcome"]}, m)
 
         outcome_import = self.course.import_outcome(1)
@@ -1666,8 +1687,8 @@ def test_get__enabled_features(self, m):
 
         enabled_features = self.course.get_enabled_features()
 
-        self.assertIsInstance(enabled_features, PaginatedList)
-        self.assertIsInstance(enabled_features[0], Feature)
+        self.assertIsInstance(enabled_features, list)
+        self.assertIsInstance(enabled_features[0], str)
 
     # get_feature_flag()
     def test_get_feature_flag(self, m):
diff --git a/tests/test_course_event.py b/tests/test_course_event.py
new file mode 100644
index 00000000..0c593b43
--- /dev/null
+++ b/tests/test_course_event.py
@@ -0,0 +1,30 @@
+import unittest
+
+import requests_mock
+
+from canvasapi import Canvas
+from tests import settings
+from tests.util import register_uris
+
+
+@requests_mock.Mocker()
+class TestCourseEvent(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            requires = {
+                "account": ["get_by_id", "query_audit_by_account"],
+                "course": ["query_audit_by_course"],
+            }
+            register_uris(requires, m)
+
+            self.account = self.canvas.get_account(1)
+
+            # isolate one of the CourseEvent objects
+            self.query_audit_by_account = self.account.query_audit_by_account()[0]
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.query_audit_by_account)
+        self.assertIsInstance(string, str)
diff --git a/tests/test_enrollment.py b/tests/test_enrollment.py
index 111cf5d0..17b76167 100644
--- a/tests/test_enrollment.py
+++ b/tests/test_enrollment.py
@@ -44,3 +44,21 @@ def test_reactivate(self, m):
         target_enrollment = self.enrollment.reactivate()
 
         self.assertIsInstance(target_enrollment, Enrollment)
+
+    # accept()
+    def test_accept(self, m):
+        register_uris({"enrollment": ["accept"]}, m)
+
+        enrollment_accepted = self.enrollment.accept()
+
+        self.assertIsInstance(enrollment_accepted, bool)
+        self.assertTrue(enrollment_accepted)
+
+    # reject()
+    def test_reject(self, m):
+        register_uris({"enrollment": ["reject"]}, m)
+
+        enrollment_rejected = self.enrollment.reject()
+
+        self.assertIsInstance(enrollment_rejected, bool)
+        self.assertTrue(enrollment_rejected)
diff --git a/tests/test_favorite.py b/tests/test_favorite.py
index 6b30759a..05b6cb9a 100644
--- a/tests/test_favorite.py
+++ b/tests/test_favorite.py
@@ -25,7 +25,6 @@ def setUp(self):
 
     # __str__()
     def test_uncapitalized___str__(self, m):
-
         register_uris(
             {
                 "current_user": [
@@ -46,7 +45,6 @@ def test_uncapitalized___str__(self, m):
         self.assertIsInstance(string, str)
 
     def test_capitalized___str__(self, m):
-
         register_uris(
             {
                 "current_user": [
@@ -68,7 +66,6 @@ def test_capitalized___str__(self, m):
 
     # remove()
     def test_remove_uncapitalized_favorite_course(self, m):
-
         register_uris(
             {
                 "current_user": [
@@ -88,7 +85,6 @@ def test_remove_uncapitalized_favorite_course(self, m):
         self.assertEqual(evnt.context_id, 1)
 
     def test_remove_uncapitalized_favorite_group(self, m):
-
         register_uris(
             {
                 "current_user": [
@@ -108,7 +104,6 @@ def test_remove_uncapitalized_favorite_group(self, m):
         self.assertEqual(evnt.context_id, 1)
 
     def test_remove_capitalized_favorite_course(self, m):
-
         register_uris(
             {
                 "current_user": [
@@ -128,7 +123,6 @@ def test_remove_capitalized_favorite_course(self, m):
         self.assertEqual(evnt.context_id, 1)
 
     def test_remove_capitalized_favorite_group(self, m):
-
         register_uris(
             {
                 "current_user": [
diff --git a/tests/test_file.py b/tests/test_file.py
index ed7a5693..543ca8a5 100644
--- a/tests/test_file.py
+++ b/tests/test_file.py
@@ -55,3 +55,13 @@ def test_contents_file(self, m):
         self.assertEqual(contents, '"Hello there"')
         contents_binary = self.file.get_contents(binary=True)
         self.assertEqual(contents_binary, b'"Hello there"')
+
+    # update()
+    def test_update_file(self, m):
+        register_uris({"file": ["update_file"]}, m)
+
+        updated_file = self.file.update(name="New filename.docx")
+
+        self.assertIsInstance(updated_file, File)
+        self.assertTrue(hasattr(updated_file, "display_name"))
+        self.assertEqual(updated_file.display_name, "New filename.docx")
diff --git a/tests/test_gradebook_history.py b/tests/test_gradebook_history.py
index 6ccf40b0..00bfcce6 100644
--- a/tests/test_gradebook_history.py
+++ b/tests/test_gradebook_history.py
@@ -10,7 +10,6 @@
 @requests_mock.Mocker()
 class TestDay(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
@@ -29,7 +28,6 @@ def test__str__(self, m):
 @requests_mock.Mocker()
 class TestGrader(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
@@ -50,7 +48,6 @@ def test__str__(self, m):
 @requests_mock.Mocker()
 class TestSubmissionHistory(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
@@ -71,7 +68,6 @@ def test__str__(self, m):
 @requests_mock.Mocker()
 class TestSubmissionVersion(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
diff --git a/tests/test_grading_period.py b/tests/test_grading_period.py
index 8a76fcf2..218c69eb 100644
--- a/tests/test_grading_period.py
+++ b/tests/test_grading_period.py
@@ -20,7 +20,6 @@ def setUp(self):
         )
 
     def test_str(self, m):
-
         test_str = str(self.grading_period)
         self.assertIsInstance(test_str, str)
 
diff --git a/tests/test_jwt.py b/tests/test_jwt.py
new file mode 100644
index 00000000..f6e81275
--- /dev/null
+++ b/tests/test_jwt.py
@@ -0,0 +1,23 @@
+import unittest
+
+import requests_mock
+
+from canvasapi import Canvas
+from tests import settings
+from tests.util import register_uris
+
+
+@requests_mock.Mocker()
+class TestJWT(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            register_uris({"jwt": ["create_jwt"]}, m)
+
+            self.jwt = self.canvas.create_jwt()
+
+    # __str__()
+    def test__str__(self, m):
+        string = str(self.jwt)
+        self.assertIsInstance(string, str)
diff --git a/tests/test_outcome_import.py b/tests/test_outcome_import.py
index 1222ea0b..3b563cba 100644
--- a/tests/test_outcome_import.py
+++ b/tests/test_outcome_import.py
@@ -17,6 +17,5 @@ def setUp(self):
         )
 
     def test_str(self, m):
-
         test_str = str(self.outcome_import)
         self.assertIsInstance(test_str, str)
diff --git a/tests/test_page.py b/tests/test_page.py
index 08e3b388..01ac1661 100644
--- a/tests/test_page.py
+++ b/tests/test_page.py
@@ -13,7 +13,6 @@
 @requests_mock.Mocker()
 class TestPage(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
diff --git a/tests/test_rubric.py b/tests/test_rubric.py
index 6735225e..90c09b79 100644
--- a/tests/test_rubric.py
+++ b/tests/test_rubric.py
@@ -3,7 +3,7 @@
 import requests_mock
 
 from canvasapi import Canvas
-from canvasapi.rubric import Rubric, RubricAssociation
+from canvasapi.rubric import Rubric, RubricAssessment, RubricAssociation
 from tests import settings
 from tests.util import register_uris
 
@@ -59,6 +59,52 @@ def test__str__(self, m):
         self.assertIsInstance(string, str)
 
 
+@requests_mock.Mocker()
+class TestRubricAssessment(unittest.TestCase):
+    def setUp(self):
+        self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
+
+        with requests_mock.Mocker() as m:
+            register_uris(
+                {
+                    "course": [
+                        "get_by_id",
+                        "create_rubric_with_association",
+                        "create_rubric_association",
+                    ],
+                    "rubric": ["create_rubric_assessment"],
+                },
+                m,
+            )
+
+            self.course = self.canvas.get_course(1)
+            self.rubric = self.course.create_rubric()
+            self.association = self.course.create_rubric_association()
+            self.assessment = self.association.create_rubric_assessment()
+
+    def test__str__(self, m):
+        string = str(self.assessment)
+        self.assertIsInstance(string, str)
+
+    def test_update(self, m):
+        register_uris({"rubric": ["update_rubric_assessment"]}, m)
+
+        self.assertEqual(self.assessment.provisional, "false")
+
+        updated_assessment = self.assessment.update(provisional=True)
+
+        self.assertEqual(updated_assessment.id, 1)
+        self.assertEqual(updated_assessment.provisional, "true")
+
+    def test_delete(self, m):
+        register_uris({"rubric": ["delete_rubric_assessment"]}, m)
+
+        deleted_assessment = self.assessment.delete()
+
+        self.assertEqual(deleted_assessment.id, 1)
+        self.assertIsInstance(deleted_assessment, RubricAssessment)
+
+
 @requests_mock.Mocker()
 class TestRubricAssociation(unittest.TestCase):
     def setUp(self):
diff --git a/tests/test_scope.py b/tests/test_scope.py
index 2e7b1c45..aea5604d 100644
--- a/tests/test_scope.py
+++ b/tests/test_scope.py
@@ -17,6 +17,5 @@ def setUp(self):
         )
 
     def test_str(self, m):
-
         test_str = str(self.scope)
         self.assertIsInstance(test_str, str)
diff --git a/tests/test_submission.py b/tests/test_submission.py
index cdcb5953..84b7a2dd 100644
--- a/tests/test_submission.py
+++ b/tests/test_submission.py
@@ -14,7 +14,6 @@
 @requests_mock.Mocker()
 class TestSubmission(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
diff --git a/tests/test_user.py b/tests/test_user.py
index 9093b783..84a8b04d 100644
--- a/tests/test_user.py
+++ b/tests/test_user.py
@@ -32,7 +32,6 @@
 @requests_mock.Mocker()
 class TestUser(unittest.TestCase):
     def setUp(self):
-
         self.canvas = Canvas(settings.BASE_URL, settings.API_KEY)
 
         with requests_mock.Mocker() as m:
@@ -551,8 +550,8 @@ def test_get_enabled_features(self, m):
 
         features = self.user.get_enabled_features()
 
-        self.assertIsInstance(features, PaginatedList)
-        self.assertIsInstance(features[0], Feature)
+        self.assertIsInstance(features, list)
+        self.assertIsInstance(features[0], str)
 
     # get_feature_flag()
     def test_get_feature_flag(self, m):
@@ -657,6 +656,12 @@ def test_create_pairing_code(self, m):
         self.assertIsInstance(pairing_code, PairingCode)
         self.assertEqual("abc123", pairing_code.code)
 
+    # terminate_sessions()
+    def test_terminate_sessions(self, m):
+        register_uris({"user": ["terminate_sessions"]}, m)
+        resp = self.user.terminate_sessions()
+        self.assertEqual(resp, "ok")
+
 
 @requests_mock.Mocker()
 class TestUserDisplay(unittest.TestCase):
diff --git a/tests/test_util.py b/tests/test_util.py
index 863d7e10..cd85648b 100644
--- a/tests/test_util.py
+++ b/tests/test_util.py
@@ -92,7 +92,6 @@ def test_is_multivalued_zip(self, m):
 
     # combine_kwargs()
     def test_combine_kwargs_empty(self, m):
-
         result = combine_kwargs()
         self.assertIsInstance(result, list)
         self.assertEqual(len(result), 0)
diff --git a/tests_requirements.txt b/tests_requirements.txt
index 3901adee..2eeab8b0 100644
--- a/tests_requirements.txt
+++ b/tests_requirements.txt
@@ -1,6 +1,6 @@
 -r requirements.txt
 
-black
+black>=23.3.0
 coverage
 flake8
 isort