From 23c341aee1a2ef76a2b20790ca8f2fe5dad935bd Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 1 Mar 2018 12:35:47 -0500 Subject: [PATCH 01/91] Added Content Migrations to Account --- canvasapi/account.py | 81 ++++++++++++++++++++++++++++++++++ canvasapi/content_migration.py | 25 +++++++++++ 2 files changed, 106 insertions(+) create mode 100644 canvasapi/content_migration.py diff --git a/canvasapi/account.py b/canvasapi/account.py index 3a03734b..c636979f 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -58,6 +58,27 @@ def create_account(self, **kwargs): ) return Account(self._requester, response.json()) + def create_content_migration(self, **kwargs): + """ + Create a content migration. + + :calls: `POST /api/v1/accounts/:account_id/content_migrations \ + `_ + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + if not 'migration_type' in kwargs: + raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + + response = self._requester.request( + 'POST', + 'accounts/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + def create_course(self, **kwargs): """ Create a course. @@ -207,6 +228,47 @@ def delete_user(self, user): ) return User(self._requester, response.json()) + def get_content_migration(self, content_migration): + """ + Retrive a Content Migration by its ID + + :calls: `GET /api/v1/accounts:account_id/content_migrations/:id \ + `_ + + :param migration: The object or ID of the course to retrieve. + :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'GET', + 'accounts/{}/content_migrations/{}'.format(self.id,migration_id) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migrations(self): + """ + List Content Migrations that the current account can view or manage. + + :calls: `GET /api/v1/accounts \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + return PaginatedList( + ContentMigration, + self._requester, + 'GET', + 'accounts/{}/content_migrations'.format(self.id) + ) + def get_courses(self, **kwargs): """ Retrieve the list of courses in this account. @@ -288,6 +350,25 @@ def get_index_of_reports(self, report_type): 'accounts/{}/reports/{}'.format(self.id, report_type) ) + def get_migration_systems(self): + """ + Return a list of Migration Systems. + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/migrators \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.Migrator` + """ + from canvasapi.content_migration import Migrator + + return PaginatedList( + Migrator, + self._requester, + 'GET', + 'accounts/{}/content_migrations/migrators'.format(self.id) + ) + def get_reports(self): """ Return a list of reports for the current context. diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py new file mode 100644 index 00000000..11a2ab5d --- /dev/null +++ b/canvasapi/content_migration.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +from six import python_2_unicode_compatible + +from canvasapi.canvas_object import CanvasObject +from canvasapi.grading_standard import GradingStandard +from canvasapi.exceptions import CanvasException, RequiredFieldMissing +from canvasapi.paginated_list import PaginatedList +from canvasapi.rubric import Rubric +from canvasapi.util import combine_kwargs, obj_or_id + +@python_2_unicode_compatible +class ContentMigration(CanvasObject): + def __str__(self): + return "{} {}".format(self.migration_type_title, self.id) + +@python_2_unicode_compatible +class MigrationIssue(CanvasObject): + def __str__(self): + return "{}: {}".format(self.id,self.description) + +@python_2_unicode_compatible +class Migrator(CanvasObject): + def __str__(self): + return "{}".format(self.type) From 7d4c53d3a43d1725dad172d83d08e38276242fbc Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 1 Mar 2018 12:57:50 -0500 Subject: [PATCH 02/91] Added Content Migrations to Courses --- canvasapi/course.py | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/canvasapi/course.py b/canvasapi/course.py index 73c55e6c..75f7a63f 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1829,6 +1829,86 @@ def get_single_grading_standard(self, grading_standard_id, **kwargs): ) return GradingStandard(self._requester, response.json()) + def create_content_migration(self, **kwargs): + """ + Create a content migration. + + :calls: `POST /api/v1/courses/:course_id/content_migrations \ + `_ + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + if not 'migration_type' in kwargs: + raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + + response = self._requester.request( + 'POST', + 'courses/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migration(self, content_migration): + """ + Retrive a Content Migration by its ID + + :calls: `GET /api/v1/courses/:course_id/content_migrations/:id \ + `_ + + :param migration: The object or ID of the course to retrieve. + :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'GET', + 'courses/{}/content_migrations/{}'.format(self.id,migration_id) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migrations(self): + """ + List Content Migrations that the current account can view or manage. + + :calls: `GET /api/v1/courses/:course_id/content_migrations/ \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + return PaginatedList( + ContentMigration, + self._requester, + 'GET', + 'courses/{}/content_migrations'.format(self.id) + ) + + def get_migration_systems(self): + """ + Return a list of Migration Systems. + + :calls: `GET /api/v1/course/:course_id/content_migrations/migrators \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.Migrator` + """ + from canvasapi.content_migration import Migrator + + return PaginatedList( + Migrator, + self._requester, + 'GET', + 'courses/{}/content_migrations/migrators'.format(self.id) + ) @python_2_unicode_compatible class CourseNickname(CanvasObject): From 33b150f3d3282cf4dfd90ffceef2e8d10cbe2a9a Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 1 Mar 2018 12:58:31 -0500 Subject: [PATCH 03/91] Grouped Migration methods together for readability --- canvasapi/account.py | 161 +++++++++++++++++++++---------------------- 1 file changed, 80 insertions(+), 81 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index c636979f..7e916ee4 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -58,27 +58,6 @@ def create_account(self, **kwargs): ) return Account(self._requester, response.json()) - def create_content_migration(self, **kwargs): - """ - Create a content migration. - - :calls: `POST /api/v1/accounts/:account_id/content_migrations \ - `_ - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - if not 'migration_type' in kwargs: - raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") - - response = self._requester.request( - 'POST', - 'accounts/{}/content_migrations'.format(self.id), - _kwargs=combine_kwargs(**kwargs) - ) - return ContentMigration(self._requester, response.json()) - def create_course(self, **kwargs): """ Create a course. @@ -228,47 +207,6 @@ def delete_user(self, user): ) return User(self._requester, response.json()) - def get_content_migration(self, content_migration): - """ - Retrive a Content Migration by its ID - - :calls: `GET /api/v1/accounts:account_id/content_migrations/:id \ - `_ - - :param migration: The object or ID of the course to retrieve. - :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - response = self._requester.request( - 'GET', - 'accounts/{}/content_migrations/{}'.format(self.id,migration_id) - ) - return ContentMigration(self._requester, response.json()) - - def get_content_migrations(self): - """ - List Content Migrations that the current account can view or manage. - - :calls: `GET /api/v1/accounts \ - `_ - - :rtype: :class:`canvasapi.paginated_list.PaginatedList` of - :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - return PaginatedList( - ContentMigration, - self._requester, - 'GET', - 'accounts/{}/content_migrations'.format(self.id) - ) - def get_courses(self, **kwargs): """ Retrieve the list of courses in this account. @@ -350,25 +288,6 @@ def get_index_of_reports(self, report_type): 'accounts/{}/reports/{}'.format(self.id, report_type) ) - def get_migration_systems(self): - """ - Return a list of Migration Systems. - - :calls: `GET /api/v1/accounts/:account_id/content_migrations/migrators \ - `_ - - :rtype: :class:`canvasapi.paginated_list.PaginatedList` of - :class:`canvasapi.content_migration.Migrator` - """ - from canvasapi.content_migration import Migrator - - return PaginatedList( - Migrator, - self._requester, - 'GET', - 'accounts/{}/content_migrations/migrators'.format(self.id) - ) - def get_reports(self): """ Return a list of reports for the current context. @@ -1253,6 +1172,86 @@ def list_rubrics(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def create_content_migration(self, **kwargs): + """ + Create a content migration. + + :calls: `POST /api/v1/accounts/:account_id/content_migrations \ + `_ + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + if not 'migration_type' in kwargs: + raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + + response = self._requester.request( + 'POST', + 'accounts/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migration(self, content_migration): + """ + Retrive a Content Migration by its ID + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/:id \ + `_ + + :param migration: The object or ID of the course to retrieve. + :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'GET', + 'accounts/{}/content_migrations/{}'.format(self.id,migration_id) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migrations(self): + """ + List Content Migrations that the current account can view or manage. + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/ \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + return PaginatedList( + ContentMigration, + self._requester, + 'GET', + 'accounts/{}/content_migrations'.format(self.id) + ) + + def get_migration_systems(self): + """ + Return a list of Migration Systems. + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/migrators \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.Migrator` + """ + from canvasapi.content_migration import Migrator + + return PaginatedList( + Migrator, + self._requester, + 'GET', + 'accounts/{}/content_migrations/migrators'.format(self.id) + ) @python_2_unicode_compatible class AccountNotification(CanvasObject): From d840bab95a5f16470439c30841d7029f61d431fd Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 2 Mar 2018 13:24:07 -0500 Subject: [PATCH 04/91] Added Content Migrations to Groups --- canvasapi/group.py | 81 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/canvasapi/group.py b/canvasapi/group.py index 4c6c80c4..a6403793 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -667,6 +667,86 @@ def list_tabs(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def create_content_migration(self, **kwargs): + """ + Create a content migration. + + :calls: `POST /api/v1/groups/:group_id/content_migrations \ + `_ + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + if not 'migration_type' in kwargs: + raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + + response = self._requester.request( + 'POST', + 'groups/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migration(self, content_migration): + """ + Retrive a Content Migration by its ID + + :calls: `GET /api/v1/groups/:group_id/content_migrations/:id \ + `_ + + :param migration: The object or ID of the course to retrieve. + :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'GET', + 'groups/{}/content_migrations/{}'.format(self.id,migration_id) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migrations(self): + """ + List Content Migrations for this group + + :calls: `GET /api/v1/groups/:group_id/content_migrations/ \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + return PaginatedList( + ContentMigration, + self._requester, + 'GET', + 'groups/{}/content_migrations'.format(self.id) + ) + + def get_migration_systems(self): + """ + Return a list of Migration Systems. + + :calls: `GET /api/v1/group/:group_id/content_migrations/migrators \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.Migrator` + """ + from canvasapi.content_migration import Migrator + + return PaginatedList( + Migrator, + self._requester, + 'GET', + 'groups/{}/content_migrations/migrators'.format(self.id) + ) @python_2_unicode_compatible class GroupMembership(CanvasObject): @@ -730,7 +810,6 @@ def remove_self(self): ) return response.json() - @python_2_unicode_compatible class GroupCategory(CanvasObject): From 4bf4b2d0e612216496ea59c07e348e1bcdef1453 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 2 Mar 2018 13:24:32 -0500 Subject: [PATCH 05/91] Added Content Migrations to Users --- canvasapi/user.py | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/canvasapi/user.py b/canvasapi/user.py index a91a32f3..dce4f479 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -544,6 +544,89 @@ def remove_observee(self, observee_id): ) return User(self._requester, response.json()) + def create_content_migration(self, **kwargs): + """ + Create a content migration. + + :calls: `POST /api/v1/users/:user_id/content_migrations \ + `_ + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + if not 'migration_type' in kwargs: + raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + + print(combine_kwargs(**kwargs)) + + response = self._requester.request( + 'POST', + 'users/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migration(self, content_migration): + """ + Retrive a Content Migration by its ID + + :calls: `GET /api/v1/users/:user_id/content_migrations/:id \ + `_ + + :param migration: The object or ID of the course to retrieve. + :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'GET', + 'users/{}/content_migrations/{}'.format(self.id,migration_id) + ) + return ContentMigration(self._requester, response.json()) + + def get_content_migrations(self): + """ + List Content Migrations for this group + + :calls: `GET /api/v1/users/:user_id/content_migrations/ \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + return PaginatedList( + ContentMigration, + self._requester, + 'GET', + 'users/{}/content_migrations'.format(self.id) + ) + + def get_migration_systems(self): + """ + Return a list of Migration Systems. + + :calls: `GET /api/v1/user/:user_id/content_migrations/migrators \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.Migrator` + """ + from canvasapi.content_migration import Migrator + + return PaginatedList( + Migrator, + self._requester, + 'GET', + 'users/{}/content_migrations/migrators'.format(self.id) + ) + @python_2_unicode_compatible class UserDisplay(CanvasObject): From 806afc0eb8d872b40306c8ac5c5ba8104a3e9f99 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 2 Mar 2018 13:32:05 -0500 Subject: [PATCH 06/91] Added ability to update Content Migration --- canvasapi/account.py | 25 ++++++++++++++++++++++++- canvasapi/course.py | 27 +++++++++++++++++++++++++-- canvasapi/group.py | 25 ++++++++++++++++++++++++- canvasapi/user.py | 22 ++++++++++++++++++++++ 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 7e916ee4..f478b455 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1234,7 +1234,7 @@ def get_content_migrations(self): 'accounts/{}/content_migrations'.format(self.id) ) - def get_migration_systems(self): + def list_migration_systems(self): """ Return a list of Migration Systems. @@ -1253,6 +1253,29 @@ def get_migration_systems(self): 'accounts/{}/content_migrations/migrators'.format(self.id) ) + def update_content_migration(self, content_migration, **kwargs): + """ + Update a Content Migration. + + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id \ + `_ + + :param content_migration: The object or ID of the Content Migration. + :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'PUT', + 'accounts/{}/content_migrations/{}'.format(self.id, content_migration_id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + @python_2_unicode_compatible class AccountNotification(CanvasObject): diff --git a/canvasapi/course.py b/canvasapi/course.py index 75f7a63f..c5117912 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1874,7 +1874,7 @@ def get_content_migration(self, content_migration): def get_content_migrations(self): """ - List Content Migrations that the current account can view or manage. + List Content Migrations for this course :calls: `GET /api/v1/courses/:course_id/content_migrations/ \ `_ @@ -1891,7 +1891,7 @@ def get_content_migrations(self): 'courses/{}/content_migrations'.format(self.id) ) - def get_migration_systems(self): + def list_migration_systems(self): """ Return a list of Migration Systems. @@ -1910,6 +1910,29 @@ def get_migration_systems(self): 'courses/{}/content_migrations/migrators'.format(self.id) ) + def update_content_migration(self, content_migration, **kwargs): + """ + Update a Content Migration. + + :calls: `PUT /api/v1/courses/:course_id/content_migrations/:id \ + `_ + + :param content_migration: The object or ID of the Content Migration. + :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'PUT', + 'courses/{}/content_migrations/{}'.format(self.id, content_migration_id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + @python_2_unicode_compatible class CourseNickname(CanvasObject): diff --git a/canvasapi/group.py b/canvasapi/group.py index a6403793..047c0ff9 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -729,7 +729,7 @@ def get_content_migrations(self): 'groups/{}/content_migrations'.format(self.id) ) - def get_migration_systems(self): + def list_migration_systems(self): """ Return a list of Migration Systems. @@ -748,6 +748,29 @@ def get_migration_systems(self): 'groups/{}/content_migrations/migrators'.format(self.id) ) + def update_content_migration(self, content_migration, **kwargs): + """ + Update a Content Migration. + + :calls: `PUT /api/v1/groups/:group_id/content_migrations/:id \ + `_ + + :param content_migration: The object or ID of the Content Migration. + :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'PUT', + 'groups/{}/content_migrations/{}'.format(self.id, content_migration_id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) + @python_2_unicode_compatible class GroupMembership(CanvasObject): diff --git a/canvasapi/user.py b/canvasapi/user.py index dce4f479..4ed29ab8 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -627,6 +627,28 @@ def get_migration_systems(self): 'users/{}/content_migrations/migrators'.format(self.id) ) + def update_content_migration(self, content_migration, **kwargs): + """ + Update a Content Migration. + + :calls: `PUT /api/v1/users/:user_id/content_migrations/:id \ + `_ + + :param content_migration: The object or ID of the Content Migration. + :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int + + :rtype: :class:`canvasapi.content_migration.ContentMigration` + """ + from canvasapi.content_migration import ContentMigration + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + response = self._requester.request( + 'PUT', + 'users/{}/content_migrations/{}'.format(self.id, content_migration_id), + _kwargs=combine_kwargs(**kwargs) + ) + return ContentMigration(self._requester, response.json()) @python_2_unicode_compatible class UserDisplay(CanvasObject): From 4f2cc0c0e23a55a4083a6ef8deda152d396a4831 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 2 Mar 2018 14:27:31 -0500 Subject: [PATCH 07/91] Added Migration Issues to Account --- canvasapi/account.py | 72 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index f478b455..2b3809e3 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1215,7 +1215,28 @@ def get_content_migration(self, content_migration): ) return ContentMigration(self._requester, response.json()) - def get_content_migrations(self): + def get_migration_issue(self,content_migration,migration_issue): + """ + List Content Migrations that the current account can view or manage. + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + :rtype: :class:`canvasapi.content_migration.MigrationIssue` + """ + from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import MigrationIssue + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) + + response = self._requester.request( + 'GET', + 'accounts/{}/content_migrations/{}/migration_issues/{}'.format(self.id,content_migration_id,migration_issue_id) + ) + return MigrationIssue(self._requester, response.json()) + + def list_content_migrations(self): """ List Content Migrations that the current account can view or manage. @@ -1234,6 +1255,28 @@ def get_content_migrations(self): 'accounts/{}/content_migrations'.format(self.id) ) + def list_migration_issues(self,content_migration): + """ + List Migration Issues for a Content Migration + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.content_migration.MigrationIssue` + """ + from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import MigrationIssue + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + + return PaginatedList( + MigrationIssue, + self._requester, + 'GET', + 'accounts/{}/content_migrations/{}/migration_issues'.format(self.id,content_migration_id) + ) + def list_migration_systems(self): """ Return a list of Migration Systems. @@ -1265,7 +1308,7 @@ def update_content_migration(self, content_migration, **kwargs): :rtype: :class:`canvasapi.content_migration.ContentMigration` """ - from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import Content content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) @@ -1276,6 +1319,31 @@ def update_content_migration(self, content_migration, **kwargs): ) return ContentMigration(self._requester, response.json()) + def update_migration_issue(self,content_migration,migration_issue,**kwargs): + """ + Update a Migration Issue within a specific content migration + + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + :rtype: :class:`canvasapi.content_migration.MigrationIssue` + """ + from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import MigrationIssue + + content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) + migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) + + if not 'workflow_state' in kwargs: + raise RequiredFieldMissing("Parameter with key 'workflow_state' is required.") + + response = self._requester.request( + 'PUT', + 'accounts/{}/content_migrations/{}/migration_issues/{}'.format(self.id,content_migration_id,migration_issue_id), + _kwargs=combine_kwargs(**kwargs) + ) + return MigrationIssue(self._requester, response.json()) + @python_2_unicode_compatible class AccountNotification(CanvasObject): From 45d57ce62d46774bbf9bb7359fe4a1af4c487175 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Mon, 5 Mar 2018 16:11:10 -0500 Subject: [PATCH 08/91] Reformatted methods to fix API workaround --- canvasapi/account.py | 119 ++++---------------- canvasapi/content_migration.py | 200 ++++++++++++++++++++++++++++++++- canvasapi/course.py | 57 ++++------ canvasapi/group.py | 57 ++++------ canvasapi/user.py | 59 ++++------ 5 files changed, 283 insertions(+), 209 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 2b3809e3..2b252833 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1191,11 +1191,15 @@ def create_content_migration(self, **kwargs): 'accounts/{}/content_migrations'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migration(self, content_migration): + response_json = response.json() + response_json.update({'account_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migration(self, content_migration, **kwargs): """ - Retrive a Content Migration by its ID + Retrive a content migration by its ID :calls: `GET /api/v1/accounts/:account_id/content_migrations/:id \ `_ @@ -1211,34 +1215,18 @@ def get_content_migration(self, content_migration): response = self._requester.request( 'GET', - 'accounts/{}/content_migrations/{}'.format(self.id,migration_id) + 'accounts/{}/content_migrations/{}'.format(self.id,migration_id), + _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_migration_issue(self,content_migration,migration_issue): - """ - List Content Migrations that the current account can view or manage. + response_json = response.json() + response_json.update({'account_id': self.id}) - :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ + return ContentMigration(self._requester, response_json) - :rtype: :class:`canvasapi.content_migration.MigrationIssue` + def get_content_migrations(self, **kwargs): """ - from canvasapi.content_migration import ContentMigration - from canvasapi.content_migration import MigrationIssue - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) - - response = self._requester.request( - 'GET', - 'accounts/{}/content_migrations/{}/migration_issues/{}'.format(self.id,content_migration_id,migration_issue_id) - ) - return MigrationIssue(self._requester, response.json()) - - def list_content_migrations(self): - """ - List Content Migrations that the current account can view or manage. + List content migrations that the current account can view or manage. :calls: `GET /api/v1/accounts/:account_id/content_migrations/ \ `_ @@ -1252,34 +1240,13 @@ def list_content_migrations(self): ContentMigration, self._requester, 'GET', - 'accounts/{}/content_migrations'.format(self.id) - ) - - def list_migration_issues(self,content_migration): - """ - List Migration Issues for a Content Migration - - :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ - `_ - - :rtype: :class:`canvasapi.paginated_list.PaginatedList` of - :class:`canvasapi.content_migration.MigrationIssue` - """ - from canvasapi.content_migration import ContentMigration - from canvasapi.content_migration import MigrationIssue - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - return PaginatedList( - MigrationIssue, - self._requester, - 'GET', - 'accounts/{}/content_migrations/{}/migration_issues'.format(self.id,content_migration_id) + 'accounts/{}/content_migrations'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) - def list_migration_systems(self): + def get_migration_systems(self, **kwargs): """ - Return a list of Migration Systems. + Return a list of migration systems. :calls: `GET /api/v1/accounts/:account_id/content_migrations/migrators \ `_ @@ -1293,56 +1260,10 @@ def list_migration_systems(self): Migrator, self._requester, 'GET', - 'accounts/{}/content_migrations/migrators'.format(self.id) - ) - - def update_content_migration(self, content_migration, **kwargs): - """ - Update a Content Migration. - - :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id \ - `_ - - :param content_migration: The object or ID of the Content Migration. - :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import Content - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - response = self._requester.request( - 'PUT', - 'accounts/{}/content_migrations/{}'.format(self.id, content_migration_id), - _kwargs=combine_kwargs(**kwargs) - ) - return ContentMigration(self._requester, response.json()) - - def update_migration_issue(self,content_migration,migration_issue,**kwargs): - """ - Update a Migration Issue within a specific content migration - - :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - :rtype: :class:`canvasapi.content_migration.MigrationIssue` - """ - from canvasapi.content_migration import ContentMigration - from canvasapi.content_migration import MigrationIssue - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) - - if not 'workflow_state' in kwargs: - raise RequiredFieldMissing("Parameter with key 'workflow_state' is required.") - - response = self._requester.request( - 'PUT', - 'accounts/{}/content_migrations/{}/migration_issues/{}'.format(self.id,content_migration_id,migration_issue_id), + 'accounts/{}/content_migrations/migrators'.format(self.id), + {'account_id': self.id}, _kwargs=combine_kwargs(**kwargs) ) - return MigrationIssue(self._requester, response.json()) @python_2_unicode_compatible class AccountNotification(CanvasObject): diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 11a2ab5d..1e7a79eb 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -11,15 +11,203 @@ @python_2_unicode_compatible class ContentMigration(CanvasObject): - def __str__(self): - return "{} {}".format(self.migration_type_title, self.id) + def __str__(self): + return "{} {}".format(self.migration_type_title, self.id) + + @property + def _parent_id(self): + """ + Return the id of the course or group that spawned this content migration. + + :rtype: int + """ + if hasattr(self, 'course_id'): + return self.course_id + elif hasattr(self, 'group_id'): + return self.group_id + elif hasattr(self, 'account_id'): + return self.account_id + elif hasattr(self, 'user_id'): + return self.user_id + else: + raise ValueError("Content Migration does not have an account_id, course_id, group_id or user_id") + + @property + def _parent_type(self): + """ + Return whether the content migration was spawned from a course or group. + + :rtype: str + """ + if hasattr(self, 'course_id'): + return 'course' + elif hasattr(self, 'group_id'): + return 'group' + elif hasattr(self, 'account_id'): + return 'account' + elif hasattr(self, 'user_id'): + return 'user' + else: + raise ValueError("Content Migration does not have an account_id, course_id, group_id or user_id") + + def get_parent(self, **kwargs): + """ + Return the object that spawned this content migration. + + :rtype: :class:`canvasapi.group.Account`, :class:`canvasapi.course.Course`, :class:`canvasapi.course.Group`, or :class:`canvasapi.course.User` + """ + from canvasapi.group import Group + from canvasapi.course import Course + from canvasapi.account import Account + from canvasapi.user import User + + response = self._requester.request( + 'GET', + '{}s/{}'.format(self._parent_type, self._parent_id), + _kwargs=combine_kwargs(**kwargs) + ) + + if self._parent_type == 'group': + return Group(self._requester, response.json()) + elif self._parent_type == 'course': + return Course(self._requester, response.json()) + elif self._parent_type == 'account': + return Account(self._requester, response.json()) + elif self._parent_type == 'user': + return User(self._requester, response.json()) + + def get_migration_issue(self,migration_issue, **kwargs): + """ + List a single issue for this content migration + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + :rtype: :class:`canvasapi.content_migration.MigrationIssue` + """ + from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import MigrationIssue + + migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) + + response = self._requester.request( + 'GET', + '{}s/{}/content_migrations/{}/migration_issues/{}'.format(self._parent_type, self._parent_id,self.id,migration_issue_id), + _kwargs=combine_kwargs(**kwargs) + ) + + response_json = response.json() + response_json.update({'context_type':self._parent_type, 'context_id':self._parent_id, 'content_migration_id':self.id}) + + return MigrationIssue(self._requester, response_json) + + + def get_migration_issues(self, **kwargs): + """ + List issues for this content migration + + :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + `_ + + :rtype: :class:`canvasapi.content_migration.MigrationIssue` + """ + from canvasapi.content_migration import MigrationIssue + + return PaginatedList( + MigrationIssue, + self._requester, + 'GET', + '{}s/{}/content_migrations/{}/migration_issues/'.format(self._parent_type, self._parent_id,self.id), + {'context_type':self._parent_type, 'context_id':self._parent_id, 'content_migration_id':self.id}, + _kwargs=combine_kwargs(**kwargs) + ) + + def update(self, **kwargs): + """ + Update an existing content migration. + + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id \ + `_ + + or `PUT /api/v1/courses/:course_id/content_migrations/:id \ + `_ + + or `PUT /api/v1/groups/:group_id/content_migrations/:id \ + `_ + + or `PUT /api/v1/users/:user_id/content_migrations/:id \ + `_ + + :returns: True if the migration was updated, False otherwise. + :rtype: bool + """ + response = self._requester.request( + 'PUT', + '{}s/{}/content_migrations/{}'.format(self._parent_type, self._parent_id,self.id), + _kwargs=combine_kwargs(**kwargs) + ) + + if 'migration_type' in response.json(): + super(ContentMigration, self).set_attributes(response.json()) + return True + else: + return False @python_2_unicode_compatible class MigrationIssue(CanvasObject): - def __str__(self): - return "{}: {}".format(self.id,self.description) + def __str__(self): + return "{}: {}".format(self.id,self.description) + + def update(self,**kwargs): + """ + Update an existing migration issue. + + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + or `PUT /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + or `PUT /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + or `PUT /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id \ + `_ + + :returns: True if the issue was updated, False otherwise. + :rtype: bool + """ + response = self._requester.request( + 'PUT', + '{}s/{}/content_migrations/{}/migration_issues/{}'.format(self.context_type, self.context_id, self.content_migration_id, self.id), + _kwargs=combine_kwargs(**kwargs) + ) + + if 'workflow_state' in response.json(): + super(MigrationIssue, self).set_attributes(response.json()) + return True + else: + return False @python_2_unicode_compatible class Migrator(CanvasObject): - def __str__(self): - return "{}".format(self.type) + def __str__(self): + return "{}".format(self.type) diff --git a/canvasapi/course.py b/canvasapi/course.py index c5117912..7e222f78 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1848,11 +1848,15 @@ def create_content_migration(self, **kwargs): 'courses/{}/content_migrations'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migration(self, content_migration): + response_json = response.json() + response_json.update({'course_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migration(self, content_migration, **kwargs): """ - Retrive a Content Migration by its ID + Retrive a content migration by its ID :calls: `GET /api/v1/courses/:course_id/content_migrations/:id \ `_ @@ -1868,13 +1872,18 @@ def get_content_migration(self, content_migration): response = self._requester.request( 'GET', - 'courses/{}/content_migrations/{}'.format(self.id,migration_id) + 'courses/{}/content_migrations/{}'.format(self.id,migration_id), + _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migrations(self): + response_json = response.json() + response_json.update({'course_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migrations(self, **kwargs): """ - List Content Migrations for this course + List content migrations that the current account can view or manage. :calls: `GET /api/v1/courses/:course_id/content_migrations/ \ `_ @@ -1888,14 +1897,16 @@ def get_content_migrations(self): ContentMigration, self._requester, 'GET', - 'courses/{}/content_migrations'.format(self.id) + 'courses/{}/content_migrations'.format(self.id), + {'course_id': self.id}, + _kwargs=combine_kwargs(**kwargs) ) - def list_migration_systems(self): + def get_migration_systems(self, **kwargs): """ - Return a list of Migration Systems. + Return a list of migration systems. - :calls: `GET /api/v1/course/:course_id/content_migrations/migrators \ + :calls: `GET /api/v1/courses/:course_id/content_migrations/migrators \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -1907,31 +1918,9 @@ def list_migration_systems(self): Migrator, self._requester, 'GET', - 'courses/{}/content_migrations/migrators'.format(self.id) - ) - - def update_content_migration(self, content_migration, **kwargs): - """ - Update a Content Migration. - - :calls: `PUT /api/v1/courses/:course_id/content_migrations/:id \ - `_ - - :param content_migration: The object or ID of the Content Migration. - :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - response = self._requester.request( - 'PUT', - 'courses/{}/content_migrations/{}'.format(self.id, content_migration_id), + 'courses/{}/content_migrations/migrators'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) @python_2_unicode_compatible class CourseNickname(CanvasObject): diff --git a/canvasapi/group.py b/canvasapi/group.py index 047c0ff9..b189e3c2 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -686,11 +686,15 @@ def create_content_migration(self, **kwargs): 'groups/{}/content_migrations'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migration(self, content_migration): + response_json = response.json() + response_json.update({'group_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migration(self, content_migration, **kwargs): """ - Retrive a Content Migration by its ID + Retrive a content migration by its ID :calls: `GET /api/v1/groups/:group_id/content_migrations/:id \ `_ @@ -706,13 +710,18 @@ def get_content_migration(self, content_migration): response = self._requester.request( 'GET', - 'groups/{}/content_migrations/{}'.format(self.id,migration_id) + 'groups/{}/content_migrations/{}'.format(self.id,migration_id), + _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migrations(self): + response_json = response.json() + response_json.update({'group_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migrations(self, **kwargs): """ - List Content Migrations for this group + List content migrations that the current account can view or manage. :calls: `GET /api/v1/groups/:group_id/content_migrations/ \ `_ @@ -726,14 +735,16 @@ def get_content_migrations(self): ContentMigration, self._requester, 'GET', - 'groups/{}/content_migrations'.format(self.id) + 'groups/{}/content_migrations'.format(self.id), + {'group_id': self.id}, + _kwargs=combine_kwargs(**kwargs) ) - def list_migration_systems(self): + def get_migration_systems(self, **kwargs): """ - Return a list of Migration Systems. + Return a list of migration systems. - :calls: `GET /api/v1/group/:group_id/content_migrations/migrators \ + :calls: `GET /api/v1/groups/:group_id/content_migrations/migrators \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -745,31 +756,9 @@ def list_migration_systems(self): Migrator, self._requester, 'GET', - 'groups/{}/content_migrations/migrators'.format(self.id) - ) - - def update_content_migration(self, content_migration, **kwargs): - """ - Update a Content Migration. - - :calls: `PUT /api/v1/groups/:group_id/content_migrations/:id \ - `_ - - :param content_migration: The object or ID of the Content Migration. - :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - response = self._requester.request( - 'PUT', - 'groups/{}/content_migrations/{}'.format(self.id, content_migration_id), + 'groups/{}/content_migrations/migrators'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) @python_2_unicode_compatible class GroupMembership(CanvasObject): diff --git a/canvasapi/user.py b/canvasapi/user.py index 4ed29ab8..276c13b3 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -558,18 +558,20 @@ def create_content_migration(self, **kwargs): if not 'migration_type' in kwargs: raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") - print(combine_kwargs(**kwargs)) - response = self._requester.request( 'POST', 'users/{}/content_migrations'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migration(self, content_migration): + response_json = response.json() + response_json.update({'user_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migration(self, content_migration, **kwargs): """ - Retrive a Content Migration by its ID + Retrive a content migration by its ID :calls: `GET /api/v1/users/:user_id/content_migrations/:id \ `_ @@ -585,13 +587,18 @@ def get_content_migration(self, content_migration): response = self._requester.request( 'GET', - 'users/{}/content_migrations/{}'.format(self.id,migration_id) + 'users/{}/content_migrations/{}'.format(self.id,migration_id), + _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) - def get_content_migrations(self): + response_json = response.json() + response_json.update({'user_id': self.id}) + + return ContentMigration(self._requester, response_json) + + def get_content_migrations(self, **kwargs): """ - List Content Migrations for this group + List content migrations that the current account can view or manage. :calls: `GET /api/v1/users/:user_id/content_migrations/ \ `_ @@ -605,14 +612,16 @@ def get_content_migrations(self): ContentMigration, self._requester, 'GET', - 'users/{}/content_migrations'.format(self.id) + 'users/{}/content_migrations'.format(self.id), + {'user_id': self.id}, + _kwargs=combine_kwargs(**kwargs) ) - def get_migration_systems(self): + def get_migration_systems(self, **kwargs): """ - Return a list of Migration Systems. + Return a list of migration systems. - :calls: `GET /api/v1/user/:user_id/content_migrations/migrators \ + :calls: `GET /api/v1/users/:user_id/content_migrations/migrators \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -624,31 +633,9 @@ def get_migration_systems(self): Migrator, self._requester, 'GET', - 'users/{}/content_migrations/migrators'.format(self.id) - ) - - def update_content_migration(self, content_migration, **kwargs): - """ - Update a Content Migration. - - :calls: `PUT /api/v1/users/:user_id/content_migrations/:id \ - `_ - - :param content_migration: The object or ID of the Content Migration. - :type content_migration: :class:`canvasapi.content_migration.ContentMigration` or int - - :rtype: :class:`canvasapi.content_migration.ContentMigration` - """ - from canvasapi.content_migration import ContentMigration - - content_migration_id = obj_or_id(content_migration, "content_migration", (ContentMigration,)) - - response = self._requester.request( - 'PUT', - 'users/{}/content_migrations/{}'.format(self.id, content_migration_id), + 'users/{}/content_migrations/migrators'.format(self.id), _kwargs=combine_kwargs(**kwargs) ) - return ContentMigration(self._requester, response.json()) @python_2_unicode_compatible class UserDisplay(CanvasObject): From 1564aafdfe431c02c5337ff7969c18d302d277aa Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Mon, 5 Mar 2018 17:27:13 -0500 Subject: [PATCH 09/91] Added unit tests for accounts.py --- tests/fixtures/account.json | 54 ++++++++++++++++++++++++++++++++++++ tests/test_account.py | 55 +++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/tests/fixtures/account.json b/tests/fixtures/account.json index e12a3e64..912aeb8f 100644 --- a/tests/fixtures/account.json +++ b/tests/fixtures/account.json @@ -1118,5 +1118,59 @@ ] }, "status_code": 200 + }, + "create_content_migration": { + "method": "POST", + "endpoint": "accounts/1/content_migrations", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_single": { + "method": "GET", + "endpoint": "accounts/1/content_migrations/1", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_multiple": { + "method": "GET", + "endpoint": "accounts/1/content_migrations", + "data": [ + { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + { + "id": 2, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + } + ], + "status_code": 200 + }, + "get_migration_systems_multiple": { + "method": "GET", + "endpoint": "accounts/1/content_migrations/migrators", + "data": [ + { + "type": "dummy_importer", + "requires_file_upload": true, + "name": "Dummy Importer 01" + }, + { + "type": "dummy_importer_02", + "requires_file_upload": false, + "name": "Dummy Importer 02" + } + ], + "status_code": 200 } } diff --git a/tests/test_account.py b/tests/test_account.py index cdcd25f4..20fa8256 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -19,6 +19,7 @@ from canvasapi.outcome import OutcomeGroup, OutcomeLink from canvasapi.rubric import Rubric from canvasapi.user import User +from canvasapi.content_migration import ContentMigration, Migrator from tests import settings from tests.util import register_uris @@ -733,3 +734,57 @@ def test_list_rubrics(self, m): self.assertIsInstance(rubrics[1], Rubric) self.assertEqual(rubrics[1].id, 2) self.assertEqual(rubrics[1].title, "Account Rubric 2") + + # create_content_migration + def test_create_content_migration(self, m): + register_uris({'account': ['create_content_migration']}, m) + + content_migration = self.account.create_content_migration(migration_type='dummy_importer') + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # create_content_migration without type + def test_create_course_migration_missing_type(self, m): + with self.assertRaises(RequiredFieldMissing): + self.account.create_content_migration() + + # get_content_migration + def test_get_content_migration(self, m): + register_uris({'account': ['get_content_migration_single']}, m) + + content_migration = self.account.get_content_migration(1) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # get_content_migrations + def test_get_content_migrations(self, m): + register_uris({'account': ['get_content_migration_multiple']}, m) + + content_migrations = self.account.get_content_migrations() + + self.assertEqual(len(list(content_migrations)), 2) + + self.assertIsInstance(content_migrations[0], ContentMigration) + self.assertEqual(content_migrations[0].id, 1) + self.assertEqual(content_migrations[0].migration_type, "dummy_importer") + self.assertIsInstance(content_migrations[1], ContentMigration) + self.assertEqual(content_migrations[1].id, 2) + self.assertEqual(content_migrations[1].migration_type, "dummy_importer") + + def test_get_migration_systems(self, m): + register_uris({'account': ['get_migration_systems_multiple']}, m) + + migration_systems = self.account.get_migration_systems() + + self.assertEqual(len(list(migration_systems)), 2) + + self.assertIsInstance(migration_systems[0], Migrator) + self.assertEqual(migration_systems[0].type, "dummy_importer") + self.assertEqual(migration_systems[0].requires_file_upload, True) + self.assertEqual(migration_systems[0].name, "Dummy Importer 01") + self.assertIsInstance(migration_systems[1], Migrator) + self.assertEqual(migration_systems[1].type, "dummy_importer_02") + self.assertEqual(migration_systems[1].requires_file_upload, False) + self.assertEqual(migration_systems[1].name, "Dummy Importer 02") From 34db565230343357c165f098a3d6de0ec8212386 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Mon, 5 Mar 2018 17:40:31 -0500 Subject: [PATCH 10/91] Added Content Migration unit tests to course --- tests/fixtures/course.json | 56 ++++++++++++++++++++++++++++++++++++- tests/test_course.py | 57 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json index 59b2fe9b..94036fe7 100644 --- a/tests/fixtures/course.json +++ b/tests/fixtures/course.json @@ -1060,7 +1060,7 @@ "method": "GET", "endpoint": "courses/1/analytics/activity", "data": [ - { + { "date": "2012-01-24", "participations": 3, "views": 10 @@ -1403,5 +1403,59 @@ ] }, "status_code": 200 + }, + "create_content_migration": { + "method": "POST", + "endpoint": "courses/1/content_migrations", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_single": { + "method": "GET", + "endpoint": "courses/1/content_migrations/1", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_multiple": { + "method": "GET", + "endpoint": "courses/1/content_migrations", + "data": [ + { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + { + "id": 2, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + } + ], + "status_code": 200 + }, + "get_migration_systems_multiple": { + "method": "GET", + "endpoint": "courses/1/content_migrations/migrators", + "data": [ + { + "type": "dummy_importer", + "requires_file_upload": true, + "name": "Dummy Importer 01" + }, + { + "type": "dummy_importer_02", + "requires_file_upload": false, + "name": "Dummy Importer 02" + } + ], + "status_code": 200 } } diff --git a/tests/test_course.py b/tests/test_course.py index b9fda7fe..bf77384d 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -29,6 +29,7 @@ from canvasapi.tab import Tab from canvasapi.user import User from canvasapi.user import UserDisplay +from canvasapi.content_migration import ContentMigration, Migrator from tests import settings from tests.util import cleanup_file, register_uris @@ -1183,6 +1184,62 @@ def test_get_single_grading_standard(self, m): self.assertEqual(response.grading_scheme[0].get('name'), "A") self.assertEqual(response.grading_scheme[0].get('value'), 0.9) + # create_content_migration + def test_create_content_migration(self, m): + register_uris({'course': ['create_content_migration']}, m) + + content_migration = self.course.create_content_migration(migration_type='dummy_importer') + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # create_content_migration without type + def test_create_course_migration_missing_type(self, m): + with self.assertRaises(RequiredFieldMissing): + self.course.create_content_migration() + + # get_content_migration + def test_get_content_migration(self, m): + register_uris({'course': ['get_content_migration_single']}, m) + + content_migration = self.course.get_content_migration(1) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # get_content_migrations + def test_get_content_migrations(self, m): + register_uris({'course': ['get_content_migration_multiple']}, m) + + content_migrations = self.course.get_content_migrations() + + self.assertEqual(len(list(content_migrations)), 2) + + self.assertIsInstance(content_migrations[0], ContentMigration) + self.assertEqual(content_migrations[0].id, 1) + self.assertEqual(content_migrations[0].migration_type, "dummy_importer") + self.assertIsInstance(content_migrations[1], ContentMigration) + self.assertEqual(content_migrations[1].id, 2) + self.assertEqual(content_migrations[1].migration_type, "dummy_importer") + + # get_migration_systems + def test_get_migration_systems(self, m): + register_uris({'course': ['get_migration_systems_multiple']}, m) + + migration_systems = self.course.get_migration_systems() + + self.assertEqual(len(list(migration_systems)), 2) + + self.assertIsInstance(migration_systems[0], Migrator) + self.assertEqual(migration_systems[0].type, "dummy_importer") + self.assertEqual(migration_systems[0].requires_file_upload, True) + self.assertEqual(migration_systems[0].name, "Dummy Importer 01") + self.assertIsInstance(migration_systems[1], Migrator) + self.assertEqual(migration_systems[1].type, "dummy_importer_02") + self.assertEqual(migration_systems[1].requires_file_upload, False) + self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + + @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): From 305f2c2fb3227f0f8f9ad32f95654f9de22eccc9 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Mon, 5 Mar 2018 17:48:40 -0500 Subject: [PATCH 11/91] Added content migration unit tests to group --- tests/fixtures/group.json | 54 +++++++++++++++++++++++++++++++++++++ tests/test_group.py | 56 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/tests/fixtures/group.json b/tests/fixtures/group.json index fbecbf80..7315cb5c 100644 --- a/tests/fixtures/group.json +++ b/tests/fixtures/group.json @@ -808,5 +808,59 @@ } ], "status_code": 200 + }, + "create_content_migration": { + "method": "POST", + "endpoint": "groups/1/content_migrations", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_single": { + "method": "GET", + "endpoint": "groups/1/content_migrations/1", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_multiple": { + "method": "GET", + "endpoint": "groups/1/content_migrations", + "data": [ + { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + { + "id": 2, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + } + ], + "status_code": 200 + }, + "get_migration_systems_multiple": { + "method": "GET", + "endpoint": "groups/1/content_migrations/migrators", + "data": [ + { + "type": "dummy_importer", + "requires_file_upload": true, + "name": "Dummy Importer 01" + }, + { + "type": "dummy_importer_02", + "requires_file_upload": false, + "name": "Dummy Importer 02" + } + ], + "status_code": 200 } } diff --git a/tests/test_group.py b/tests/test_group.py index e948c44a..130d4448 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -15,6 +15,7 @@ from canvasapi.file import File from canvasapi.folder import Folder from canvasapi.tab import Tab +from canvasapi.content_migration import ContentMigration, Migrator from tests import settings from tests.util import cleanup_file, register_uris @@ -442,6 +443,61 @@ def test_list_tabs(self, m): self.assertEqual(len(tab_list), 2) self.assertIsInstance(tab_list[0], Tab) + # create_content_migration + def test_create_content_migration(self, m): + register_uris({'group': ['create_content_migration']}, m) + + content_migration = self.group.create_content_migration(migration_type='dummy_importer') + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # create_content_migration without type + def test_create_course_migration_missing_type(self, m): + with self.assertRaises(RequiredFieldMissing): + self.group.create_content_migration() + + # get_content_migration + def test_get_content_migration(self, m): + register_uris({'group': ['get_content_migration_single']}, m) + + content_migration = self.group.get_content_migration(1) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # get_content_migrations + def test_get_content_migrations(self, m): + register_uris({'group': ['get_content_migration_multiple']}, m) + + content_migrations = self.group.get_content_migrations() + + self.assertEqual(len(list(content_migrations)), 2) + + self.assertIsInstance(content_migrations[0], ContentMigration) + self.assertEqual(content_migrations[0].id, 1) + self.assertEqual(content_migrations[0].migration_type, "dummy_importer") + self.assertIsInstance(content_migrations[1], ContentMigration) + self.assertEqual(content_migrations[1].id, 2) + self.assertEqual(content_migrations[1].migration_type, "dummy_importer") + + # get_migration_systems + def test_get_migration_systems(self, m): + register_uris({'group': ['get_migration_systems_multiple']}, m) + + migration_systems = self.group.get_migration_systems() + + self.assertEqual(len(list(migration_systems)), 2) + + self.assertIsInstance(migration_systems[0], Migrator) + self.assertEqual(migration_systems[0].type, "dummy_importer") + self.assertEqual(migration_systems[0].requires_file_upload, True) + self.assertEqual(migration_systems[0].name, "Dummy Importer 01") + self.assertIsInstance(migration_systems[1], Migrator) + self.assertEqual(migration_systems[1].type, "dummy_importer_02") + self.assertEqual(migration_systems[1].requires_file_upload, False) + self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + @requests_mock.Mocker() class TestGroupMembership(unittest.TestCase): From a2700538e6b075a6b74cdc6dbe1a6dd1da78e534 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Mon, 5 Mar 2018 17:54:35 -0500 Subject: [PATCH 12/91] Added Content Migration unit tests to user --- canvasapi/user.py | 1 + tests/fixtures/user.json | 54 +++++++++++++++++++++++++++++++++++++ tests/test_user.py | 57 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) diff --git a/canvasapi/user.py b/canvasapi/user.py index 276c13b3..3e4d5321 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -5,6 +5,7 @@ from canvasapi.calendar_event import CalendarEvent from canvasapi.canvas_object import CanvasObject from canvasapi.communication_channel import CommunicationChannel +from canvasapi.exceptions import RequiredFieldMissing from canvasapi.folder import Folder from canvasapi.paginated_list import PaginatedList from canvasapi.upload import Uploader diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json index 6895b3bf..69257732 100644 --- a/tests/fixtures/user.json +++ b/tests/fixtures/user.json @@ -752,5 +752,59 @@ } ], "status_code": 200 + }, + "create_content_migration": { + "method": "POST", + "endpoint": "users/1/content_migrations", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_single": { + "method": "GET", + "endpoint": "users/1/content_migrations/1", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "get_content_migration_multiple": { + "method": "GET", + "endpoint": "users/1/content_migrations", + "data": [ + { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + { + "id": 2, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + } + ], + "status_code": 200 + }, + "get_migration_systems_multiple": { + "method": "GET", + "endpoint": "users/1/content_migrations/migrators", + "data": [ + { + "type": "dummy_importer", + "requires_file_upload": true, + "name": "Dummy Importer 01" + }, + { + "type": "dummy_importer_02", + "requires_file_upload": false, + "name": "Dummy Importer 02" + } + ], + "status_code": 200 } } diff --git a/tests/test_user.py b/tests/test_user.py index eb5fbd82..8dba8d2b 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -16,6 +16,8 @@ from canvasapi.page_view import PageView from canvasapi.user import User from canvasapi.login import Login +from canvasapi.exceptions import RequiredFieldMissing +from canvasapi.content_migration import ContentMigration, Migrator from tests import settings from tests.util import cleanup_file, register_uris @@ -336,6 +338,61 @@ def test_remove_observee(self, m): self.assertIsInstance(response, User) + # create_content_migration + def test_create_content_migration(self, m): + register_uris({'user': ['create_content_migration']}, m) + + content_migration = self.user.create_content_migration(migration_type='dummy_importer') + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # create_content_migration without type + def test_create_course_migration_missing_type(self, m): + with self.assertRaises(RequiredFieldMissing): + self.user.create_content_migration() + + # get_content_migration + def test_get_content_migration(self, m): + register_uris({'user': ['get_content_migration_single']}, m) + + content_migration = self.user.get_content_migration(1) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + # get_content_migrations + def test_get_content_migrations(self, m): + register_uris({'user': ['get_content_migration_multiple']}, m) + + content_migrations = self.user.get_content_migrations() + + self.assertEqual(len(list(content_migrations)), 2) + + self.assertIsInstance(content_migrations[0], ContentMigration) + self.assertEqual(content_migrations[0].id, 1) + self.assertEqual(content_migrations[0].migration_type, "dummy_importer") + self.assertIsInstance(content_migrations[1], ContentMigration) + self.assertEqual(content_migrations[1].id, 2) + self.assertEqual(content_migrations[1].migration_type, "dummy_importer") + + # get_migration_systems + def test_get_migration_systems(self, m): + register_uris({'user': ['get_migration_systems_multiple']}, m) + + migration_systems = self.user.get_migration_systems() + + self.assertEqual(len(list(migration_systems)), 2) + + self.assertIsInstance(migration_systems[0], Migrator) + self.assertEqual(migration_systems[0].type, "dummy_importer") + self.assertEqual(migration_systems[0].requires_file_upload, True) + self.assertEqual(migration_systems[0].name, "Dummy Importer 01") + self.assertIsInstance(migration_systems[1], Migrator) + self.assertEqual(migration_systems[1].type, "dummy_importer_02") + self.assertEqual(migration_systems[1].requires_file_upload, False) + self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + @requests_mock.Mocker() class TestUserDisplay(unittest.TestCase): From 281ba664d110ad074d430f86d02330d57e84a8de Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 12:23:40 -0500 Subject: [PATCH 13/91] Added access to Progress through ContentMigration --- canvasapi/content_migration.py | 73 ++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 1e7a79eb..59de58f3 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -50,32 +50,6 @@ def _parent_type(self): else: raise ValueError("Content Migration does not have an account_id, course_id, group_id or user_id") - def get_parent(self, **kwargs): - """ - Return the object that spawned this content migration. - - :rtype: :class:`canvasapi.group.Account`, :class:`canvasapi.course.Course`, :class:`canvasapi.course.Group`, or :class:`canvasapi.course.User` - """ - from canvasapi.group import Group - from canvasapi.course import Course - from canvasapi.account import Account - from canvasapi.user import User - - response = self._requester.request( - 'GET', - '{}s/{}'.format(self._parent_type, self._parent_id), - _kwargs=combine_kwargs(**kwargs) - ) - - if self._parent_type == 'group': - return Group(self._requester, response.json()) - elif self._parent_type == 'course': - return Course(self._requester, response.json()) - elif self._parent_type == 'account': - return Account(self._requester, response.json()) - elif self._parent_type == 'user': - return User(self._requester, response.json()) - def get_migration_issue(self,migration_issue, **kwargs): """ List a single issue for this content migration @@ -140,6 +114,53 @@ def get_migration_issues(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def get_parent(self, **kwargs): + """ + Return the object that spawned this content migration. + + :rtype: :class:`canvasapi.group.Account`, :class:`canvasapi.course.Course`, :class:`canvasapi.course.Group`, or :class:`canvasapi.course.User` + """ + from canvasapi.group import Group + from canvasapi.course import Course + from canvasapi.account import Account + from canvasapi.user import User + + response = self._requester.request( + 'GET', + '{}s/{}'.format(self._parent_type, self._parent_id), + _kwargs=combine_kwargs(**kwargs) + ) + + if self._parent_type == 'group': + return Group(self._requester, response.json()) + elif self._parent_type == 'course': + return Course(self._requester, response.json()) + elif self._parent_type == 'account': + return Account(self._requester, response.json()) + elif self._parent_type == 'user': + return User(self._requester, response.json()) + + def get_progress(self, **kwargs): + """ + Get the progress of the current content migration. + + :calls: `GET /api/v1/progress/:id \ + `_ + + :rtype: :class:`canvasapi.progress.Progress` + """ + + from canvasapi.progress import Progress + + progress_id = self.progress_url.split("/")[-1] + + response = self._requester.request( + 'GET', + 'progress/{}'.format(progress_id), + _kwargs=combine_kwargs(**kwargs) + ) + return Progress(self._requester, response.json()) + def update(self, **kwargs): """ Update an existing content migration. From b2be64ec93359aae82faec856acb3d50604a4740 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 13:17:53 -0500 Subject: [PATCH 14/91] Added unit tests for content migrations --- tests/fixtures/account.json | 3 +- tests/fixtures/content_migration.json | 162 +++++++++++++++++ tests/fixtures/course.json | 3 +- tests/fixtures/group.json | 3 +- tests/fixtures/user.json | 3 +- tests/test_account.py | 1 + tests/test_content_migration.py | 242 ++++++++++++++++++++++++++ tests/test_course.py | 2 - 8 files changed, 413 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/content_migration.json create mode 100644 tests/test_content_migration.py diff --git a/tests/fixtures/account.json b/tests/fixtures/account.json index 912aeb8f..54f6084a 100644 --- a/tests/fixtures/account.json +++ b/tests/fixtures/account.json @@ -1135,7 +1135,8 @@ "data": { "id": 1, "migration_type": "dummy_importer", - "migration_type_title": "Dummy Importer" + "migration_type_title": "Dummy Importer", + "progress_url": "https://example.com/api/v1/progress/1" }, "status_code": 200 }, diff --git a/tests/fixtures/content_migration.json b/tests/fixtures/content_migration.json new file mode 100644 index 00000000..cb3b531a --- /dev/null +++ b/tests/fixtures/content_migration.json @@ -0,0 +1,162 @@ +{ + "update": { + "method": "PUT", + "endpoint": "accounts/1/content_migrations/1", + "data": { + "id": 1, + "migration_type": "dummy_importer", + "migration_type_title": "Dummy Importer" + }, + "status_code": 200 + }, + "update_fail": { + "method": "PUT", + "endpoint": "accounts/1/content_migrations/1", + "data": {}, + "status_code": 200 + }, + "get_parent_account": { + "method": "GET", + "endpoint": "accounts/1", + "data": { + "id": 1, + "name": "Dummy Parent" + }, + "status_code": 200 + }, + "get_parent_course": { + "method": "GET", + "endpoint": "courses/1", + "data": { + "id": 1, + "name": "Dummy Parent" + }, + "status_code": 200 + }, + "get_parent_group": { + "method": "GET", + "endpoint": "groups/1", + "data": { + "id": 1, + "name": "Dummy Parent" + }, + "status_code": 200 + }, + "get_parent_user": { + "method": "GET", + "endpoint": "users/1", + "data": { + "id": 1, + "name": "Dummy Parent" + }, + "status_code": 200 + }, + "get_migration_issue_single": { + "method": "GET", + "endpoint": "accounts/1/content_migrations/1/migration_issues/1", + "data": { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy", + "error_report_html_url": "https://example.com/error_reports/1" + }, + "status_code": 200 + }, + "get_migration_issue_single_course": { + "method": "GET", + "endpoint": "courses/1/content_migrations/1/migration_issues/1", + "data": { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy", + "error_report_html_url": "https://example.com/error_reports/1" + }, + "status_code": 200 + }, + "get_migration_issue_single_group": { + "method": "GET", + "endpoint": "groups/1/content_migrations/1/migration_issues/1", + "data": { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy", + "error_report_html_url": "https://example.com/error_reports/1" + }, + "status_code": 200 + }, + "get_migration_issue_single_user": { + "method": "GET", + "endpoint": "users/1/content_migrations/1/migration_issues/1", + "data": { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy", + "error_report_html_url": "https://example.com/error_reports/1" + }, + "status_code": 200 + }, + "get_migration_issue_multiple": { + "method": "GET", + "endpoint": "accounts/1/content_migrations/1/migration_issues/?per_page=100", + "data": [ + { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy" + }, + { + "id": 2, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "Another Dummy Error", + "workflow_state": "active", + "issue_type": "dummy" + } + ], + "status_code": 200 + }, + "get_progress": { + "method": "GET", + "endpoint": "progress/1", + "data": { + "id": 1, + "context_id": 1, + "context_type": "Content Migration", + "user_id": 123, + "tag": "assign_unassigned_members", + "completion": 100, + "workflow_state": "running", + "created_at": "2013-01-15T15:00:00Z", + "updated_at": "2013-01-15T15:04:00Z", + "message": "Test 123" + }, + "status_code": 200 + }, + "update_issue": { + "method": "PUT", + "endpoint": "accounts/1/content_migrations/1/migration_issues/1", + "data": { + "id": 1, + "content_migration_url": "https://example.com/api/v1/courses/1/content_migrations/1", + "description": "A Dummy Error", + "workflow_state": "active", + "issue_type": "dummy" + }, + "status_code": 200 + }, + "update_issue_fail": { + "method": "PUT", + "endpoint": "accounts/1/content_migrations/1/migration_issues/1", + "data": {}, + "status_code": 200 + } +} diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json index 94036fe7..2f30d675 100644 --- a/tests/fixtures/course.json +++ b/tests/fixtures/course.json @@ -1420,7 +1420,8 @@ "data": { "id": 1, "migration_type": "dummy_importer", - "migration_type_title": "Dummy Importer" + "migration_type_title": "Dummy Importer", + "progress_url": "https://example.com/api/v1/progress/1" }, "status_code": 200 }, diff --git a/tests/fixtures/group.json b/tests/fixtures/group.json index 7315cb5c..27d2abd8 100644 --- a/tests/fixtures/group.json +++ b/tests/fixtures/group.json @@ -825,7 +825,8 @@ "data": { "id": 1, "migration_type": "dummy_importer", - "migration_type_title": "Dummy Importer" + "migration_type_title": "Dummy Importer", + "progress_url": "https://example.com/api/v1/progress/1" }, "status_code": 200 }, diff --git a/tests/fixtures/user.json b/tests/fixtures/user.json index 69257732..4aa36b01 100644 --- a/tests/fixtures/user.json +++ b/tests/fixtures/user.json @@ -769,7 +769,8 @@ "data": { "id": 1, "migration_type": "dummy_importer", - "migration_type_title": "Dummy Importer" + "migration_type_title": "Dummy Importer", + "progress_url": "https://example.com/api/v1/progress/4" }, "status_code": 200 }, diff --git a/tests/test_account.py b/tests/test_account.py index 20fa8256..a8fdca05 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -773,6 +773,7 @@ def test_get_content_migrations(self, m): self.assertEqual(content_migrations[1].id, 2) self.assertEqual(content_migrations[1].migration_type, "dummy_importer") + # get_migration_systems def test_get_migration_systems(self, m): register_uris({'account': ['get_migration_systems_multiple']}, m) diff --git a/tests/test_content_migration.py b/tests/test_content_migration.py new file mode 100644 index 00000000..827b80c3 --- /dev/null +++ b/tests/test_content_migration.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import, division, print_function, unicode_literals +import unittest + +import requests_mock + +from canvasapi import Canvas +from canvasapi.account import Account +from canvasapi.content_migration import ContentMigration, MigrationIssue, Migrator +from canvasapi.course import Course +from canvasapi.group import Group +from canvasapi.progress import Progress +from canvasapi.user import User +from tests import settings +from tests.util import register_uris + + +@requests_mock.Mocker() +class TestContentMigration(unittest.TestCase): + def setUp(self): + self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) + + with requests_mock.Mocker() as m: + requires = { + 'course': ['get_by_id', 'get_content_migration_single'], + 'group': ['get_by_id', 'get_content_migration_single'], + 'account': ['get_by_id', 'get_content_migration_single'], + 'user': ['get_by_id', 'get_content_migration_single'] + } + register_uris(requires, m) + + self.account = self.canvas.get_account(1) + self.course = self.canvas.get_course(1) + self.group = self.canvas.get_group(1) + self.user = self.canvas.get_user(1) + + self.content_migration = self.account.get_content_migration(1) + self.content_migration_course = self.course.get_content_migration(1) + self.content_migration_group = self.group.get_content_migration(1) + self.content_migration_user = self.user.get_content_migration(1) + + # __str__() + def test__str__(self, m): + string = str(self.content_migration) + self.assertIsInstance(string, str) + + # _parent_type + def test_parent_type_acount(self, m): + self.assertEqual(self.content_migration._parent_type, 'account') + + def test_parent_type_acount(self, m): + self.assertEqual(self.content_migration_course._parent_type, 'course') + + def test_parent_type_acount(self, m): + self.assertEqual(self.content_migration_group._parent_type, 'group') + + def test_parent_type_acount(self, m): + self.assertEqual(self.content_migration_user._parent_type, 'user') + + def test_parent_type_no_type(self, m): + migration = ContentMigration(self.canvas._Canvas__requester, {'id': 1}) + with self.assertRaises(ValueError): + migration._parent_type + + # _parent_id + def test_parent_id_acount(self, m): + self.assertEqual(self.content_migration._parent_id, 1) + + def test_parent_id_acount(self, m): + self.assertEqual(self.content_migration_course._parent_id, 1) + + def test_parent_id_acount(self, m): + self.assertEqual(self.content_migration_group._parent_id, 1) + + def test_parent_id_acount(self, m): + self.assertEqual(self.content_migration_user._parent_id, 1) + + def test_parent_id_no_id(self, m): + migration = ContentMigration(self.canvas._Canvas__requester, {'id': 1}) + with self.assertRaises(ValueError): + migration._parent_id + + # get_migration_issue() + def test_get_migration_issue(self, m): + register_uris({'content_migration': ['get_migration_issue_single']}, m) + + issue = self.content_migration.get_migration_issue(1) + self.assertIsInstance(issue, MigrationIssue) + self.assertTrue(hasattr(issue, 'id')) + self.assertEqual(issue.id, 1) + + # get_migration_issues() + def test_get_migration_issues(self, m): + register_uris({'content_migration': ['get_migration_issue_multiple']}, m) + + issues = self.content_migration.get_migration_issues() + + self.assertEqual(len(list(issues)),2) + + self.assertIsInstance(issues[0], MigrationIssue) + self.assertTrue(hasattr(issues[0], 'id')) + self.assertEqual(issues[0].id, 1) + self.assertIsInstance(issues[1], MigrationIssue) + self.assertTrue(hasattr(issues[1], 'id')) + self.assertEqual(issues[1].id, 2) + + # get_parent() + def test_get_parent_account(self, m): + register_uris({'content_migration': ['get_parent_account']}, m) + + account = self.content_migration.get_parent() + self.assertIsInstance(account, Account) + self.assertTrue(hasattr(account, 'id')) + self.assertEqual(account.id, 1) + + def test_get_parent_course(self, m): + register_uris({'content_migration': ['get_parent_course']}, m) + + course = self.content_migration_course.get_parent() + self.assertIsInstance(course, Course) + self.assertTrue(hasattr(course, 'id')) + self.assertEqual(course.id, 1) + + def test_get_parent_group(self, m): + register_uris({'content_migration': ['get_parent_group']}, m) + + group = self.content_migration_group.get_parent() + self.assertIsInstance(group, Group) + self.assertTrue(hasattr(group, 'id')) + self.assertEqual(group.id, 1) + + def test_get_parent_user(self, m): + register_uris({'content_migration': ['get_parent_user']}, m) + + user = self.content_migration_user.get_parent() + self.assertIsInstance(user, User) + self.assertTrue(hasattr(user, 'id')) + self.assertEqual(user.id, 1) + + # get_progress() + def test_get_progress(self, m): + register_uris({'content_migration': ['get_progress']}, m) + + progress = self.content_migration.get_progress() + self.assertIsInstance(progress, Progress) + self.assertTrue(hasattr(progress, 'id')) + self.assertEqual(progress.id, 1) + + # update() + def test_update(self, m): + register_uris({'content_migration': ['update']}, m) + + worked = self.content_migration.update() + self.assertTrue(worked) + self.assertTrue(hasattr(self.content_migration, 'migration_type')) + self.assertEqual(self.content_migration.migration_type, "dummy_importer") + + def test_update_fail(self, m): + register_uris({'content_migration': ['update_fail']}, m) + + worked = self.content_migration.update() + self.assertFalse(worked) + +@requests_mock.Mocker() +class TestMigrationIssue(unittest.TestCase): + + def setUp(self): + self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) + + with requests_mock.Mocker() as m: + requires = { + 'course': ['get_by_id', 'get_content_migration_single'], + 'group': ['get_by_id', 'get_content_migration_single'], + 'account': ['get_by_id', 'get_content_migration_single'], + 'user': ['get_by_id', 'get_content_migration_single'], + 'content_migration': ['get_migration_issue_single', 'get_migration_issue_single_course', 'get_migration_issue_single_group', 'get_migration_issue_single_user'] + } + register_uris(requires, m) + + self.account = self.canvas.get_account(1) + self.course = self.canvas.get_course(1) + self.group = self.canvas.get_group(1) + self.user = self.canvas.get_user(1) + + self.content_migration = self.account.get_content_migration(1) + self.content_migration_course = self.course.get_content_migration(1) + self.content_migration_group = self.group.get_content_migration(1) + self.content_migration_user = self.user.get_content_migration(1) + + self.migration_issue = self.content_migration.get_migration_issue(1) + self.migration_issue_course = self.content_migration_course.get_migration_issue(1) + self.migration_issue_group = self.content_migration_group.get_migration_issue(1) + self.migration_issue_user = self.content_migration_user.get_migration_issue(1) + + # __str__() + def test__str__(self, m): + string = str(self.migration_issue) + self.assertIsInstance(string, str) + + # update() + def test_update(self, m): + register_uris({'content_migration': ['update_issue']}, m) + + worked = self.migration_issue.update() + self.assertTrue(worked) + self.assertTrue(hasattr(self.migration_issue, 'id')) + self.assertEqual(self.migration_issue.id, 1) + + def test_update_fail(self, m): + register_uris({'content_migration': ['update_issue_fail']}, m) + + worked = self.migration_issue.update() + self.assertFalse(worked) + +@requests_mock.Mocker() +class TestMigrator(unittest.TestCase): + def setUp(self): + self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) + + with requests_mock.Mocker() as m: + requires = { + 'course': ['get_by_id', 'get_migration_systems_multiple'], + 'group': ['get_by_id', 'get_migration_systems_multiple'], + 'account': ['get_by_id', 'get_migration_systems_multiple'], + 'user': ['get_by_id', 'get_migration_systems_multiple'], + 'content_migration': ['get_migration_issue_single', 'get_migration_issue_single_course', 'get_migration_issue_single_group', 'get_migration_issue_single_user'] + } + register_uris(requires, m) + + self.account = self.canvas.get_account(1) + self.course = self.canvas.get_course(1) + self.group = self.canvas.get_group(1) + self.user = self.canvas.get_user(1) + + self.migrator = self.account.get_migration_systems()[0] + self.migrator_course = self.course.get_migration_systems()[0] + self.migrator_group = self.group.get_migration_systems()[0] + self.migrator_user = self.user.get_migration_systems()[0] + + # __str__() + def test__str__(self, m): + string = str(self.migrator) + self.assertIsInstance(string, str) diff --git a/tests/test_course.py b/tests/test_course.py index bf77384d..9cabb0fa 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -1239,8 +1239,6 @@ def test_get_migration_systems(self, m): self.assertEqual(migration_systems[1].requires_file_upload, False) self.assertEqual(migration_systems[1].name, "Dummy Importer 02") - - @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): From f21c468e224e9e6f74ef6b7b07359d67b526898d Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 13:18:56 -0500 Subject: [PATCH 15/91] Fixed account_id not being added when getting a list of content migrations --- canvasapi/account.py | 1 + 1 file changed, 1 insertion(+) diff --git a/canvasapi/account.py b/canvasapi/account.py index 2b252833..0cd0ec1f 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1241,6 +1241,7 @@ def get_content_migrations(self, **kwargs): self._requester, 'GET', 'accounts/{}/content_migrations'.format(self.id), + {'account_id': self.id}, _kwargs=combine_kwargs(**kwargs) ) From b3c757d9ad51457575249ba883109207e2b6e9b5 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 13:56:45 -0500 Subject: [PATCH 16/91] Cleaned up content migration for readability --- canvasapi/content_migration.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 59de58f3..1dc44e23 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -3,10 +3,8 @@ from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject -from canvasapi.grading_standard import GradingStandard from canvasapi.exceptions import CanvasException, RequiredFieldMissing from canvasapi.paginated_list import PaginatedList -from canvasapi.rubric import Rubric from canvasapi.util import combine_kwargs, obj_or_id @python_2_unicode_compatible From 9b2c337eb192ac33591644c36623a481ffb64914 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 15:05:47 -0500 Subject: [PATCH 17/91] Fixed styling to comply with flake8 linter --- canvasapi/account.py | 5 +-- canvasapi/content_migration.py | 62 +++++++++++++++++++++++---------- canvasapi/course.py | 5 +-- canvasapi/group.py | 6 ++-- canvasapi/user.py | 5 +-- tests/test_content_migration.py | 32 ++++++++++------- tests/test_course.py | 1 + 7 files changed, 77 insertions(+), 39 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 0cd0ec1f..40b5bc23 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1183,7 +1183,7 @@ def create_content_migration(self, **kwargs): """ from canvasapi.content_migration import ContentMigration - if not 'migration_type' in kwargs: + if 'migration_type' not in kwargs: raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") response = self._requester.request( @@ -1215,7 +1215,7 @@ def get_content_migration(self, content_migration, **kwargs): response = self._requester.request( 'GET', - 'accounts/{}/content_migrations/{}'.format(self.id,migration_id), + 'accounts/{}/content_migrations/{}'.format(self.id, migration_id), _kwargs=combine_kwargs(**kwargs) ) @@ -1266,6 +1266,7 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + @python_2_unicode_compatible class AccountNotification(CanvasObject): diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 1dc44e23..95a54909 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -3,10 +3,10 @@ from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject -from canvasapi.exceptions import CanvasException, RequiredFieldMissing from canvasapi.paginated_list import PaginatedList from canvasapi.util import combine_kwargs, obj_or_id + @python_2_unicode_compatible class ContentMigration(CanvasObject): def __str__(self): @@ -15,7 +15,8 @@ def __str__(self): @property def _parent_id(self): """ - Return the id of the course or group that spawned this content migration. + Return the id of the course or group that spawned + this content migration. :rtype: int """ @@ -28,7 +29,8 @@ def _parent_id(self): elif hasattr(self, 'user_id'): return self.user_id else: - raise ValueError("Content Migration does not have an account_id, course_id, group_id or user_id") + raise ValueError( + "Content Migration does not have an account_id, course_id, group_id or user_id") @property def _parent_type(self): @@ -46,13 +48,15 @@ def _parent_type(self): elif hasattr(self, 'user_id'): return 'user' else: - raise ValueError("Content Migration does not have an account_id, course_id, group_id or user_id") + raise ValueError( + "Content Migration does not have an account_id, course_id, group_id or user_id") - def get_migration_issue(self,migration_issue, **kwargs): + def get_migration_issue(self, migration_issue, **kwargs): """ List a single issue for this content migration - :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + :calls: + `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ `_ or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ @@ -66,28 +70,34 @@ def get_migration_issue(self,migration_issue, **kwargs): :rtype: :class:`canvasapi.content_migration.MigrationIssue` """ - from canvasapi.content_migration import ContentMigration from canvasapi.content_migration import MigrationIssue migration_issue_id = obj_or_id(migration_issue, "migration_issue", (MigrationIssue,)) response = self._requester.request( 'GET', - '{}s/{}/content_migrations/{}/migration_issues/{}'.format(self._parent_type, self._parent_id,self.id,migration_issue_id), + '{}s/{}/content_migrations/{}/migration_issues/{}'.format( + self._parent_type, + self._parent_id, + self.id, + migration_issue_id), _kwargs=combine_kwargs(**kwargs) ) response_json = response.json() - response_json.update({'context_type':self._parent_type, 'context_id':self._parent_id, 'content_migration_id':self.id}) + response_json.update({ + 'context_type': self._parent_type, + 'context_id': self._parent_id, + 'content_migration_id': self.id}) return MigrationIssue(self._requester, response_json) - def get_migration_issues(self, **kwargs): """ List issues for this content migration - :calls: `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + :calls: + `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ `_ or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ @@ -107,8 +117,12 @@ def get_migration_issues(self, **kwargs): MigrationIssue, self._requester, 'GET', - '{}s/{}/content_migrations/{}/migration_issues/'.format(self._parent_type, self._parent_id,self.id), - {'context_type':self._parent_type, 'context_id':self._parent_id, 'content_migration_id':self.id}, + '{}s/{}/content_migrations/{}/migration_issues/'.format( + self._parent_type, + self._parent_id, self.id), + {'context_type': self._parent_type, + 'context_id': self._parent_id, + 'content_migration_id': self.id}, _kwargs=combine_kwargs(**kwargs) ) @@ -116,7 +130,10 @@ def get_parent(self, **kwargs): """ Return the object that spawned this content migration. - :rtype: :class:`canvasapi.group.Account`, :class:`canvasapi.course.Course`, :class:`canvasapi.course.Group`, or :class:`canvasapi.course.User` + :rtype: :class:`canvasapi.group.Account`, + or :class:`canvasapi.course.Course`, + or :class:`canvasapi.course.Group`, + or :class:`canvasapi.course.User` """ from canvasapi.group import Group from canvasapi.course import Course @@ -180,7 +197,7 @@ def update(self, **kwargs): """ response = self._requester.request( 'PUT', - '{}s/{}/content_migrations/{}'.format(self._parent_type, self._parent_id,self.id), + '{}s/{}/content_migrations/{}'.format(self._parent_type, self._parent_id, self.id), _kwargs=combine_kwargs(**kwargs) ) @@ -190,16 +207,18 @@ def update(self, **kwargs): else: return False + @python_2_unicode_compatible class MigrationIssue(CanvasObject): def __str__(self): - return "{}: {}".format(self.id,self.description) + return "{}: {}".format(self.id, self.description) - def update(self,**kwargs): + def update(self, **kwargs): """ Update an existing migration issue. - :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ + :calls: + `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ `_ or `PUT /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ @@ -216,7 +235,11 @@ def update(self,**kwargs): """ response = self._requester.request( 'PUT', - '{}s/{}/content_migrations/{}/migration_issues/{}'.format(self.context_type, self.context_id, self.content_migration_id, self.id), + '{}s/{}/content_migrations/{}/migration_issues/{}'.format( + self.context_type, + self.context_id, + self.content_migration_id, + self.id), _kwargs=combine_kwargs(**kwargs) ) @@ -226,6 +249,7 @@ def update(self,**kwargs): else: return False + @python_2_unicode_compatible class Migrator(CanvasObject): def __str__(self): diff --git a/canvasapi/course.py b/canvasapi/course.py index 7e222f78..4a708878 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1840,7 +1840,7 @@ def create_content_migration(self, **kwargs): """ from canvasapi.content_migration import ContentMigration - if not 'migration_type' in kwargs: + if 'migration_type' not in kwargs: raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") response = self._requester.request( @@ -1872,7 +1872,7 @@ def get_content_migration(self, content_migration, **kwargs): response = self._requester.request( 'GET', - 'courses/{}/content_migrations/{}'.format(self.id,migration_id), + 'courses/{}/content_migrations/{}'.format(self.id, migration_id), _kwargs=combine_kwargs(**kwargs) ) @@ -1922,6 +1922,7 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + @python_2_unicode_compatible class CourseNickname(CanvasObject): diff --git a/canvasapi/group.py b/canvasapi/group.py index b189e3c2..b2ca11a9 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -678,7 +678,7 @@ def create_content_migration(self, **kwargs): """ from canvasapi.content_migration import ContentMigration - if not 'migration_type' in kwargs: + if 'migration_type' not in kwargs: raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") response = self._requester.request( @@ -710,7 +710,7 @@ def get_content_migration(self, content_migration, **kwargs): response = self._requester.request( 'GET', - 'groups/{}/content_migrations/{}'.format(self.id,migration_id), + 'groups/{}/content_migrations/{}'.format(self.id, migration_id), _kwargs=combine_kwargs(**kwargs) ) @@ -760,6 +760,7 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + @python_2_unicode_compatible class GroupMembership(CanvasObject): @@ -822,6 +823,7 @@ def remove_self(self): ) return response.json() + @python_2_unicode_compatible class GroupCategory(CanvasObject): diff --git a/canvasapi/user.py b/canvasapi/user.py index 3e4d5321..83291083 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -556,7 +556,7 @@ def create_content_migration(self, **kwargs): """ from canvasapi.content_migration import ContentMigration - if not 'migration_type' in kwargs: + if 'migration_type' not in kwargs: raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") response = self._requester.request( @@ -588,7 +588,7 @@ def get_content_migration(self, content_migration, **kwargs): response = self._requester.request( 'GET', - 'users/{}/content_migrations/{}'.format(self.id,migration_id), + 'users/{}/content_migrations/{}'.format(self.id, migration_id), _kwargs=combine_kwargs(**kwargs) ) @@ -638,6 +638,7 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + @python_2_unicode_compatible class UserDisplay(CanvasObject): diff --git a/tests/test_content_migration.py b/tests/test_content_migration.py index 827b80c3..2616dadc 100644 --- a/tests/test_content_migration.py +++ b/tests/test_content_migration.py @@ -5,7 +5,7 @@ from canvasapi import Canvas from canvasapi.account import Account -from canvasapi.content_migration import ContentMigration, MigrationIssue, Migrator +from canvasapi.content_migration import ContentMigration, MigrationIssue from canvasapi.course import Course from canvasapi.group import Group from canvasapi.progress import Progress @@ -44,16 +44,16 @@ def test__str__(self, m): self.assertIsInstance(string, str) # _parent_type - def test_parent_type_acount(self, m): + def test_parent_type_account(self, m): self.assertEqual(self.content_migration._parent_type, 'account') - def test_parent_type_acount(self, m): + def test_parent_type_course(self, m): self.assertEqual(self.content_migration_course._parent_type, 'course') - def test_parent_type_acount(self, m): + def test_parent_type_group(self, m): self.assertEqual(self.content_migration_group._parent_type, 'group') - def test_parent_type_acount(self, m): + def test_parent_type_user(self, m): self.assertEqual(self.content_migration_user._parent_type, 'user') def test_parent_type_no_type(self, m): @@ -62,16 +62,16 @@ def test_parent_type_no_type(self, m): migration._parent_type # _parent_id - def test_parent_id_acount(self, m): + def test_parent_id_account(self, m): self.assertEqual(self.content_migration._parent_id, 1) - def test_parent_id_acount(self, m): + def test_parent_id_course(self, m): self.assertEqual(self.content_migration_course._parent_id, 1) - def test_parent_id_acount(self, m): + def test_parent_id_group(self, m): self.assertEqual(self.content_migration_group._parent_id, 1) - def test_parent_id_acount(self, m): + def test_parent_id_user(self, m): self.assertEqual(self.content_migration_user._parent_id, 1) def test_parent_id_no_id(self, m): @@ -94,7 +94,7 @@ def test_get_migration_issues(self, m): issues = self.content_migration.get_migration_issues() - self.assertEqual(len(list(issues)),2) + self.assertEqual(len(list(issues)), 2) self.assertIsInstance(issues[0], MigrationIssue) self.assertTrue(hasattr(issues[0], 'id')) @@ -160,6 +160,7 @@ def test_update_fail(self, m): worked = self.content_migration.update() self.assertFalse(worked) + @requests_mock.Mocker() class TestMigrationIssue(unittest.TestCase): @@ -172,7 +173,10 @@ def setUp(self): 'group': ['get_by_id', 'get_content_migration_single'], 'account': ['get_by_id', 'get_content_migration_single'], 'user': ['get_by_id', 'get_content_migration_single'], - 'content_migration': ['get_migration_issue_single', 'get_migration_issue_single_course', 'get_migration_issue_single_group', 'get_migration_issue_single_user'] + 'content_migration': ['get_migration_issue_single', + 'get_migration_issue_single_course', + 'get_migration_issue_single_group', + 'get_migration_issue_single_user'] } register_uris(requires, m) @@ -211,6 +215,7 @@ def test_update_fail(self, m): worked = self.migration_issue.update() self.assertFalse(worked) + @requests_mock.Mocker() class TestMigrator(unittest.TestCase): def setUp(self): @@ -222,7 +227,10 @@ def setUp(self): 'group': ['get_by_id', 'get_migration_systems_multiple'], 'account': ['get_by_id', 'get_migration_systems_multiple'], 'user': ['get_by_id', 'get_migration_systems_multiple'], - 'content_migration': ['get_migration_issue_single', 'get_migration_issue_single_course', 'get_migration_issue_single_group', 'get_migration_issue_single_user'] + 'content_migration': ['get_migration_issue_single', + 'get_migration_issue_single_course', + 'get_migration_issue_single_group', + 'get_migration_issue_single_user'] } register_uris(requires, m) diff --git a/tests/test_course.py b/tests/test_course.py index 9cabb0fa..a11705b5 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -1239,6 +1239,7 @@ def test_get_migration_systems(self, m): self.assertEqual(migration_systems[1].requires_file_upload, False) self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): From c2d953d91f329ae2a5f200a09b126f384841b2ef Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Tue, 6 Mar 2018 15:54:07 -0500 Subject: [PATCH 18/91] Added copy file to folder --- canvasapi/content_migration.py | 36 ++++++++++++++++++++++------------ canvasapi/folder.py | 24 ++++++++++++++++++++++- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 95a54909..ad35800a 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -56,16 +56,20 @@ def get_migration_issue(self, migration_issue, **kwargs): List a single issue for this content migration :calls: - `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `GET + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` @@ -97,16 +101,20 @@ def get_migration_issues(self, **kwargs): List issues for this content migration :calls: - `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `GET + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ `_ - or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` @@ -218,16 +226,20 @@ def update(self, **kwargs): Update an existing migration issue. :calls: - `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ + `PUT + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ `_ - or `PUT /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ + or `PUT + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ `_ - or `PUT /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id \ + or `PUT + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id \ `_ - or `PUT /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id \ + or `PUT + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id \ `_ :returns: True if the issue was updated, False otherwise. diff --git a/canvasapi/folder.py b/canvasapi/folder.py index f3353cba..724e5f7d 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -4,7 +4,7 @@ from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList -from canvasapi.util import combine_kwargs +from canvasapi.util import combine_kwargs, obj_or_id @python_2_unicode_compatible @@ -105,3 +105,25 @@ def update(self, **kwargs): super(Folder, self).set_attributes(response.json()) return Folder(self._requester, response.json()) + + def copy_file(self, file, **kwargs): + """ + Updates a folder. + + :calls: `POST /api/v1/folders/:dest_folder_id/copy_file \ + `_ + + :rtype: :class:`canvasapi.folder.Folder` + """ + from canvasapi.file import File + + file_id = obj_or_id(file, "file", (File,)) + kwargs['source_file_id'] = file_id + + response = self._requester.request( + 'POST', + 'folders/{}/copy_file'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + + return File(self._requester, response.json()) From c56cdc167344d723ead79947f19485243dfa971a Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Tue, 6 Mar 2018 16:34:23 -0500 Subject: [PATCH 19/91] Added Content Migration docs. Fixed docstring linter errors --- canvasapi/content_migration.py | 79 ++++++++++++++++++---------------- docs/class-reference.rst | 1 + docs/content-migration-ref.rst | 6 +++ 3 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 docs/content-migration-ref.rst diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 95a54909..4cf6bdf0 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -53,20 +53,23 @@ def _parent_type(self): def get_migration_issue(self, migration_issue, **kwargs): """ - List a single issue for this content migration + List a single issue for this content migration. - :calls: - `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ - `_ + :calls: `GET + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues + `_ - or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ - `_ + or `GET + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues + `_ - or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ - `_ + or `GET + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues + `_ - or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ - `_ + or `GET + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues + `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` """ @@ -97,16 +100,20 @@ def get_migration_issues(self, **kwargs): List issues for this content migration :calls: - `GET /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + `GET + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id `_ - or `GET /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id `_ - or `GET /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id `_ - or `GET /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + or `GET + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` @@ -159,8 +166,8 @@ def get_progress(self, **kwargs): """ Get the progress of the current content migration. - :calls: `GET /api/v1/progress/:id \ - `_ + :calls: `GET /api/v1/progress/:id + `_ :rtype: :class:`canvasapi.progress.Progress` """ @@ -180,17 +187,17 @@ def update(self, **kwargs): """ Update an existing content migration. - :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id \ - `_ + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id + `_ - or `PUT /api/v1/courses/:course_id/content_migrations/:id \ - `_ + or `PUT /api/v1/courses/:course_id/content_migrations/:id + `_ - or `PUT /api/v1/groups/:group_id/content_migrations/:id \ - `_ + or `PUT /api/v1/groups/:group_id/content_migrations/:id + `_ - or `PUT /api/v1/users/:user_id/content_migrations/:id \ - `_ + or `PUT /api/v1/users/:user_id/content_migrations/:id + `_ :returns: True if the migration was updated, False otherwise. :rtype: bool @@ -217,18 +224,18 @@ def update(self, **kwargs): """ Update an existing migration issue. - :calls: - `PUT /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ + :calls: `PUT + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id + `_ :returns: True if the issue was updated, False otherwise. :rtype: bool diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 38f1046a..4ca9cd4c 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -11,6 +11,7 @@ Class Reference avatar-ref bookmark-ref calendar-event-ref + content-migration-ref conversation-ref course-ref current-user-ref diff --git a/docs/content-migration-ref.rst b/docs/content-migration-ref.rst new file mode 100644 index 00000000..64016172 --- /dev/null +++ b/docs/content-migration-ref.rst @@ -0,0 +1,6 @@ +================ +ContentMigration +================ + +.. autoclass:: canvasapi.content_migration.ContentMigration + :members: From 54a2b1eae82e10721314351e1875dcc41636a1d3 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Wed, 7 Mar 2018 15:53:50 -0500 Subject: [PATCH 20/91] Added unit test and documentation for copy file --- canvasapi/content_migration.py | 77 ++++++++++++++++------------------ canvasapi/folder.py | 2 +- docs/.coverage | 1 + docs/class-reference.rst | 1 + docs/folder-ref.rst | 6 +++ tests/fixtures/folder.json | 10 +++++ tests/test_folder.py | 9 ++++ 7 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 docs/.coverage create mode 100644 docs/folder-ref.rst diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index ad35800a..4cf6bdf0 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -53,24 +53,23 @@ def _parent_type(self): def get_migration_issue(self, migration_issue, **kwargs): """ - List a single issue for this content migration + List a single issue for this content migration. - :calls: - `GET - /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ - `_ + :calls: `GET + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues + `_ or `GET - /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ - `_ + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues + `_ or `GET - /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ - `_ + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues + `_ or `GET - /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ - `_ + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues + `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` """ @@ -102,19 +101,19 @@ def get_migration_issues(self, **kwargs): :calls: `GET - /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues \ + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues \ + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues \ + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues \ + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` @@ -167,8 +166,8 @@ def get_progress(self, **kwargs): """ Get the progress of the current content migration. - :calls: `GET /api/v1/progress/:id \ - `_ + :calls: `GET /api/v1/progress/:id + `_ :rtype: :class:`canvasapi.progress.Progress` """ @@ -188,17 +187,17 @@ def update(self, **kwargs): """ Update an existing content migration. - :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id \ - `_ + :calls: `PUT /api/v1/accounts/:account_id/content_migrations/:id + `_ - or `PUT /api/v1/courses/:course_id/content_migrations/:id \ - `_ + or `PUT /api/v1/courses/:course_id/content_migrations/:id + `_ - or `PUT /api/v1/groups/:group_id/content_migrations/:id \ - `_ + or `PUT /api/v1/groups/:group_id/content_migrations/:id + `_ - or `PUT /api/v1/users/:user_id/content_migrations/:id \ - `_ + or `PUT /api/v1/users/:user_id/content_migrations/:id + `_ :returns: True if the migration was updated, False otherwise. :rtype: bool @@ -225,22 +224,18 @@ def update(self, **kwargs): """ Update an existing migration issue. - :calls: - `PUT - /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT - /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT - /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ - - or `PUT - /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id \ - `_ + :calls: `PUT + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id + `_ + or `PUT + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id + `_ :returns: True if the issue was updated, False otherwise. :rtype: bool diff --git a/canvasapi/folder.py b/canvasapi/folder.py index 724e5f7d..6a52d0e2 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -108,7 +108,7 @@ def update(self, **kwargs): def copy_file(self, file, **kwargs): """ - Updates a folder. + Copies a file into the current folder. :calls: `POST /api/v1/folders/:dest_folder_id/copy_file \ `_ diff --git a/docs/.coverage b/docs/.coverage new file mode 100644 index 00000000..fe8af938 --- /dev/null +++ b/docs/.coverage @@ -0,0 +1 @@ +!coverage.py: This is a private format, don't read it directly!{"lines":{}} \ No newline at end of file diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 38f1046a..b6c5fd27 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -17,6 +17,7 @@ Class Reference discussion-topic-ref enrollment-term-ref external-tool-ref + folder-ref group-ref login-ref module-ref diff --git a/docs/folder-ref.rst b/docs/folder-ref.rst new file mode 100644 index 00000000..b8f513b4 --- /dev/null +++ b/docs/folder-ref.rst @@ -0,0 +1,6 @@ +============= +Folder +============= + +.. autoclass:: canvasapi.folder.Folder + :members: diff --git a/tests/fixtures/folder.json b/tests/fixtures/folder.json index a667019e..b7c2b006 100644 --- a/tests/fixtures/folder.json +++ b/tests/fixtures/folder.json @@ -103,5 +103,15 @@ "full_name": "course_files/New Name" }, "status_code": 200 + }, + "copy_file": { + "method": "POST", + "endpoint": "folders/1/copy_file", + "data": { + "id": 1, + "display_name": "Dummy File-1", + "size": 1298 + }, + "status_code": 200 } } diff --git a/tests/test_folder.py b/tests/test_folder.py index eb29e942..25b83968 100644 --- a/tests/test_folder.py +++ b/tests/test_folder.py @@ -70,3 +70,12 @@ def test_update(self, m): response = self.folder.update(name=new_name) self.assertIsInstance(response, Folder) self.assertEqual(self.folder.name, new_name) + + # copy_file() + def test_copy_file(self, m): + register_uris({'folder': ['copy_file']}, m) + + new_file = self.folder.copy_file(1) + self.assertIsInstance(new_file, File) + self.assertEqual(new_file.display_name, 'Dummy File-1') + self.assertEqual(new_file.id, 1) From d15f17704c5b850c8a437f2c09c024b9268c332e Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Wed, 7 Mar 2018 16:57:25 -0500 Subject: [PATCH 21/91] Deprecated list_ methods in account --- canvasapi/account.py | 159 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 158 insertions(+), 1 deletion(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 3a03734b..fbf801ce 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -2,6 +2,8 @@ from six import python_2_unicode_compatible +import warnings + from canvasapi.canvas_object import CanvasObject from canvasapi.grading_standard import GradingStandard from canvasapi.exceptions import CanvasException, RequiredFieldMissing @@ -397,6 +399,28 @@ def list_roles(self, **kwargs): """ List the roles available to an account. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_roles` instead. + + :calls: `GET /api/v1/accounts/:account_id/roles \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.account.Role` + """ + warnings.warn( + "`list_roles` is being deprecated and will be removed in a future version." + " Use `get_roles` instead", + DeprecationWarning + ) + + return self.get_roles(**kwargs) + + def get_roles(self, **kwargs): + """ + List the roles available to an account. + :calls: `GET /api/v1/accounts/:account_id/roles \ `_ @@ -540,6 +564,27 @@ def list_groups(self, **kwargs): """ Return a list of active groups for the specified account. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_groups` instead. + + :calls: `GET /api/v1/accounts/:account_id/groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` + """ + warnings.warn( + "`list_groups` is being deprecated and will be removed in a future version." + " Use `get_groups` instead", + DeprecationWarning + ) + + return self.get_groups(**kwargs) + + def get_groups(self, **kwargs): + """ + Return a list of active groups for the specified account. + :calls: `GET /api/v1/accounts/:account_id/groups \ `_ @@ -575,7 +620,30 @@ def create_group_category(self, name, **kwargs): ) return GroupCategory(self._requester, response.json()) - def list_group_categories(self): + def list_group_categories(self, kwargs): + """ + List group categories for a context + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_group_categories` instead. + + :calls: `GET /api/v1/accounts/:account_id/group_categories \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.group.GroupCategory` + """ + + warnings.warn( + "`list_group_categories` is being deprecated and will be removed in a future version." + " Use `get_group_categories` instead", + DeprecationWarning + ) + + return self.get_group_categories(**kwargs) + + def get_group_categories(self, **kwargs): """ List group categories for a context @@ -649,6 +717,28 @@ def list_enrollment_terms(self, **kwargs): """ List enrollment terms for a context + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_enrollment_terms` instead. + + :calls: `GET /api/v1/accounts/:account_id/terms \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.enrollment_term.EnrollmentTerm` + """ + warnings.warn( + "`list_enrollment_terms` is being deprecated and will be removed in a future version." + " Use `get_enrollment_terms` instead", + DeprecationWarning + ) + + return self.get_enrollment_terms(**kwargs) + + def get_enrollment_terms(self, **kwargs): + """ + List enrollment terms for a context + :calls: `GET /api/v1/accounts/:account_id/terms \ `_ @@ -671,6 +761,28 @@ def list_user_logins(self, **kwargs): """ Given a user ID, return that user's logins for the given account. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_user_logins` instead. + + :calls: `GET /api/v1/accounts/:account_id/logins \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.login.Login` + """ + warnings.warn( + "`list_user_logins` is being deprecated and will be removed in a future version." + " Use `get_user_logins` instead", + DeprecationWarning + ) + + return self.get_user_logins(**kwargs) + + def get_user_logins(self, **kwargs): + """ + Given a user ID, return that user's logins for the given account. + :calls: `GET /api/v1/accounts/:account_id/logins \ `_ @@ -903,6 +1015,29 @@ def list_authentication_providers(self, **kwargs): """ Return the list of authentication providers + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_authentication_providers` instead. + + :calls: `GET /api/v1/accounts/:account_id/authentication_providers \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.authentication_provider.AuthenticationProvider` + """ + warnings.warn( + "`list_authentication_providers`" + " is being deprecated and will be removed in a future version." + " Use `get_authentication_providers` instead", + DeprecationWarning + ) + + return self.get_authentication_providers(**kwargs) + + def get_authentication_providers(self, **kwargs): + """ + Return the list of authentication providers + :calls: `GET /api/v1/accounts/:account_id/authentication_providers \ `_ @@ -1158,6 +1293,28 @@ def list_rubrics(self, **kwargs): """ Get the paginated list of active rubrics for the current account. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.account.Account.get_rubrics` instead. + + :calls: `GET /api/v1/accounts/:account_id/rubrics \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.rubric.Rubric` + """ + warnings.warn( + "`list_rubrics` is being deprecated and will be removed in a future version." + " Use `get_rubrics` instead", + DeprecationWarning + ) + + return self.get_rubrics(**kwargs) + + def get_rubrics(self, **kwargs): + """ + Get the paginated list of active rubrics for the current account. + :calls: `GET /api/v1/accounts/:account_id/rubrics \ `_ From b9436ec7a491667c514948e3755e007215521390 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Wed, 7 Mar 2018 17:46:18 -0500 Subject: [PATCH 22/91] Deprecated list_ methods in canvas --- canvasapi/canvas.py | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 2f98dcf1..e7f6fe27 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -665,6 +665,28 @@ def list_calendar_events(self, **kwargs): """ List calendar events. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.canvas.Canvas.get_calendar_events` instead. + + :calls: `GET /api/v1/calendar_events \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.calendar_event.CalendarEvent` + """ + warnings.warn( + "`list_calendar_events` is being deprecated and will be removed in a future version." + " Use `get_calendar_events` instead", + DeprecationWarning + ) + + return self.get_calendar_events(**kwargs) + + def get_calendar_events(self, **kwargs): + """ + List calendar events. + :calls: `GET /api/v1/calendar_events \ `_ @@ -740,6 +762,29 @@ def list_appointment_groups(self, **kwargs): """ List appointment groups. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.canvas.Canvas.get_appointment_groups` instead. + + :calls: `GET /api/v1/appointment_groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.appointment_group.AppointmentGroup` + """ + warnings.warn( + "`list_appointment_groups`" + " is being deprecated and will be removed in a future version." + " Use `get_appointment_groups` instead", + DeprecationWarning + ) + + return self.get_appointment_groups(**kwargs) + + def get_appointment_groups(self, **kwargs): + """ + List appointment groups. + :calls: `GET /api/v1/appointment_groups \ `_ @@ -825,6 +870,30 @@ def list_user_participants(self, appointment_group, **kwargs): """ List user participants in this appointment group. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi. canvas.Canvas.get_user_participants` instead. + + :calls: `GET /api/v1/appointment_groups/:id/users \ + `_ + + :param appointment_group: The object or ID of the appointment group. + :type appointment_group: :class:`canvasapi.appointment_group.AppointmentGroup` or int + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.user.User` + """ + warnings.warn( + "`list_user_participants` is being deprecated and will be removed in a future version." + " Use `get_user_participants` instead", + DeprecationWarning + ) + + return self.get_user_participants(appointment_group, **kwargs) + + def get_user_participants(self, appointment_group, **kwargs): + """ + List user participants in this appointment group. + :calls: `GET /api/v1/appointment_groups/:id/users \ `_ @@ -852,6 +921,31 @@ def list_group_participants(self, appointment_group, **kwargs): """ List student group participants in this appointment group. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi. canvas.Canvas.get_group_participants` instead. + + :calls: `GET /api/v1/appointment_groups/:id/groups \ + `_ + + :param appointment_group: The object or ID of the appointment group. + :type appointment_group: :class:`canvasapi.appointment_group.AppointmentGroup` or int + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` + """ + warnings.warn( + "`list_group_participants`" + " is being deprecated and will be removed in a future version." + " Use `get_group_participants` instead", + DeprecationWarning + ) + + return self.get_group_participants(appointment_group, **kwargs) + + def get_group_participants(self, appointment_group, **kwargs): + """ + List student group participants in this appointment group. + :calls: `GET /api/v1/appointment_groups/:id/groups \ `_ From 4e2c25c6d52d46dee29e8ad855af14d550bbb34a Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Wed, 7 Mar 2018 17:48:37 -0500 Subject: [PATCH 23/91] Added unit tests for refactored get_ methods in account --- canvasapi/account.py | 5 +- tests/fixtures/account.json | 16 ++-- tests/test_account.py | 139 ++++++++++++++++++++++++++++++++--- tests/test_paginated_list.py | 4 +- 4 files changed, 140 insertions(+), 24 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index fbf801ce..397418f8 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -620,7 +620,7 @@ def create_group_category(self, name, **kwargs): ) return GroupCategory(self._requester, response.json()) - def list_group_categories(self, kwargs): + def list_group_categories(self, **kwargs): """ List group categories for a context @@ -659,7 +659,8 @@ def get_group_categories(self, **kwargs): GroupCategory, self._requester, 'GET', - 'accounts/{}/group_categories'.format(self.id) + 'accounts/{}/group_categories'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) def create_external_tool(self, name, privacy_level, consumer_key, shared_secret, **kwargs): diff --git a/tests/fixtures/account.json b/tests/fixtures/account.json index e12a3e64..115314db 100644 --- a/tests/fixtures/account.json +++ b/tests/fixtures/account.json @@ -288,7 +288,7 @@ ], "status_code": 200 }, - "list_enrollment_terms": { + "get_enrollment_terms": { "method": "GET", "endpoint": "accounts/1/terms", "data": { @@ -304,7 +304,7 @@ }, "status_code": 200 }, - "list_groups_context": { + "get_groups_context": { "method": "GET", "endpoint": "accounts/1/groups", "data": [ @@ -322,7 +322,7 @@ "Link": "; rel=\"next\"" } }, - "list_groups_context2": { + "get_groups_context2": { "method": "GET", "endpoint": "accounts/1/groups?page=2&per_page=2", "data": [ @@ -599,7 +599,7 @@ }, "status_code": 200 }, - "list_group_categories": { + "get_group_categories": { "method": "GET", "endpoint": "accounts/1/group_categories", "data": [ @@ -628,7 +628,7 @@ ], "status_code": 200 }, - "list_roles": { + "get_roles": { "method": "GET", "endpoint": "accounts/1/roles", "data": [ @@ -648,7 +648,7 @@ }, "status_code": 200 }, - "list_roles_2": { + "get_roles_2": { "method": "GET", "endpoint": "accounts/1/roles/?page=2&per_page=2", "data": [ @@ -705,7 +705,7 @@ }, "status_code": 200 }, - "list_user_logins": { + "get_user_logins": { "method": "GET", "endpoint": "accounts/1/logins", "data": [ @@ -724,7 +724,7 @@ }, "status_code": 200 }, - "list_user_logins_2": { + "get_user_logins_2": { "method": "GET", "endpoint": "accounts/1/logins/?page=2&per_page=2", "data": [ diff --git a/tests/test_account.py b/tests/test_account.py index cdcd25f4..9ab8f855 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -2,6 +2,7 @@ import datetime import pytz import unittest +import warnings import requests_mock @@ -291,11 +292,29 @@ def test_update_fail(self, m): self.assertFalse(self.account.update(account=update_account_dict)) + # list_roles() def test_list_roles(self, m): - requires = {'account': ['list_roles', 'list_roles_2']} + requires = {'account': ['get_roles', 'get_roles_2']} register_uris(requires, m) - roles = self.account.list_roles() + with warnings.catch_warnings(record=True) as warning_list: + roles = self.account.list_roles() + role_list = [role for role in roles] + + self.assertEqual(len(role_list), 4) + self.assertIsInstance(role_list[0], Role) + self.assertTrue(hasattr(role_list[0], 'role')) + self.assertTrue(hasattr(role_list[0], 'label')) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_roles() + def test_get_roles(self, m): + requires = {'account': ['get_roles', 'get_roles_2']} + register_uris(requires, m) + + roles = self.account.get_roles() role_list = [role for role in roles] self.assertEqual(len(role_list), 4) @@ -373,11 +392,27 @@ def test_get_enrollment(self, m): enrollment_by_obj = self.account.get_enrollment(enrollment_by_id) self.assertIsInstance(enrollment_by_obj, Enrollment) + # list_groups() def test_list_groups(self, m): - requires = {'account': ['list_groups_context', 'list_groups_context2']} + requires = {'account': ['get_groups_context', 'get_groups_context2']} + register_uris(requires, m) + + with warnings.catch_warnings(record=True) as warning_list: + groups = self.account.list_groups() + group_list = [group for group in groups] + + self.assertIsInstance(group_list[0], Group) + self.assertEqual(len(group_list), 4) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_groups() + def test_get_groups(self, m): + requires = {'account': ['get_groups_context', 'get_groups_context2']} register_uris(requires, m) - groups = self.account.list_groups() + groups = self.account.get_groups() group_list = [group for group in groups] self.assertIsInstance(group_list[0], Group) @@ -393,9 +428,22 @@ def test_create_group_category(self, m): # list_group_categories() def test_list_group_categories(self, m): - register_uris({'account': ['list_group_categories']}, m) + register_uris({'account': ['get_group_categories']}, m) + + with warnings.catch_warnings(record=True) as warning_list: + response = self.account.list_group_categories() + category_list = [category for category in response] + + self.assertIsInstance(category_list[0], GroupCategory) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_group_categories() + def test_get_group_categories(self, m): + register_uris({'account': ['get_group_categories']}, m) - response = self.account.list_group_categories() + response = self.account.get_group_categories() category_list = [category for category in response] self.assertIsInstance(category_list[0], GroupCategory) @@ -430,19 +478,47 @@ def test_create_enrollment_term(self, m): # list_enrollment_terms() def test_list_enrollment_terms(self, m): - register_uris({'account': ['list_enrollment_terms']}, m) + register_uris({'account': ['get_enrollment_terms']}, m) + + with warnings.catch_warnings(record=True) as warning_list: + response = self.account.list_enrollment_terms() + enrollment_terms_list = [category for category in response] + + self.assertIsInstance(enrollment_terms_list[0], EnrollmentTerm) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_enrollment_terms() + def test_get_enrollment_terms(self, m): + register_uris({'account': ['get_enrollment_terms']}, m) - response = self.account.list_enrollment_terms() + response = self.account.get_enrollment_terms() enrollment_terms_list = [category for category in response] self.assertIsInstance(enrollment_terms_list[0], EnrollmentTerm) # list_user_logins() def test_list_user_logins(self, m): - requires = {'account': ['list_user_logins', 'list_user_logins_2']} + requires = {'account': ['get_user_logins', 'get_user_logins_2']} register_uris(requires, m) - response = self.account.list_user_logins() + with warnings.catch_warnings(record=True) as warning_list: + response = self.account.list_user_logins() + login_list = [login for login in response] + + self.assertIsInstance(login_list[0], Login) + self.assertEqual(len(login_list), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_user_logins() + def test_get_user_logins(self, m): + requires = {'account': ['get_user_logins', 'get_user_logins_2']} + register_uris(requires, m) + + response = self.account.get_user_logins() login_list = [login for login in response] self.assertIsInstance(login_list[0], Login) @@ -551,7 +627,27 @@ def test_list_authentication_providers(self, m): 'list_authentication_providers_2']} register_uris(requires, m) - authentication_providers = self.account.list_authentication_providers() + with warnings.catch_warnings(record=True) as warning_list: + authentication_providers = self.account.list_authentication_providers() + authentication_providers_list = [ + authentication_provider for authentication_provider in authentication_providers + ] + + self.assertEqual(len(authentication_providers_list), 4) + self.assertIsInstance(authentication_providers_list[0], AuthenticationProvider) + self.assertTrue(hasattr(authentication_providers_list[0], 'auth_type')) + self.assertTrue(hasattr(authentication_providers_list[0], 'position')) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_authentication_providers() + def test_get_authentication_providers(self, m): + requires = {'account': ['list_authentication_providers', + 'list_authentication_providers_2']} + register_uris(requires, m) + + authentication_providers = self.account.get_authentication_providers() authentication_providers_list = [ authentication_provider for authentication_provider in authentication_providers ] @@ -723,7 +819,26 @@ def test_get_rubric(self, m): def test_list_rubrics(self, m): register_uris({'account': ['get_rubric_multiple']}, m) - rubrics = self.account.list_rubrics() + with warnings.catch_warnings(record=True) as warning_list: + rubrics = self.account.list_rubrics() + + self.assertEqual(len(list(rubrics)), 2) + + self.assertIsInstance(rubrics[0], Rubric) + self.assertEqual(rubrics[0].id, 1) + self.assertEqual(rubrics[0].title, "Account Rubric 1") + self.assertIsInstance(rubrics[1], Rubric) + self.assertEqual(rubrics[1].id, 2) + self.assertEqual(rubrics[1].title, "Account Rubric 2") + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_rubrics + def test_get_rubrics(self, m): + register_uris({'account': ['get_rubric_multiple']}, m) + + rubrics = self.account.get_rubrics() self.assertEqual(len(list(rubrics)), 2) diff --git a/tests/test_paginated_list.py b/tests/test_paginated_list.py index 8fd65211..43dad819 100644 --- a/tests/test_paginated_list.py +++ b/tests/test_paginated_list.py @@ -208,7 +208,7 @@ def test_repr(self, m): self.assertEqual(pag_list.__repr__(), '') def test_root_element_incorrect(self, m): - register_uris({'account': ['list_enrollment_terms']}, m) + register_uris({'account': ['get_enrollment_terms']}, m) pag_list = PaginatedList( EnrollmentTerm, @@ -222,7 +222,7 @@ def test_root_element_incorrect(self, m): pag_list[0] def test_root_element(self, m): - register_uris({'account': ['list_enrollment_terms']}, m) + register_uris({'account': ['get_enrollment_terms']}, m) pag_list = PaginatedList( EnrollmentTerm, From 24aadf8164ca35eeb78b5d02d49252dccbba2a79 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Wed, 7 Mar 2018 17:58:33 -0500 Subject: [PATCH 24/91] Added unit tests for refactored get_ methods in canvas --- tests/test_canvas.py | 98 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/tests/test_canvas.py b/tests/test_canvas.py index c3c59c1d..bd23f733 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -464,7 +464,19 @@ def test_create_calendar_event_fail(self, m): def test_list_calendar_events(self, m): register_uris({'calendar_event': ['list_calendar_events']}, m) - cal_events = self.canvas.list_calendar_events() + with warnings.catch_warnings(record=True) as warning_list: + cal_events = self.canvas.list_calendar_events() + cal_event_list = [cal_event for cal_event in cal_events] + self.assertEqual(len(cal_event_list), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_calendar_events() + def test_get_calendar_events(self, m): + register_uris({'calendar_event': ['list_calendar_events']}, m) + + cal_events = self.canvas.get_calendar_events() cal_event_list = [cal_event for cal_event in cal_events] self.assertEqual(len(cal_event_list), 2) @@ -506,7 +518,19 @@ def test_reserve_time_slot_by_participant_id(self, m): def test_list_appointment_groups(self, m): register_uris({'appointment_group': ['list_appointment_groups']}, m) - appt_groups = self.canvas.list_appointment_groups() + with warnings.catch_warnings(record=True) as warning_list: + appt_groups = self.canvas.list_appointment_groups() + appt_groups_list = [appt_group for appt_group in appt_groups] + self.assertEqual(len(appt_groups_list), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_appointment_groups() + def test_get_appointment_groups(self, m): + register_uris({'appointment_group': ['list_appointment_groups']}, m) + + appt_groups = self.canvas.get_appointment_groups() appt_groups_list = [appt_group for appt_group in appt_groups] self.assertEqual(len(appt_groups_list), 2) @@ -555,14 +579,37 @@ def test_list_user_participants(self, m): ] }, m) - users_by_id = self.canvas.list_user_participants(222) - users_list_by_id = [user for user in users_by_id] - self.assertEqual(len(users_list_by_id), 2) + with warnings.catch_warnings(record=True) as warning_list: + users_by_id = self.canvas.list_user_participants(222) + users_list_by_id = [user for user in users_by_id] + self.assertEqual(len(users_list_by_id), 2) + + appointment_group_for_obj = self.canvas.get_appointment_group(222) + users_by_id = self.canvas.list_user_participants(appointment_group_for_obj) + users_list_by_id = [user for user in users_by_id] + self.assertEqual(len(users_list_by_id), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_user_participants() + def test_get_user_participants(self, m): + register_uris( + { + 'appointment_group': [ + 'get_appointment_group_222', + 'list_user_participants' + ] + }, m) + + users_by_id = self.canvas.get_user_participants(222) + users_get_by_id = [user for user in users_by_id] + self.assertEqual(len(users_get_by_id), 2) appointment_group_for_obj = self.canvas.get_appointment_group(222) - users_by_id = self.canvas.list_user_participants(appointment_group_for_obj) - users_list_by_id = [user for user in users_by_id] - self.assertEqual(len(users_list_by_id), 2) + users_by_id = self.canvas.get_user_participants(appointment_group_for_obj) + users_get_by_id = [user for user in users_by_id] + self.assertEqual(len(users_get_by_id), 2) # list_group_participants() def test_list_group_participants(self, m): @@ -574,14 +621,37 @@ def test_list_group_participants(self, m): ] }, m) - groups_by_id = self.canvas.list_group_participants(222) - groups_list_by_id = [group for group in groups_by_id] - self.assertEqual(len(groups_list_by_id), 2) + with warnings.catch_warnings(record=True) as warning_list: + groups_by_id = self.canvas.list_group_participants(222) + groups_list_by_id = [group for group in groups_by_id] + self.assertEqual(len(groups_list_by_id), 2) + + appointment_group_for_obj = self.canvas.get_appointment_group(222) + groups_by_obj = self.canvas.list_group_participants(appointment_group_for_obj) + groups_list_by_obj = [group for group in groups_by_obj] + self.assertEqual(len(groups_list_by_obj), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_group_participants() + def test_get_group_participants(self, m): + register_uris( + { + 'appointment_group': [ + 'get_appointment_group_222', + 'list_group_participants' + ] + }, m) + + groups_by_id = self.canvas.get_group_participants(222) + groups_get_by_id = [group for group in groups_by_id] + self.assertEqual(len(groups_get_by_id), 2) appointment_group_for_obj = self.canvas.get_appointment_group(222) - groups_by_obj = self.canvas.list_group_participants(appointment_group_for_obj) - groups_list_by_obj = [group for group in groups_by_obj] - self.assertEqual(len(groups_list_by_obj), 2) + groups_by_obj = self.canvas.get_group_participants(appointment_group_for_obj) + groups_get_by_obj = [group for group in groups_by_obj] + self.assertEqual(len(groups_get_by_obj), 2) # get_file() def test_get_file(self, m): From a16f7886eeb9eb43b61728ed3dac052f164df0ae Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 8 Mar 2018 11:32:01 -0500 Subject: [PATCH 25/91] Deprecated list_ methods in communication_channels --- canvasapi/communication_channel.py | 58 ++++++++++++++++++++++++++++- docs/class-reference.rst | 1 + docs/communication-channel-ref.rst | 6 +++ tests/test_communication_channel.py | 33 +++++++++++++++- 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 docs/communication-channel-ref.rst diff --git a/canvasapi/communication_channel.py b/canvasapi/communication_channel.py index 34c22fa5..6d1c838b 100644 --- a/canvasapi/communication_channel.py +++ b/canvasapi/communication_channel.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import warnings + from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject @@ -13,7 +15,31 @@ class CommunicationChannel(CanvasObject): def __str__(self): return "{} ({})".format(self.address, self.id) - def list_preferences(self): + def list_preferences(self, **kwargs): + """ + Fetch all preferences for the given communication channel. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.communication_channel.CommunicationChannel.get_preferences` + instead. + + :calls: `GET + /api/v1/users/:user_id/communication_channels/:communication_channel_id/ \ + notification_preferences \ + `_ + + :rtype: `list` + """ + warnings.warn( + "`list_preferences` is being deprecated and will be removed in a future version." + " Use `get_preferences` instead", + DeprecationWarning + ) + + return self.get_preferences(**kwargs) + + def get_preferences(self, **kwargs): """ Fetch all preferences for the given communication channel. @@ -31,9 +57,37 @@ def list_preferences(self): self.id ) ) + return response.json()['notification_preferences'] - def list_preference_categories(self): + def list_preference_categories(self, **kwargs): + """ + Fetch all notification preference categories for the given communication + channel. + + .. warning:: + .. deprecated:: 0.10.0 + Use + :func:`canvasapi.communication_channel.CommunicationChannel.get_preference_categories` + instead. + + :calls: `GET + /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ + notification_preference_categories \ + `_ + + :rtype: `list` + """ + warnings.warn( + "`list_preference_categories`" + " is being deprecated and will be removed in a future version." + " Use `get_preference_categories` instead", + DeprecationWarning + ) + + return self.get_preference_categories(**kwargs) + + def get_preference_categories(self, **kwargs): """ Fetch all notification preference categories for the given communication channel. diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 38f1046a..8b4dd56e 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -11,6 +11,7 @@ Class Reference avatar-ref bookmark-ref calendar-event-ref + communication-channel-ref conversation-ref course-ref current-user-ref diff --git a/docs/communication-channel-ref.rst b/docs/communication-channel-ref.rst new file mode 100644 index 00000000..17fcbd07 --- /dev/null +++ b/docs/communication-channel-ref.rst @@ -0,0 +1,6 @@ +==================== +CommunicationChannel +==================== + +.. autoclass:: canvasapi.communication_channel.CommunicationChannel + :members: diff --git a/tests/test_communication_channel.py b/tests/test_communication_channel.py index b0bb10fa..f72fc513 100644 --- a/tests/test_communication_channel.py +++ b/tests/test_communication_channel.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.notification_preference import NotificationPreference @@ -30,7 +31,21 @@ def test__str__(self, m): def test_list_preferences(self, m): register_uris({'communication_channel': ['list_preferences']}, m) - preferences = self.comm_chan.list_preferences() + with warnings.catch_warnings(record=True) as warning_list: + preferences = self.comm_chan.list_preferences() + preference_list = [preference for preference in preferences] + + self.assertEqual(len(preference_list), 2) + self.assertEqual(preference_list[0]['notification'], 'new_announcement') + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_preferences() + def test_get_preferences(self, m): + register_uris({'communication_channel': ['list_preferences']}, m) + + preferences = self.comm_chan.get_preferences() preference_list = [preference for preference in preferences] self.assertEqual(len(preference_list), 2) @@ -40,7 +55,21 @@ def test_list_preferences(self, m): def test_list_preference_categories(self, m): register_uris({'communication_channel': ['list_preference_categories']}, m) - categories = self.comm_chan.list_preference_categories() + with warnings.catch_warnings(record=True) as warning_list: + categories = self.comm_chan.list_preference_categories() + + self.assertEqual(len(categories), 2) + self.assertIsInstance(categories, list) + self.assertEqual(categories[0], 'announcement') + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_preference_categories() + def test_get_preference_categories(self, m): + register_uris({'communication_channel': ['list_preference_categories']}, m) + + categories = self.comm_chan.get_preference_categories() self.assertEqual(len(categories), 2) self.assertIsInstance(categories, list) From ddbea1529ba27a6ebb6aab6adda16211cbbee559 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 8 Mar 2018 11:37:59 -0500 Subject: [PATCH 26/91] Added kwargs to get_ methods --- canvasapi/communication_channel.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/canvasapi/communication_channel.py b/canvasapi/communication_channel.py index 6d1c838b..142308b6 100644 --- a/canvasapi/communication_channel.py +++ b/canvasapi/communication_channel.py @@ -55,7 +55,8 @@ def get_preferences(self, **kwargs): 'users/{}/communication_channels/{}/notification_preferences'.format( self.user_id, self.id - ) + ), + _kwargs=combine_kwargs(**kwargs) ) return response.json()['notification_preferences'] @@ -104,7 +105,8 @@ def get_preference_categories(self, **kwargs): 'users/{}/communication_channels/{}/notification_preference_categories'.format( self.user_id, self.id - ) + ), + _kwargs=combine_kwargs(**kwargs) ) return response.json()['categories'] From 6b5056cae26b196679683df6cf54436258d2468e Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 8 Mar 2018 12:16:20 -0500 Subject: [PATCH 27/91] Deprecated list_ methods in courses --- canvasapi/course.py | 217 +++++++++++++++++++++++++++++++++++- tests/test_course.py | 158 +++++++++++++++++++++++--- tests/test_external_feed.py | 2 +- tests/test_file.py | 2 +- tests/test_tab.py | 2 +- 5 files changed, 359 insertions(+), 22 deletions(-) diff --git a/canvasapi/course.py b/canvasapi/course.py index 73c55e6c..74dd2294 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -737,6 +737,28 @@ def list_groups(self, **kwargs): """ Return list of active groups for the specified course. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_groups` instead. + + :calls: `GET /api/v1/courses/:course_id/groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.course.Course` + """ + warnings.warn( + "`list_groups` is being deprecated and will be removed in a future version." + " Use `get_groups` instead", + DeprecationWarning + ) + + return self.get_groups(**kwargs) + + def get_groups(self, **kwargs): + """ + Return list of active groups for the specified course. + :calls: `GET /api/v1/courses/:course_id/groups \ `_ @@ -773,7 +795,29 @@ def create_group_category(self, name, **kwargs): ) return GroupCategory(self._requester, response.json()) - def list_group_categories(self): + def list_group_categories(self, **kwargs): + """ + List group categories for a context. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_group_categories` instead. + + :calls: `GET /api/v1/courses/:course_id/group_categories \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.group.GroupCategory` + """ + warnings.warn( + "`list_group_categories` is being deprecated and will be removed in a future version." + " Use `get_group_categories` instead", + DeprecationWarning + ) + + return self.get_group_categories(**kwargs) + + def get_group_categories(self, **kwargs): """ List group categories for a context. @@ -789,7 +833,8 @@ def list_group_categories(self): GroupCategory, self._requester, 'GET', - 'courses/{}/group_categories'.format(self.id) + 'courses/{}/group_categories'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) def get_file(self, file, **kwargs): @@ -908,6 +953,28 @@ def list_assignment_groups(self, **kwargs): """ List assignment groups for the specified course. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_assignment_groups` instead. + + :calls: `GET /api/v1/courses/:course_id/assignment_groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.assignment.AssignmentGroup` + """ + warnings.warn( + "`list_assignment_groups` is being deprecated and will be removed in a future version." + " Use `get_assignment_groups` instead", + DeprecationWarning + ) + + return self.get_assignment_groups(**kwargs) + + def get_assignment_groups(self, **kwargs): + """ + List assignment groups for the specified course. + :calls: `GET /api/v1/courses/:course_id/assignment_groups \ `_ @@ -1228,6 +1295,30 @@ def list_multiple_submissions(self, **kwargs): List submissions for multiple assignments. Get all existing submissions for a given set of students and assignments. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_multiple_submissions` instead. + + :calls: `GET /api/v1/courses/:course_id/students/submissions \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.submission.Submission` + """ + warnings.warn( + "`list_multiple_submissions`" + " is being deprecated and will be removed in a future version." + " Use `get_multiple_submissions` instead", + DeprecationWarning + ) + + return self.get_multiple_submissions(**kwargs) + + def get_multiple_submissions(self, **kwargs): + """ + List submissions for multiple assignments. + Get all existing submissions for a given set of students and assignments. + :calls: `GET /api/v1/courses/:course_id/students/submissions \ `_ @@ -1430,7 +1521,29 @@ def mark_submission_as_unread(self, assignment, user, **kwargs): }) return submission.mark_unread(**kwargs) - def list_external_feeds(self): + def list_external_feeds(self, **kwargs): + """ + Returns the list of External Feeds this course. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_external_feeds` instead. + + :calls: `GET /api/v1/courses/:course_id/external_feeds \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.external_feed.ExternalFeed` + """ + warnings.warn( + "`list_external_feeds` is being deprecated and will be removed in a future version." + " Use `get_external_feeds` instead", + DeprecationWarning + ) + + return self.get_external_feeds(**kwargs) + + def get_external_feeds(self, **kwargs): """ Returns the list of External Feeds this course. @@ -1445,7 +1558,8 @@ def list_external_feeds(self): ExternalFeed, self._requester, 'GET', - 'courses/{}/external_feeds'.format(self.id) + 'courses/{}/external_feeds'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) def create_external_feed(self, url, **kwargs): @@ -1494,6 +1608,28 @@ def list_files(self, **kwargs): """ Returns the paginated list of files for the course. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_files` instead. + + :calls: `GET api/v1/courses/:course_id/files \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.file.File` + """ + warnings.warn( + "`list_files` is being deprecated and will be removed in a future version." + " Use `get_files` instead", + DeprecationWarning + ) + + return self.get_files(**kwargs) + + def get_files(self, **kwargs): + """ + Returns the paginated list of files for the course. + :calls: `GET api/v1/courses/:course_id/files \ `_ @@ -1530,7 +1666,30 @@ def get_folder(self, folder): ) return Folder(self._requester, response.json()) - def list_folders(self): + def list_folders(self, **kwargs): + """ + Returns the paginated list of all folders for the given course. This will be returned as a + flat list containing all subfolders as well. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_folders` instead. + + :calls: `GET /api/v1/courses/:course_id/folders \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.folder.Folder` + """ + warnings.warn( + "`list_folders` is being deprecated and will be removed in a future version." + " Use `get_folders` instead", + DeprecationWarning + ) + + return self.get_folders(**kwargs) + + def get_folders(self, **kwargs): """ Returns the paginated list of all folders for the given course. This will be returned as a flat list containing all subfolders as well. @@ -1545,7 +1704,8 @@ def list_folders(self): Folder, self._requester, 'GET', - 'courses/{}/folders'.format(self.id) + 'courses/{}/folders'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) def create_folder(self, name, **kwargs): @@ -1572,6 +1732,29 @@ def list_tabs(self, **kwargs): List available tabs for a course. Returns a list of navigation tabs available in the current context. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_tabs` instead. + + :calls: `GET /api/v1/courses/:course_id/tabs \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.tab.Tab` + """ + warnings.warn( + "`list_tabs` is being deprecated and will be removed in a future version." + " Use `get_tabs` instead", + DeprecationWarning + ) + + return self.get_tabs(**kwargs) + + def get_tabs(self, **kwargs): + """ + List available tabs for a course. + Returns a list of navigation tabs available in the current context. + :calls: `GET /api/v1/courses/:course_id/tabs \ `_ @@ -1629,6 +1812,28 @@ def list_rubrics(self, **kwargs): """ Get the paginated list of active rubrics for the current course. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.course.Course.get_rubrics` instead. + + :calls: `GET /api/v1/courses/:course_id/rubrics \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.rubric.Rubric` + """ + warnings.warn( + "`list_rubrics` is being deprecated and will be removed in a future version." + " Use `get_rubrics` instead", + DeprecationWarning + ) + + return self.get_rubrics(**kwargs) + + def get_rubrics(self, **kwargs): + """ + Get the paginated list of active rubrics for the current course. + :calls: `GET /api/v1/courses/:course_id/rubrics \ `_ diff --git a/tests/test_course.py b/tests/test_course.py index b9fda7fe..f933cb4f 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -463,11 +463,27 @@ def test_create_course_section(self, m): self.assertIsInstance(section, Section) + # list_groups() def test_list_groups(self, m): requires = {'course': ['list_groups_context', 'list_groups_context2']} register_uris(requires, m) - groups = self.course.list_groups() + with warnings.catch_warnings(record=True) as warning_list: + groups = self.course.list_groups() + group_list = [group for group in groups] + + self.assertIsInstance(group_list[0], Group) + self.assertEqual(len(group_list), 4) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_groups() + def test_get_groups(self, m): + requires = {'course': ['list_groups_context', 'list_groups_context2']} + register_uris(requires, m) + + groups = self.course.get_groups() group_list = [group for group in groups] self.assertIsInstance(group_list[0], Group) @@ -485,7 +501,19 @@ def test_create_group_category(self, m): def test_list_group_categories(self, m): register_uris({'course': ['list_group_categories']}, m) - response = self.course.list_group_categories() + with warnings.catch_warnings(record=True) as warning_list: + response = self.course.list_group_categories() + category_list = [category for category in response] + self.assertIsInstance(category_list[0], GroupCategory) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_group_categories() + def test_get_group_categories(self, m): + register_uris({'course': ['list_group_categories']}, m) + + response = self.course.get_group_categories() category_list = [category for category in response] self.assertIsInstance(category_list[0], GroupCategory) @@ -621,13 +649,31 @@ def test_get_assignment_group(self, m): self.assertTrue(hasattr(assignment_group_by_obj, 'course_id')) self.assertEqual(assignment_group_by_obj.course_id, 1) - # list_group_categories() + # list_assignment_groups() def test_list_assignment_groups(self, m): register_uris({ 'assignment': ['list_assignment_groups', 'get_assignment_group'] }, m) - response = self.course.list_assignment_groups() + with warnings.catch_warnings(record=True) as warning_list: + response = self.course.list_assignment_groups() + asnt_group_list = [assignment_group for assignment_group in response] + self.assertIsInstance(asnt_group_list[0], AssignmentGroup) + self.assertTrue(hasattr(asnt_group_list[0], 'id')) + self.assertTrue(hasattr(asnt_group_list[0], 'name')) + self.assertTrue(hasattr(asnt_group_list[0], 'course_id')) + self.assertEqual(asnt_group_list[0].course_id, 1) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_assignment_groups() + def test_get_assignment_groups(self, m): + register_uris({ + 'assignment': ['list_assignment_groups', 'get_assignment_group'] + }, m) + + response = self.course.get_assignment_groups() asnt_group_list = [assignment_group for assignment_group in response] self.assertIsInstance(asnt_group_list[0], AssignmentGroup) self.assertTrue(hasattr(asnt_group_list[0], 'id')) @@ -778,18 +824,32 @@ def test_list_submissions(self, m): def test_list_multiple_submissions(self, m): register_uris({'course': ['list_multiple_submissions']}, m) - submissions = self.course.list_multiple_submissions() + with warnings.catch_warnings(record=True) as warning_list: + submissions = self.course.list_multiple_submissions() + submission_list = [submission for submission in submissions] + + self.assertEqual(len(submission_list), 2) + self.assertIsInstance(submission_list[0], Submission) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_multiple_submission() + def test_get_multiple_submissions(self, m): + register_uris({'course': ['list_multiple_submissions']}, m) + + submissions = self.course.get_multiple_submissions() submission_list = [submission for submission in submissions] self.assertEqual(len(submission_list), 2) self.assertIsInstance(submission_list[0], Submission) - def test_list_multiple_submissions_grouped_param(self, m): + def test_get_multiple_submissions_grouped_param(self, m): register_uris({'course': ['list_multiple_submissions']}, m) with warnings.catch_warnings(record=True) as warning_list: warnings.simplefilter('always') - submissions = self.course.list_multiple_submissions(grouped=True) + submissions = self.course.get_multiple_submissions(grouped=True) submission_list = [submission for submission in submissions] # Ensure using the `grouped` param raises a warning @@ -943,7 +1003,21 @@ def test_mark_submission_as_unread(self, m): def test_list_external_feeds(self, m): register_uris({'course': ['list_external_feeds']}, m) - feeds = self.course.list_external_feeds() + with warnings.catch_warnings(record=True) as warning_list: + feeds = self.course.list_external_feeds() + feed_list = [feed for feed in feeds] + self.assertEqual(len(feed_list), 2) + self.assertTrue(hasattr(feed_list[0], 'url')) + self.assertIsInstance(feed_list[0], ExternalFeed) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_external_feeds() + def test_get_external_feeds(self, m): + register_uris({'course': ['list_external_feeds']}, m) + + feeds = self.course.get_external_feeds() feed_list = [feed for feed in feeds] self.assertEqual(len(feed_list), 2) self.assertTrue(hasattr(feed_list[0], 'url')) @@ -973,10 +1047,23 @@ def test_delete_external_feed(self, m): self.assertEqual(deleted_ef_by_obj.display_name, "My Blog") # list_files() - def test_course_files(self, m): + def test_list_files(self, m): register_uris({'course': ['list_course_files', 'list_course_files2']}, m) - files = self.course.list_files() + with warnings.catch_warnings(record=True) as warning_list: + files = self.course.list_files() + file_list = [file for file in files] + self.assertEqual(len(file_list), 4) + self.assertIsInstance(file_list[0], File) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_files() + def test_get_files(self, m): + register_uris({'course': ['list_course_files', 'list_course_files2']}, m) + + files = self.course.get_files() file_list = [file for file in files] self.assertEqual(len(file_list), 4) self.assertIsInstance(file_list[0], File) @@ -997,7 +1084,20 @@ def test_get_folder(self, m): def test_list_folders(self, m): register_uris({'course': ['list_folders']}, m) - folders = self.course.list_folders() + with warnings.catch_warnings(record=True) as warning_list: + folders = self.course.list_folders() + folder_list = [folder for folder in folders] + self.assertEqual(len(folder_list), 2) + self.assertIsInstance(folder_list[0], Folder) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_folders() + def test_get_folders(self, m): + register_uris({'course': ['list_folders']}, m) + + folders = self.course.get_folders() folder_list = [folder for folder in folders] self.assertEqual(len(folder_list), 2) self.assertIsInstance(folder_list[0], Folder) @@ -1014,7 +1114,20 @@ def test_create_folder(self, m): def test_list_tabs(self, m): register_uris({'course': ['list_tabs']}, m) - tabs = self.course.list_tabs() + with warnings.catch_warnings(record=True) as warning_list: + tabs = self.course.list_tabs() + tab_list = [tab for tab in tabs] + self.assertEqual(len(tab_list), 2) + self.assertIsInstance(tab_list[0], Tab) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_tabs() + def test_get_tabs(self, m): + register_uris({'course': ['list_tabs']}, m) + + tabs = self.course.get_tabs() tab_list = [tab for tab in tabs] self.assertEqual(len(tab_list), 2) self.assertIsInstance(tab_list[0], Tab) @@ -1045,7 +1158,26 @@ def test_get_rubric(self, m): def test_list_rubrics(self, m): register_uris({'course': ['get_rubric_multiple']}, m) - rubrics = self.course.list_rubrics() + with warnings.catch_warnings(record=True) as warning_list: + rubrics = self.course.list_rubrics() + + self.assertEqual(len(list(rubrics)), 2) + + self.assertIsInstance(rubrics[0], Rubric) + self.assertEqual(rubrics[0].id, 1) + self.assertEqual(rubrics[0].title, "Course Rubric 1") + self.assertIsInstance(rubrics[1], Rubric) + self.assertEqual(rubrics[1].id, 2) + self.assertEqual(rubrics[1].title, "Course Rubric 2") + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_rubrics + def test_get_rubrics(self, m): + register_uris({'course': ['get_rubric_multiple']}, m) + + rubrics = self.course.get_rubrics() self.assertEqual(len(list(rubrics)), 2) diff --git a/tests/test_external_feed.py b/tests/test_external_feed.py index 38baf88e..2f64df45 100644 --- a/tests/test_external_feed.py +++ b/tests/test_external_feed.py @@ -18,7 +18,7 @@ def setUp(self): register_uris({'course': ['get_by_id', 'list_external_feeds']}, m) self.course = self.canvas.get_course(1) - self.external_feed = self.course.list_external_feeds()[0] + self.external_feed = self.course.get_external_feeds()[0] # __str__() def test__str__(self, m): diff --git a/tests/test_file.py b/tests/test_file.py index 0c32bce3..483bfdcb 100644 --- a/tests/test_file.py +++ b/tests/test_file.py @@ -21,7 +21,7 @@ def setUp(self): register_uris({'course': ['get_by_id', 'list_course_files', 'list_course_files2']}, m) self.course = self.canvas.get_course(1) - self.file = self.course.list_files()[0] + self.file = self.course.get_files()[0] # __str__() def test__str__(self, m): diff --git a/tests/test_tab.py b/tests/test_tab.py index 91eaabc5..7a85b122 100644 --- a/tests/test_tab.py +++ b/tests/test_tab.py @@ -21,7 +21,7 @@ def setUp(self): self.course = self.canvas.get_course(1) - tabs = self.course.list_tabs() + tabs = self.course.get_tabs() tab_list = [tab for tab in tabs] self.tab = tab_list[0] From 868a04ab3941b5d20da5027f2ae408881e2efbe7 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 8 Mar 2018 12:28:11 -0500 Subject: [PATCH 28/91] Deprecated list_ methods in current_user --- canvasapi/current_user.py | 45 ++++++++++++++++++++++++++++++++++++++ tests/test_current_user.py | 32 +++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/canvasapi/current_user.py b/canvasapi/current_user.py index 1607146e..6c34417e 100644 --- a/canvasapi/current_user.py +++ b/canvasapi/current_user.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import warnings + from six import python_2_unicode_compatible from canvasapi.bookmark import Bookmark @@ -27,6 +29,27 @@ def list_groups(self, **kwargs): """ Return the list of active groups for the user. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.current_user.CurrentUser.get_groups` instead. + + :calls: `GET /api/v1/users/self/groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` + """ + warnings.warn( + "`list_groups` is being deprecated and will be removed in a future version." + " Use `get_groups` instead", + DeprecationWarning + ) + + return self.get_groups(**kwargs) + + def get_groups(self, **kwargs): + """ + Return the list of active groups for the user. + :calls: `GET /api/v1/users/self/groups \ `_ @@ -46,6 +69,28 @@ def list_bookmarks(self, **kwargs): """ List bookmarks that the current user can view or manage. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.current_user.CurrentUser.get_bookmarks` instead. + + :calls: `GET /api/v1/users/self/bookmarks \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.bookmark.Bookmark` + """ + warnings.warn( + "`list_bookmarks` is being deprecated and will be removed in a future version." + " Use `get_bookmarks` instead", + DeprecationWarning + ) + + return self.get_bookmarks(**kwargs) + + def get_bookmarks(self, **kwargs): + """ + List bookmarks that the current user can view or manage. + :calls: `GET /api/v1/users/self/bookmarks \ `_ diff --git a/tests/test_current_user.py b/tests/test_current_user.py index e39173f0..c4a704b0 100644 --- a/tests/test_current_user.py +++ b/tests/test_current_user.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.bookmark import Bookmark @@ -27,7 +28,21 @@ def test__str__(self, m): def test_list_groups(self, m): register_uris({'current_user': ['list_groups', 'list_groups2']}, m) - groups = self.user.list_groups() + with warnings.catch_warnings(record=True) as warning_list: + groups = self.user.list_groups() + group_list = [group for group in groups] + + self.assertEqual(len(group_list), 4) + self.assertIsInstance(group_list[0], Group) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_groups() + def test_get_groups(self, m): + register_uris({'current_user': ['list_groups', 'list_groups2']}, m) + + groups = self.user.get_groups() group_list = [group for group in groups] self.assertEqual(len(group_list), 4) @@ -37,7 +52,20 @@ def test_list_groups(self, m): def test_list_bookmarks(self, m): register_uris({'bookmark': ['list_bookmarks']}, m) - bookmarks = self.user.list_bookmarks() + with warnings.catch_warnings(record=True) as warning_list: + bookmarks = self.user.list_bookmarks() + bookmark_list = [bookmark for bookmark in bookmarks] + self.assertEqual(len(bookmark_list), 2) + self.assertIsInstance(bookmark_list[0], Bookmark) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_bookmarks() + def test_get_bookmarks(self, m): + register_uris({'bookmark': ['list_bookmarks']}, m) + + bookmarks = self.user.get_bookmarks() bookmark_list = [bookmark for bookmark in bookmarks] self.assertEqual(len(bookmark_list), 2) self.assertIsInstance(bookmark_list[0], Bookmark) From f31601bc4b69d591e9369fff5a455f4e8d3d8c47 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Thu, 8 Mar 2018 12:59:28 -0500 Subject: [PATCH 29/91] Added migration_type as a required field when creating a content migration --- canvasapi/account.py | 15 +++++++++++---- canvasapi/course.py | 15 +++++++++++---- canvasapi/group.py | 15 +++++++++++---- canvasapi/user.py | 15 +++++++++++---- tests/test_account.py | 21 ++++++++++++++++----- tests/test_course.py | 21 ++++++++++++++++----- tests/test_group.py | 21 ++++++++++++++++----- tests/test_user.py | 21 ++++++++++++++++----- 8 files changed, 108 insertions(+), 36 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 40b5bc23..76ae79de 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1172,19 +1172,26 @@ def list_rubrics(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) - def create_content_migration(self, **kwargs): + def create_content_migration(self, migration_type, **kwargs): """ Create a content migration. :calls: `POST /api/v1/accounts/:account_id/content_migrations \ `_ + :param migration_type: The migrator type to use in this migration + :type migration_type: str or :class:`canvasapi.content_migration.Migrator` + :rtype: :class:`canvasapi.content_migration.ContentMigration` """ - from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import ContentMigration, Migrator - if 'migration_type' not in kwargs: - raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + if isinstance(migration_type, Migrator): + kwargs['migration_type'] = migration_type.type + elif isinstance(migration_type, str): + kwargs['migration_type'] = migration_type + else: + raise TypeError('Parameter migration_type must be of type Migrator or str') response = self._requester.request( 'POST', diff --git a/canvasapi/course.py b/canvasapi/course.py index 4a708878..826cf436 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1829,19 +1829,26 @@ def get_single_grading_standard(self, grading_standard_id, **kwargs): ) return GradingStandard(self._requester, response.json()) - def create_content_migration(self, **kwargs): + def create_content_migration(self, migration_type, **kwargs): """ Create a content migration. :calls: `POST /api/v1/courses/:course_id/content_migrations \ `_ + :param migration_type: The migrator type to use in this migration + :type migration_type: str or :class:`canvasapi.content_migration.Migrator` + :rtype: :class:`canvasapi.content_migration.ContentMigration` """ - from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import ContentMigration, Migrator - if 'migration_type' not in kwargs: - raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + if isinstance(migration_type, Migrator): + kwargs['migration_type'] = migration_type.type + elif isinstance(migration_type, str): + kwargs['migration_type'] = migration_type + else: + raise TypeError('Parameter migration_type must be of type Migrator or str') response = self._requester.request( 'POST', diff --git a/canvasapi/group.py b/canvasapi/group.py index b2ca11a9..f4c7f1c1 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -667,19 +667,26 @@ def list_tabs(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) - def create_content_migration(self, **kwargs): + def create_content_migration(self, migration_type, **kwargs): """ Create a content migration. :calls: `POST /api/v1/groups/:group_id/content_migrations \ `_ + :param migration_type: The migrator type to use in this migration + :type migration_type: str or :class:`canvasapi.content_migration.Migrator` + :rtype: :class:`canvasapi.content_migration.ContentMigration` """ - from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import ContentMigration, Migrator - if 'migration_type' not in kwargs: - raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + if isinstance(migration_type, Migrator): + kwargs['migration_type'] = migration_type.type + elif isinstance(migration_type, str): + kwargs['migration_type'] = migration_type + else: + raise TypeError('Parameter migration_type must be of type Migrator or str') response = self._requester.request( 'POST', diff --git a/canvasapi/user.py b/canvasapi/user.py index 83291083..e4cc4cc3 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -545,19 +545,26 @@ def remove_observee(self, observee_id): ) return User(self._requester, response.json()) - def create_content_migration(self, **kwargs): + def create_content_migration(self, migration_type, **kwargs): """ Create a content migration. :calls: `POST /api/v1/users/:user_id/content_migrations \ `_ + :param migration_type: The migrator type to use in this migration + :type migration_type: str or :class:`canvasapi.content_migration.Migrator` + :rtype: :class:`canvasapi.content_migration.ContentMigration` """ - from canvasapi.content_migration import ContentMigration + from canvasapi.content_migration import ContentMigration, Migrator - if 'migration_type' not in kwargs: - raise RequiredFieldMissing("Parameter with key 'migration_type' is required.") + if isinstance(migration_type, Migrator): + kwargs['migration_type'] = migration_type.type + elif isinstance(migration_type, str): + kwargs['migration_type'] = migration_type + else: + raise TypeError('Parameter migration_type must be of type Migrator or str') response = self._requester.request( 'POST', diff --git a/tests/test_account.py b/tests/test_account.py index a8fdca05..94aeac8c 100644 --- a/tests/test_account.py +++ b/tests/test_account.py @@ -739,15 +739,26 @@ def test_list_rubrics(self, m): def test_create_content_migration(self, m): register_uris({'account': ['create_content_migration']}, m) - content_migration = self.account.create_content_migration(migration_type='dummy_importer') + content_migration = self.account.create_content_migration('dummy_importer') self.assertIsInstance(content_migration, ContentMigration) self.assertTrue(hasattr(content_migration, 'migration_type')) - # create_content_migration without type - def test_create_course_migration_missing_type(self, m): - with self.assertRaises(RequiredFieldMissing): - self.account.create_content_migration() + def test_create_content_migration_migrator(self, m): + register_uris({'account': ['create_content_migration', + 'get_migration_systems_multiple']}, m) + + migrators = self.account.get_migration_systems() + content_migration = self.account.create_content_migration(migrators[0]) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + def test_create_content_migration_bad_migration_type(self, m): + register_uris({'account': ['create_content_migration']}, m) + + with self.assertRaises(TypeError): + self.account.create_content_migration(1) # get_content_migration def test_get_content_migration(self, m): diff --git a/tests/test_course.py b/tests/test_course.py index a11705b5..2509579c 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -1188,15 +1188,26 @@ def test_get_single_grading_standard(self, m): def test_create_content_migration(self, m): register_uris({'course': ['create_content_migration']}, m) - content_migration = self.course.create_content_migration(migration_type='dummy_importer') + content_migration = self.course.create_content_migration('dummy_importer') self.assertIsInstance(content_migration, ContentMigration) self.assertTrue(hasattr(content_migration, 'migration_type')) - # create_content_migration without type - def test_create_course_migration_missing_type(self, m): - with self.assertRaises(RequiredFieldMissing): - self.course.create_content_migration() + def test_create_content_migration_migrator(self, m): + register_uris({'course': ['create_content_migration', + 'get_migration_systems_multiple']}, m) + + migrators = self.course.get_migration_systems() + content_migration = self.course.create_content_migration(migrators[0]) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + def test_create_content_migration_bad_migration_type(self, m): + register_uris({'course': ['create_content_migration']}, m) + + with self.assertRaises(TypeError): + self.course.create_content_migration(1) # get_content_migration def test_get_content_migration(self, m): diff --git a/tests/test_group.py b/tests/test_group.py index 130d4448..fc4e6d1a 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -447,15 +447,26 @@ def test_list_tabs(self, m): def test_create_content_migration(self, m): register_uris({'group': ['create_content_migration']}, m) - content_migration = self.group.create_content_migration(migration_type='dummy_importer') + content_migration = self.group.create_content_migration('dummy_importer') self.assertIsInstance(content_migration, ContentMigration) self.assertTrue(hasattr(content_migration, 'migration_type')) - # create_content_migration without type - def test_create_course_migration_missing_type(self, m): - with self.assertRaises(RequiredFieldMissing): - self.group.create_content_migration() + def test_create_content_migration_migrator(self, m): + register_uris({'group': ['create_content_migration', + 'get_migration_systems_multiple']}, m) + + migrators = self.group.get_migration_systems() + content_migration = self.group.create_content_migration(migrators[0]) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + def test_create_content_migration_bad_migration_type(self, m): + register_uris({'group': ['create_content_migration']}, m) + + with self.assertRaises(TypeError): + self.group.create_content_migration(1) # get_content_migration def test_get_content_migration(self, m): diff --git a/tests/test_user.py b/tests/test_user.py index 8dba8d2b..97674d10 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -342,15 +342,26 @@ def test_remove_observee(self, m): def test_create_content_migration(self, m): register_uris({'user': ['create_content_migration']}, m) - content_migration = self.user.create_content_migration(migration_type='dummy_importer') + content_migration = self.user.create_content_migration('dummy_importer') self.assertIsInstance(content_migration, ContentMigration) self.assertTrue(hasattr(content_migration, 'migration_type')) - # create_content_migration without type - def test_create_course_migration_missing_type(self, m): - with self.assertRaises(RequiredFieldMissing): - self.user.create_content_migration() + def test_create_content_migration_migrator(self, m): + register_uris({'user': ['create_content_migration', + 'get_migration_systems_multiple']}, m) + + migrators = self.user.get_migration_systems() + content_migration = self.user.create_content_migration(migrators[0]) + + self.assertIsInstance(content_migration, ContentMigration) + self.assertTrue(hasattr(content_migration, 'migration_type')) + + def test_create_content_migration_bad_migration_type(self, m): + register_uris({'user': ['create_content_migration']}, m) + + with self.assertRaises(TypeError): + self.user.create_content_migration(1) # get_content_migration def test_get_content_migration(self, m): From 4f981b1cb16b9f908d25f487f04afa28b8f305ae Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 11:59:13 -0500 Subject: [PATCH 30/91] Updated docstrings to reflect variable names --- canvasapi/account.py | 4 ++-- canvasapi/course.py | 4 ++-- canvasapi/group.py | 4 ++-- canvasapi/user.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 76ae79de..40affb78 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1211,8 +1211,8 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/accounts/:account_id/content_migrations/:id \ `_ - :param migration: The object or ID of the course to retrieve. - :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + :param content_migration: The object or ID of the course to retrieve. + :type contnet_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` """ diff --git a/canvasapi/course.py b/canvasapi/course.py index 826cf436..95ef7514 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1868,8 +1868,8 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/courses/:course_id/content_migrations/:id \ `_ - :param migration: The object or ID of the course to retrieve. - :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + :param content_migration: The object or ID of the course to retrieve. + :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` """ diff --git a/canvasapi/group.py b/canvasapi/group.py index f4c7f1c1..8404a91f 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -706,8 +706,8 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/groups/:group_id/content_migrations/:id \ `_ - :param migration: The object or ID of the course to retrieve. - :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + :param content_migration: The object or ID of the course to retrieve. + :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` """ diff --git a/canvasapi/user.py b/canvasapi/user.py index e4cc4cc3..a1ff135c 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -584,8 +584,8 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/users/:user_id/content_migrations/:id \ `_ - :param migration: The object or ID of the course to retrieve. - :type migration: int, str or :class:`canvasapi.content_migration.ContentMigration` + :param content_migration: The object or ID of the course to retrieve. + :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` """ From 6fd792899ae4904c8d9973c509705b4bf9fc7827 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 12:14:34 -0500 Subject: [PATCH 31/91] Added genralized progress call and updated docstrings --- canvasapi/account.py | 2 +- canvasapi/canvas.py | 24 ++++++++++++++++++++++++ canvasapi/content_migration.py | 5 ++++- canvasapi/course.py | 2 +- canvasapi/group.py | 2 +- canvasapi/user.py | 2 +- tests/fixtures/outcome.json | 17 +++++++++++++++++ tests/test_canvas.py | 9 +++++++++ 8 files changed, 58 insertions(+), 5 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 40affb78..9b5c579e 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1211,7 +1211,7 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/accounts/:account_id/content_migrations/:id \ `_ - :param content_migration: The object or ID of the course to retrieve. + :param content_migration: The object or ID of the content migration to retrieve. :type contnet_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 2f98dcf1..f43c0073 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -1017,3 +1017,27 @@ def get_outcome_group(self, group): ) return OutcomeGroup(self.__requester, response.json()) + + def get_progress(self, progress, **kwargs): + """ + Get a specific progress. + + :calls: `GET /api/v1/progress/:id + `_ + + :param progress: The object or ID of the progress to retrieve. + :type progress: int, str or :class:`canvasapi.progress.Progress` + + :rtype: :class:`canvasapi.progress.Progress` + """ + + from canvasapi.progress import Progress + + progress_id = obj_or_id(progress, "progress", (Progress,)) + + response = self.__requester.request( + 'GET', + 'progress/{}'.format(progress_id), + _kwargs=combine_kwargs(**kwargs) + ) + return Progress(self.__requester, response.json()) diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 4cf6bdf0..9323ab7f 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -15,7 +15,7 @@ def __str__(self): @property def _parent_id(self): """ - Return the id of the course or group that spawned + Return the id of the account, course, group, or user that spawned this content migration. :rtype: int @@ -71,6 +71,9 @@ def get_migration_issue(self, migration_issue, **kwargs): /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues `_ + :param migration_issue: The object or ID of the issue to retrieve. + :type migration_issue: int, str or :class:`canvasapi.content_migration.ContentMigration` + :rtype: :class:`canvasapi.content_migration.MigrationIssue` """ from canvasapi.content_migration import MigrationIssue diff --git a/canvasapi/course.py b/canvasapi/course.py index 95ef7514..178e473f 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1868,7 +1868,7 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/courses/:course_id/content_migrations/:id \ `_ - :param content_migration: The object or ID of the course to retrieve. + :param content_migration: The object or ID of the content migration to retrieve. :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` diff --git a/canvasapi/group.py b/canvasapi/group.py index 8404a91f..6feaba8a 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -706,7 +706,7 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/groups/:group_id/content_migrations/:id \ `_ - :param content_migration: The object or ID of the course to retrieve. + :param content_migration: The object or ID of the content migration to retrieve. :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` diff --git a/canvasapi/user.py b/canvasapi/user.py index a1ff135c..adc6fbea 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -584,7 +584,7 @@ def get_content_migration(self, content_migration, **kwargs): :calls: `GET /api/v1/users/:user_id/content_migrations/:id \ `_ - :param content_migration: The object or ID of the course to retrieve. + :param content_migration: The object or ID of the content migration to retrieve. :type content_migration: int, str or :class:`canvasapi.content_migration.ContentMigration` :rtype: :class:`canvasapi.content_migration.ContentMigration` diff --git a/tests/fixtures/outcome.json b/tests/fixtures/outcome.json index 2da69c95..139d6848 100644 --- a/tests/fixtures/outcome.json +++ b/tests/fixtures/outcome.json @@ -1267,5 +1267,22 @@ "context_type": "Course", "description": null } + }, + "get_progress": { + "method": "GET", + "endpoint": "progress/1", + "data": { + "id": 1, + "context_id": 1, + "context_type": "Content Migration", + "user_id": 123, + "tag": "assign_unassigned_members", + "completion": 100, + "workflow_state": "running", + "created_at": "2013-01-15T15:00:00Z", + "updated_at": "2013-01-15T15:04:00Z", + "message": "Test 123" + }, + "status_code": 200 } } diff --git a/tests/test_canvas.py b/tests/test_canvas.py index c3c59c1d..ab4e49d7 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -649,3 +649,12 @@ def test_get_outcome_group(self, m): self.assertIsInstance(outcome_group_by_obj, OutcomeGroup) self.assertEqual(outcome_group_by_obj.id, 1) self.assertEqual(outcome_group_by_obj.title, "Canvas outcome group title") + + # get_progress() + def test_get_progress(self, m): + register_uris({'content_migration': ['get_progress']}, m) + + progress = self.canvas.get_progress(1) + self.assertIsInstance(progress, Progress) + self.assertTrue(hasattr(progress, 'id')) + self.assertEqual(progress.id, 1) From b69419422e14b8f844165fc4130c406551b0dffe Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 12:19:16 -0500 Subject: [PATCH 32/91] Removed extranous import --- canvasapi/user.py | 1 - tests/test_user.py | 1 - 2 files changed, 2 deletions(-) diff --git a/canvasapi/user.py b/canvasapi/user.py index adc6fbea..408efe4c 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -5,7 +5,6 @@ from canvasapi.calendar_event import CalendarEvent from canvasapi.canvas_object import CanvasObject from canvasapi.communication_channel import CommunicationChannel -from canvasapi.exceptions import RequiredFieldMissing from canvasapi.folder import Folder from canvasapi.paginated_list import PaginatedList from canvasapi.upload import Uploader diff --git a/tests/test_user.py b/tests/test_user.py index 97674d10..58c8ddd4 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -16,7 +16,6 @@ from canvasapi.page_view import PageView from canvasapi.user import User from canvasapi.login import Login -from canvasapi.exceptions import RequiredFieldMissing from canvasapi.content_migration import ContentMigration, Migrator from tests import settings from tests.util import cleanup_file, register_uris From 26dec274eaeaea527f8f5489eb8bf4fb2b7fd359 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 12:31:01 -0500 Subject: [PATCH 33/91] Added string compatability for python 2.7 --- canvasapi/account.py | 4 ++-- canvasapi/course.py | 4 ++-- canvasapi/group.py | 4 ++-- canvasapi/user.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 9b5c579e..4f85ee77 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from six import python_2_unicode_compatible +from six import python_2_unicode_compatible, string_types from canvasapi.canvas_object import CanvasObject from canvasapi.grading_standard import GradingStandard @@ -1188,7 +1188,7 @@ def create_content_migration(self, migration_type, **kwargs): if isinstance(migration_type, Migrator): kwargs['migration_type'] = migration_type.type - elif isinstance(migration_type, str): + elif isinstance(migration_type, string_types): kwargs['migration_type'] = migration_type else: raise TypeError('Parameter migration_type must be of type Migrator or str') diff --git a/canvasapi/course.py b/canvasapi/course.py index 178e473f..44a72e2f 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -2,7 +2,7 @@ import warnings -from six import python_2_unicode_compatible, text_type +from six import python_2_unicode_compatible, text_type, string_types from canvasapi.canvas_object import CanvasObject from canvasapi.discussion_topic import DiscussionTopic @@ -1845,7 +1845,7 @@ def create_content_migration(self, migration_type, **kwargs): if isinstance(migration_type, Migrator): kwargs['migration_type'] = migration_type.type - elif isinstance(migration_type, str): + elif isinstance(migration_type, string_types): kwargs['migration_type'] = migration_type else: raise TypeError('Parameter migration_type must be of type Migrator or str') diff --git a/canvasapi/group.py b/canvasapi/group.py index 6feaba8a..c39c8bae 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from six import python_2_unicode_compatible, text_type +from six import python_2_unicode_compatible, text_type, string_types from canvasapi.canvas_object import CanvasObject from canvasapi.discussion_topic import DiscussionTopic @@ -683,7 +683,7 @@ def create_content_migration(self, migration_type, **kwargs): if isinstance(migration_type, Migrator): kwargs['migration_type'] = migration_type.type - elif isinstance(migration_type, str): + elif isinstance(migration_type, string_types): kwargs['migration_type'] = migration_type else: raise TypeError('Parameter migration_type must be of type Migrator or str') diff --git a/canvasapi/user.py b/canvasapi/user.py index 408efe4c..3734868c 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, division, print_function, unicode_literals -from six import python_2_unicode_compatible +from six import python_2_unicode_compatible, string_types from canvasapi.calendar_event import CalendarEvent from canvasapi.canvas_object import CanvasObject @@ -560,7 +560,7 @@ def create_content_migration(self, migration_type, **kwargs): if isinstance(migration_type, Migrator): kwargs['migration_type'] = migration_type.type - elif isinstance(migration_type, str): + elif isinstance(migration_type, string_types): kwargs['migration_type'] = migration_type else: raise TypeError('Parameter migration_type must be of type Migrator or str') From 1885c6f04d1d631b739dd44c8588f0b285339ce6 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 13:02:50 -0500 Subject: [PATCH 34/91] Deprecated list_ methods in discussion_topic --- canvasapi/discussion_topic.py | 83 ++++++++++++++++++++++++++++++++++ docs/class-reference.rst | 1 + docs/discussion-entry-ref.rst | 6 +++ tests/test_discussion_topic.py | 80 +++++++++++++++++++++++++++++--- 4 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 docs/discussion-entry-ref.rst diff --git a/canvasapi/discussion_topic.py b/canvasapi/discussion_topic.py index d8fa6fa5..06e45cdb 100644 --- a/canvasapi/discussion_topic.py +++ b/canvasapi/discussion_topic.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import warnings + from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject @@ -137,6 +139,31 @@ def list_topic_entries(self, **kwargs): """ Retreive the top-level entries in a discussion topic. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.dicussion_topic.DiscussionTopic.get_topic_entries` instead. + + :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entries \ + `_ + + or `GET /api/v1/groups/:group_id/discussion_topics/:topic_id/entries \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.discussion_topic.DiscussionEntry` + """ + warnings.warn( + "`list_topic_entries` is being deprecated and will be removed in a future version." + " Use `get_topic_entries` instead", + DeprecationWarning + ) + + return self.get_topic_entries(**kwargs) + + def get_topic_entries(self, **kwargs): + """ + Retreive the top-level entries in a discussion topic. + :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entries \ `_ @@ -167,6 +194,35 @@ def list_entries(self, ids, **kwargs): Retrieve a paginated list of discussion entries, given a list of ids. Entries will be returned in id order, smallest id first. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi. dicussion_topic.DiscussionTopic.get_entries` instead. + + :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entry_list \ + `_ + + or `GET /api/v1/groups/:group_id/discussion_topics/:topic_id/entry_list \ + `_ + + :param ids: A list of entry objects or IDs to retrieve. + :type ids: :class:`canvasapi.discussion_topic.DiscussionEntry`, or list or tuple of int + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.discussion_topic.DiscussionEntry` + """ + warnings.warn( + "`list_entries` is being deprecated and will be removed in a future version." + " Use `get_entries` instead", + DeprecationWarning + ) + + return self.get_entries(ids, **kwargs) + + def get_entries(self, ids, **kwargs): + """ + Retrieve a paginated list of discussion entries, given a list + of ids. Entries will be returned in id order, smallest id first. + :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entry_list \ `_ @@ -473,6 +529,33 @@ def list_replies(self, **kwargs): """ Retrieves the replies to a top-level entry in a discussion topic. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi. dicussion_topic.DiscussionEntry.get_replies` instead. + + :calls: `GET + /api/v1/courses/:course_id/discussion_topics/:topic_id/entries/:entry_id/replies \ + `_ + + or `GET + /api/v1/groups/:group_id/discussion_topics/:topic_id/entries/:entry_id/replies \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.discussion_topic.DiscussionEntry` + """ + warnings.warn( + "`list_replies` is being deprecated and will be removed in a future version." + " Use `get_replies` instead", + DeprecationWarning + ) + + return self.get_replies(**kwargs) + + def get_replies(self, **kwargs): + """ + Retrieves the replies to a top-level entry in a discussion topic. + :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entries/:entry_id/replies \ `_ diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 8b4dd56e..614705fb 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -15,6 +15,7 @@ Class Reference conversation-ref course-ref current-user-ref + discussion-entry-ref discussion-topic-ref enrollment-term-ref external-tool-ref diff --git a/docs/discussion-entry-ref.rst b/docs/discussion-entry-ref.rst new file mode 100644 index 00000000..9ddad097 --- /dev/null +++ b/docs/discussion-entry-ref.rst @@ -0,0 +1,6 @@ +=============== +DiscussionEntry +=============== + +.. autoclass:: canvasapi.discussion_topic.DiscussionEntry + :members: diff --git a/tests/test_discussion_topic.py b/tests/test_discussion_topic.py index 9d4d0178..317e0460 100644 --- a/tests/test_discussion_topic.py +++ b/tests/test_discussion_topic.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.course import Course @@ -62,7 +63,25 @@ def test_post_entry(self, m): def test_list_topic_entries(self, m): register_uris({'discussion_topic': ['list_topic_entries']}, m) - entries = self.discussion_topic.list_topic_entries() + with warnings.catch_warnings(record=True) as warning_list: + entries = self.discussion_topic.list_topic_entries() + entry_list = [entry for entry in entries] + self.assertEqual(len(entry_list), 2) + + self.assertIsInstance(entry_list[0], DiscussionEntry) + self.assertTrue(hasattr(entry_list[0], 'id')) + self.assertEqual(entry_list[0].id, 1) + self.assertTrue(hasattr(entry_list[0], 'user_id')) + self.assertEqual(entry_list[0].user_id, 1) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_topic_entries() + def test_get_topic_entries(self, m): + register_uris({'discussion_topic': ['list_topic_entries']}, m) + + entries = self.discussion_topic.get_topic_entries() entry_list = [entry for entry in entries] self.assertEqual(len(entry_list), 2) @@ -76,7 +95,37 @@ def test_list_topic_entries(self, m): def test_list_entries(self, m): register_uris({'discussion_topic': ['list_entries']}, m) - entries_by_id = self.discussion_topic.list_entries([1, 2, 3]) + with warnings.catch_warnings(record=True) as warning_list: + entries_by_id = self.discussion_topic.list_entries([1, 2, 3]) + entry_list_by_id = [entry for entry in entries_by_id] + self.assertTrue(len(entry_list_by_id), 3) + + entry_by_id = entry_list_by_id[-1] + self.assertIsInstance(entry_by_id, DiscussionEntry) + self.assertTrue(hasattr(entry_by_id, 'id')) + self.assertEqual(entry_by_id.id, 3) + self.assertTrue(hasattr(entry_by_id, 'message')) + self.assertEqual(entry_by_id.message, 'Lower level entry') + + entries_by_obj = self.discussion_topic.list_entries(entries_by_id) + entry_list_by_obj = [entry for entry in entries_by_obj] + self.assertTrue(len(entry_list_by_obj), 3) + + entry_by_obj = entry_list_by_obj[-1] + self.assertIsInstance(entry_by_obj, DiscussionEntry) + self.assertTrue(hasattr(entry_by_obj, 'id')) + self.assertEqual(entry_by_obj.id, 3) + self.assertTrue(hasattr(entry_by_obj, 'message')) + self.assertEqual(entry_by_obj.message, 'Lower level entry') + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_entries() + def test_get_entries(self, m): + register_uris({'discussion_topic': ['list_entries']}, m) + + entries_by_id = self.discussion_topic.get_entries([1, 2, 3]) entry_list_by_id = [entry for entry in entries_by_id] self.assertTrue(len(entry_list_by_id), 3) @@ -87,7 +136,7 @@ def test_list_entries(self, m): self.assertTrue(hasattr(entry_by_id, 'message')) self.assertEqual(entry_by_id.message, 'Lower level entry') - entries_by_obj = self.discussion_topic.list_entries(entries_by_id) + entries_by_obj = self.discussion_topic.get_entries(entries_by_id) entry_list_by_obj = [entry for entry in entries_by_obj] self.assertTrue(len(entry_list_by_obj), 3) @@ -236,8 +285,8 @@ def setUp(self): self.group = self.canvas.get_group(1) self.discussion_topic = self.course.get_discussion_topic(1) self.discussion_topic_group = self.group.get_discussion_topic(1) - self.discussion_entry = self.discussion_topic.list_entries([1])[0] - self.discussion_entry_group = self.discussion_topic_group.list_entries([1])[0] + self.discussion_entry = self.discussion_topic.get_entries([1])[0] + self.discussion_entry_group = self.discussion_topic_group.get_entries([1])[0] # __str__() def test__str__(self, m): @@ -301,7 +350,26 @@ def test_post_reply(self, m): def test_list_replies(self, m): register_uris({'discussion_topic': ['list_entry_replies']}, m) - replies = self.discussion_entry.list_replies() + with warnings.catch_warnings(record=True) as warning_list: + replies = self.discussion_entry.list_replies() + reply_list = [reply for reply in replies] + self.assertTrue(len(reply_list), 5) + + reply = reply_list[0] + self.assertIsInstance(reply, DiscussionEntry) + self.assertTrue(hasattr(reply, 'id')) + self.assertEqual(reply.id, 5) + self.assertTrue(hasattr(reply, 'message')) + self.assertEqual(reply.message, 'Reply message 1') + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_replies() + def test_get_replies(self, m): + register_uris({'discussion_topic': ['list_entry_replies']}, m) + + replies = self.discussion_entry.get_replies() reply_list = [reply for reply in replies] self.assertTrue(len(reply_list), 5) From 4d0c6131cbcbb1e8b80832963d2973e74fc4b55f Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 13:04:27 -0500 Subject: [PATCH 35/91] Fixed spelling error in docstring --- canvasapi/discussion_topic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/canvasapi/discussion_topic.py b/canvasapi/discussion_topic.py index 06e45cdb..5ca1db14 100644 --- a/canvasapi/discussion_topic.py +++ b/canvasapi/discussion_topic.py @@ -141,7 +141,7 @@ def list_topic_entries(self, **kwargs): .. warning:: .. deprecated:: 0.10.0 - Use :func:`canvasapi.dicussion_topic.DiscussionTopic.get_topic_entries` instead. + Use :func:`canvasapi.discussion_topic.DiscussionTopic.get_topic_entries` instead. :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entries \ `_ @@ -196,7 +196,7 @@ def list_entries(self, ids, **kwargs): .. warning:: .. deprecated:: 0.10.0 - Use :func:`canvasapi. dicussion_topic.DiscussionTopic.get_entries` instead. + Use :func:`canvasapi. discussion_topic.DiscussionTopic.get_entries` instead. :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entry_list \ `_ @@ -531,7 +531,7 @@ def list_replies(self, **kwargs): .. warning:: .. deprecated:: 0.10.0 - Use :func:`canvasapi. dicussion_topic.DiscussionEntry.get_replies` instead. + Use :func:`canvasapi. discussion_topic.DiscussionEntry.get_replies` instead. :calls: `GET /api/v1/courses/:course_id/discussion_topics/:topic_id/entries/:entry_id/replies \ From 87bfd07b87af1750c94cd519968eb8b6be7b070d Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 13:16:16 -0500 Subject: [PATCH 36/91] Deprecated list_ methods in folder --- canvasapi/folder.py | 48 +++++++++++++++++++++++++++++++++++++++- docs/class-reference.rst | 1 + docs/folder-ref.rst | 6 +++++ tests/test_folder.py | 33 ++++++++++++++++++++++++--- 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 docs/folder-ref.rst diff --git a/canvasapi/folder.py b/canvasapi/folder.py index f3353cba..f38cab2e 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -1,5 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import warnings + from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject @@ -17,6 +19,28 @@ def list_files(self, **kwargs): """ Returns the paginated list of files for the folder. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.folder.Folder.get_files` instead. + + :calls: `GET api/v1/folders/:id/files \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.file.File` + """ + warnings.warn( + "`list_files` is being deprecated and will be removed in a future version." + " Use `get_files` instead", + DeprecationWarning + ) + + return self.get_files(**kwargs) + + def get_files(self, **kwargs): + """ + Returns the paginated list of files for the folder. + :calls: `GET api/v1/folders/:id/files \ `_ @@ -50,7 +74,29 @@ def delete(self, **kwargs): ) return Folder(self._requester, response.json()) - def list_folders(self): + def list_folders(self, **kwargs): + """ + Returns the paginated list of folders in the folder. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.folder.Folder.get_folders` instead. + + :calls: `GET /api/v1/folders/:id/folders \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.folder.Folder` + """ + warnings.warn( + "`list_folders` is being deprecated and will be removed in a future version." + " Use `get_folders` instead", + DeprecationWarning + ) + + return self.get_folders(**kwargs) + + def get_folders(self, **kwargs): """ Returns the paginated list of folders in the folder. diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 614705fb..870954a2 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -19,6 +19,7 @@ Class Reference discussion-topic-ref enrollment-term-ref external-tool-ref + folder-ref group-ref login-ref module-ref diff --git a/docs/folder-ref.rst b/docs/folder-ref.rst new file mode 100644 index 00000000..b8f513b4 --- /dev/null +++ b/docs/folder-ref.rst @@ -0,0 +1,6 @@ +============= +Folder +============= + +.. autoclass:: canvasapi.folder.Folder + :members: diff --git a/tests/test_folder.py b/tests/test_folder.py index eb29e942..3ba9793b 100644 --- a/tests/test_folder.py +++ b/tests/test_folder.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.file import File @@ -27,10 +28,23 @@ def test__str__(self, m): self.assertIsInstance(string, str) # list_files() - def test_folder_files(self, m): + def test_list_files(self, m): register_uris({'folder': ['list_folder_files', 'list_folder_files2']}, m) - files = self.folder.list_files() + with warnings.catch_warnings(record=True) as warning_list: + files = self.folder.list_files() + file_list = [file for file in files] + self.assertEqual(len(file_list), 4) + self.assertIsInstance(file_list[0], File) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_files() + def test_get_files(self, m): + register_uris({'folder': ['list_folder_files', 'list_folder_files2']}, m) + + files = self.folder.get_files() file_list = [file for file in files] self.assertEqual(len(file_list), 4) self.assertIsInstance(file_list[0], File) @@ -49,7 +63,20 @@ def test_delete_file(self, m): def test_list_folders(self, m): register_uris({'folder': ['list_folders']}, m) - folders = self.folder.list_folders() + with warnings.catch_warnings(record=True) as warning_list: + folders = self.folder.list_folders() + folder_list = [folder for folder in folders] + self.assertEqual(len(folder_list), 2) + self.assertIsInstance(folder_list[0], Folder) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_folders() + def test_get_folders(self, m): + register_uris({'folder': ['list_folders']}, m) + + folders = self.folder.get_folders() folder_list = [folder for folder in folders] self.assertEqual(len(folder_list), 2) self.assertIsInstance(folder_list[0], Folder) From 3bcdec1eee968927d8cc693d0febaa18942fb7f8 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 13:45:44 -0500 Subject: [PATCH 37/91] Deprecated list_ methods in group --- canvasapi/group.py | 186 +++++++++++++++++++++++++++++++++++++++++++- tests/test_group.py | 140 +++++++++++++++++++++++++++++---- 2 files changed, 309 insertions(+), 17 deletions(-) diff --git a/canvasapi/group.py b/canvasapi/group.py index 4c6c80c4..3b4206ed 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -2,6 +2,8 @@ from six import python_2_unicode_compatible, text_type +import warnings + from canvasapi.canvas_object import CanvasObject from canvasapi.discussion_topic import DiscussionTopic from canvasapi.folder import Folder @@ -187,6 +189,28 @@ def list_users(self, **kwargs): """ List users in a group. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_users` instead. + + :calls: `GET /api/v1/groups/:group_id/users \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.user.User` + """ + warnings.warn( + "`list_users` is being deprecated and will be removed in a future version." + " Use `get_users` instead", + DeprecationWarning + ) + + return self.get_users(**kwargs) + + def get_users(self, **kwargs): + """ + List users in a group. + :calls: `GET /api/v1/groups/:group_id/users \ `_ @@ -287,6 +311,28 @@ def list_memberships(self, **kwargs): """ List users in a group. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_memberships` instead. + + :calls: `GET /api/v1/groups/:group_id/memberships \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.group.GroupMembership` + """ + warnings.warn( + "`list_memberships` is being deprecated and will be removed in a future version." + " Use `get_memberships` instead", + DeprecationWarning + ) + + return self.get_memberships(**kwargs) + + def get_memberships(self, **kwargs): + """ + List users in a group. + :calls: `GET /api/v1/groups/:group_id/memberships \ `_ @@ -511,7 +557,29 @@ def reorder_pinned_topics(self, order): return response.json().get('reorder') - def list_external_feeds(self): + def list_external_feeds(self, **kwargs): + """ + Returns the list of External Feeds this group. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_external_feeds` instead. + + :calls: `GET /api/v1/groups/:group_id/external_feeds \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.external_feed.ExternalFeed` + """ + warnings.warn( + "`list_external_feeds` is being deprecated and will be removed in a future version." + " Use `get_external_feeds` instead", + DeprecationWarning + ) + + return self.get_external_feeds(**kwargs) + + def get_external_feeds(self, **kwargs): """ Returns the list of External Feeds this group. @@ -575,6 +643,28 @@ def list_files(self, **kwargs): """ Returns the paginated list of files for the group. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_files` instead. + + :calls: `GET api/v1/groups/:group_id/files \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.file.File` + """ + warnings.warn( + "`list_files` is being deprecated and will be removed in a future version." + " Use `get_files` instead", + DeprecationWarning + ) + + return self.get_files(**kwargs) + + def get_files(self, **kwargs): + """ + Returns the paginated list of files for the group. + :calls: `GET api/v1/groups/:group_id/files \ `_ @@ -611,7 +701,30 @@ def get_folder(self, folder): ) return Folder(self._requester, response.json()) - def list_folders(self): + def list_folders(self, **kwargs): + """ + Returns the paginated list of all folders for the given group. This will be returned as a + flat list containing all subfolders as well. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_folders` instead. + + :calls: `GET /api/v1/groups/:group_id/folders \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.folder.Folder` + """ + warnings.warn( + "`list_folders` is being deprecated and will be removed in a future version." + " Use `get_folders` instead", + DeprecationWarning + ) + + return self.get_folders(**kwargs) + + def get_folders(self, **kwargs): """ Returns the paginated list of all folders for the given group. This will be returned as a flat list containing all subfolders as well. @@ -653,6 +766,29 @@ def list_tabs(self, **kwargs): List available tabs for a group. Returns a list of navigation tabs available in the current context. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.Group.get_tabs` instead. + + :calls: `GET /api/v1/groups/:group_id/tabs \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.tab.Tab` + """ + warnings.warn( + "`list_tabs` is being deprecated and will be removed in a future version." + " Use `get_tabs` instead", + DeprecationWarning + ) + + return self.get_tabs(**kwargs) + + def get_tabs(self, **kwargs): + """ + List available tabs for a group. + Returns a list of navigation tabs available in the current context. + :calls: `GET /api/v1/groups/:group_id/tabs \ `_ @@ -784,7 +920,29 @@ def delete(self): ) return response.json() - def list_groups(self): + def list_groups(self, **kwargs): + """ + List groups in group category. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.GroupCategory.get_groups` instead. + + :calls: `GET /api/v1/group_categories/:group_category_id/groups \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.group.Group` + """ + warnings.warn( + "`list_groups` is being deprecated and will be removed in a future version." + " Use `get_groups` instead", + DeprecationWarning + ) + + return self.get_groups(**kwargs) + + def get_groups(self, **kwargs): """ List groups in group category. @@ -805,6 +963,28 @@ def list_users(self, **kwargs): """ List users in group category. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.group.GroupCategory.get_users` instead. + + :calls: `GET /api/v1/group_categories/:group_category_id/users \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.user.User` + """ + warnings.warn( + "`list_users` is being deprecated and will be removed in a future version." + " Use `get_users` instead", + DeprecationWarning + ) + + return self.get_users(**kwargs) + + def get_users(self, **kwargs): + """ + List users in group category. + :calls: `GET /api/v1/group_categories/:group_category_id/users \ `_ diff --git a/tests/test_group.py b/tests/test_group.py index e948c44a..0828f747 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import unittest import uuid +import warnings import requests import requests_mock @@ -54,7 +55,7 @@ def test_edit_front_page(self, m): self.assertTrue(hasattr(new_front_page, 'url')) self.assertTrue(hasattr(new_front_page, 'title')) - # list_pages() + # get_pages() def test_get_pages(self, m): register_uris({'group': ['get_pages', 'get_pages2']}, m) @@ -122,8 +123,22 @@ def test_invite(self, m): def test_list_users(self, m): register_uris({'group': ['list_users', 'list_users_p2']}, m) + with warnings.catch_warnings(record=True) as warning_list: + from canvasapi.user import User + users = self.group.list_users() + user_list = [user for user in users] + self.assertIsInstance(user_list[0], User) + self.assertEqual(len(user_list), 4) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_users() + def test_get_users(self, m): + register_uris({'group': ['list_users', 'list_users_p2']}, m) + from canvasapi.user import User - users = self.group.list_users() + users = self.group.get_users() user_list = [user for user in users] self.assertIsInstance(user_list[0], User) self.assertEqual(len(user_list), 4) @@ -143,7 +158,7 @@ def test_remove_user(self, m): user_by_id = self.group.remove_user(1) self.assertIsInstance(user_by_id, User) - users = self.group.list_users() + users = self.group.get_users() user_by_obj = self.group.remove_user(users[0]) self.assertIsInstance(user_by_obj, User) @@ -181,7 +196,21 @@ def test_get_activity_stream_summary(self, m): def test_list_memberships(self, m): register_uris({'group': ['list_memberships', 'list_memberships_p2']}, m) - response = self.group.list_memberships() + with warnings.catch_warnings(record=True) as warning_list: + response = self.group.list_memberships() + membership_list = [membership for membership in response] + self.assertEqual(len(membership_list), 4) + self.assertIsInstance(membership_list[0], GroupMembership) + self.assertTrue(hasattr(membership_list[0], 'id')) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_memberships() + def test_get_memberships(self, m): + register_uris({'group': ['list_memberships', 'list_memberships_p2']}, m) + + response = self.group.get_memberships() membership_list = [membership for membership in response] self.assertEqual(len(membership_list), 4) self.assertIsInstance(membership_list[0], GroupMembership) @@ -201,7 +230,7 @@ def test_get_membership(self, m): membership_by_id = self.group.get_membership(1, "users") self.assertIsInstance(membership_by_id, GroupMembership) - users = self.group.list_users() + users = self.group.get_users() membership_by_obj = self.group.get_membership(users[0], "users") self.assertIsInstance(membership_by_obj, GroupMembership) @@ -219,7 +248,7 @@ def test_create_membership(self, m): response = self.group.create_membership(1) self.assertIsInstance(response, GroupMembership) - users = self.group.list_users() + users = self.group.get_users() response = self.group.create_membership(users[0]) self.assertIsInstance(response, GroupMembership) @@ -237,7 +266,7 @@ def test_update_membership(self, m): updated_membership_by_id = self.group.update_membership(1) self.assertIsInstance(updated_membership_by_id, GroupMembership) - users = self.group.list_users() + users = self.group.get_users() updated_membership_by_obj = self.group.update_membership(users[0]) self.assertIsInstance(updated_membership_by_obj, GroupMembership) @@ -365,7 +394,21 @@ def test_reorder_pinned_topics_invalid_input(self, m): def test_list_external_feeds(self, m): register_uris({'group': ['list_external_feeds']}, m) - feeds = self.group.list_external_feeds() + with warnings.catch_warnings(record=True) as warning_list: + feeds = self.group.list_external_feeds() + feed_list = [feed for feed in feeds] + self.assertEqual(len(feed_list), 2) + self.assertTrue(hasattr(feed_list[0], 'url')) + self.assertIsInstance(feed_list[0], ExternalFeed) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_external_feeds() + def test_get_external_feeds(self, m): + register_uris({'group': ['list_external_feeds']}, m) + + feeds = self.group.get_external_feeds() feed_list = [feed for feed in feeds] self.assertEqual(len(feed_list), 2) self.assertTrue(hasattr(feed_list[0], 'url')) @@ -396,10 +439,23 @@ def test_delete_external_feed(self, m): self.assertEqual(deleted_ef_by_obj.display_name, "My Blog") # list_files() - def test_group_files(self, m): + def test_list_files(self, m): + register_uris({'group': ['list_group_files', 'list_group_files2']}, m) + + with warnings.catch_warnings(record=True) as warning_list: + files = self.group.list_files() + file_list = [file for file in files] + self.assertEqual(len(file_list), 4) + self.assertIsInstance(file_list[0], File) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_files() + def test_get_files(self, m): register_uris({'group': ['list_group_files', 'list_group_files2']}, m) - files = self.group.list_files() + files = self.group.get_files() file_list = [file for file in files] self.assertEqual(len(file_list), 4) self.assertIsInstance(file_list[0], File) @@ -420,7 +476,20 @@ def test_get_folder(self, m): def test_list_folders(self, m): register_uris({'group': ['list_folders']}, m) - folders = self.group.list_folders() + with warnings.catch_warnings(record=True) as warning_list: + folders = self.group.list_folders() + folder_list = [folder for folder in folders] + self.assertEqual(len(folder_list), 2) + self.assertIsInstance(folder_list[0], Folder) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_folders() + def test_get_folders(self, m): + register_uris({'group': ['list_folders']}, m) + + folders = self.group.get_folders() folder_list = [folder for folder in folders] self.assertEqual(len(folder_list), 2) self.assertIsInstance(folder_list[0], Folder) @@ -437,7 +506,20 @@ def test_create_folder(self, m): def test_list_tabs(self, m): register_uris({'group': ['list_tabs']}, m) - tabs = self.group.list_tabs() + with warnings.catch_warnings(record=True) as warning_list: + tabs = self.group.list_tabs() + tab_list = [tab for tab in tabs] + self.assertEqual(len(tab_list), 2) + self.assertIsInstance(tab_list[0], Tab) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_tabs() + def test_get_tabs(self, m): + register_uris({'group': ['list_tabs']}, m) + + tabs = self.group.get_tabs() tab_list = [tab for tab in tabs] self.assertEqual(len(tab_list), 2) self.assertIsInstance(tab_list[0], Tab) @@ -541,7 +623,21 @@ def test_delete_category(self, m): def test_list_groups(self, m): register_uris({'group': ['category_list_groups']}, m) - response = self.group_category.list_groups() + with warnings.catch_warnings(record=True) as warning_list: + response = self.group_category.list_groups() + group_list = [group for group in response] + self.assertEqual(len(group_list), 2) + self.assertIsInstance(group_list[0], Group) + self.assertTrue(hasattr(group_list[0], 'id')) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_groups() + def test_get_groups(self, m): + register_uris({'group': ['category_list_groups']}, m) + + response = self.group_category.get_groups() group_list = [group for group in response] self.assertEqual(len(group_list), 2) self.assertIsInstance(group_list[0], Group) @@ -553,7 +649,23 @@ def test_list_users(self, m): register_uris({'group': ['category_list_users']}, m) - response = self.group_category.list_users() + with warnings.catch_warnings(record=True) as warning_list: + response = self.group_category.list_users() + user_list = [user for user in response] + self.assertEqual(len(user_list), 4) + self.assertIsInstance(user_list[0], User) + self.assertTrue(hasattr(user_list[0], 'user_id')) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_users() + def test_get_users(self, m): + from canvasapi.user import User + + register_uris({'group': ['category_list_users']}, m) + + response = self.group_category.get_users() user_list = [user for user in response] self.assertEqual(len(user_list), 4) self.assertIsInstance(user_list[0], User) From cb683704fd33ba5b8a5547ab755002567ad608bb Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 13:54:06 -0500 Subject: [PATCH 38/91] Deprecated list_ methods in module --- canvasapi/module.py | 23 +++++++++++++++++++++++ tests/test_module.py | 19 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/canvasapi/module.py b/canvasapi/module.py index 9171045e..cf383cd2 100644 --- a/canvasapi/module.py +++ b/canvasapi/module.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from six import python_2_unicode_compatible +import warnings from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing @@ -77,6 +78,28 @@ def list_module_items(self, **kwargs): """ List all of the items in this module. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.module.Modules.get_module_items` instead. + + :calls: `GET /api/v1/courses/:course_id/modules/:module_id/items \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.module.ModuleItem` + """ + warnings.warn( + "`list_module_items` is being deprecated and will be removed in a future version." + " Use `get_module_items` instead", + DeprecationWarning + ) + + return self.get_module_items(**kwargs) + + 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 \ `_ diff --git a/tests/test_module.py b/tests/test_module.py index f3ae8d7b..df738e34 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.exceptions import RequiredFieldMissing @@ -59,7 +60,23 @@ def test_relock(self, m): def test_list_module_items(self, m): register_uris({'module': ['list_module_items', 'list_module_items2']}, m) - module_items = self.module.list_module_items() + with warnings.catch_warnings(record=True) as warning_list: + module_items = self.module.list_module_items() + module_item_list = [module_item for module_item in module_items] + + self.assertEqual(len(module_item_list), 4) + self.assertIsInstance(module_item_list[0], ModuleItem) + self.assertTrue(hasattr(module_item_list[0], 'course_id')) + self.assertEqual(module_item_list[0].course_id, self.course.id) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_module_items() + def test_get_module_items(self, m): + register_uris({'module': ['list_module_items', 'list_module_items2']}, m) + + module_items = self.module.get_module_items() module_item_list = [module_item for module_item in module_items] self.assertEqual(len(module_item_list), 4) From bbd5af79cad2d8864ff1bfd02d72f8190e3bc0d5 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 14:18:30 -0500 Subject: [PATCH 39/91] Deprecated list_ methods in outcome --- canvasapi/outcome.py | 57 +++++++++++++++++++++++++++++- tests/test_outcome.py | 82 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 7 deletions(-) diff --git a/canvasapi/outcome.py b/canvasapi/outcome.py index 4a1aebb4..9dc25937 100644 --- a/canvasapi/outcome.py +++ b/canvasapi/outcome.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from six import python_2_unicode_compatible +import warnings from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList @@ -159,6 +160,33 @@ def list_linked_outcomes(self, **kwargs): """ List linked outcomes. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.outcome.Outcome.get_linked_outcomes` instead. + + :calls: `GET /api/v1/global/outcome_groups/:id/outcomes \ + `_ + or `GET /api/v1/accounts/:account_id/outcome_groups/:id/outcomes \ + `_ + or `GET /api/v1/courses/:course_id/outcome_groups/:id/outcomes \ + `_ + + :returns: Paginated List of Outcomes linked to the group. + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.outcome.OutcomeLink` + """ + warnings.warn( + "`list_linked_outcomes` is being deprecated and will be removed in a future version." + " Use `get_linked_outcomes` instead", + DeprecationWarning + ) + + return self.get_linked_outcomes(**kwargs) + + def get_linked_outcomes(self, **kwargs): + """ + List linked outcomes. + :calls: `GET /api/v1/global/outcome_groups/:id/outcomes \ `_ or `GET /api/v1/accounts/:account_id/outcome_groups/:id/outcomes \ @@ -267,7 +295,34 @@ def unlink_outcome(self, outcome): return 'context_id' in response.json() - def list_subgroups(self): + def list_subgroups(self, **kwargs): + """ + List subgroups. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.outcome.Outcome.get_subgroups` instead. + + :calls: `GET /api/v1/global/outcome_groups/:id/subgroups \ + `_ + or `GET /api/v1/accounts/:account_id/outcome_groups/:id/subgroups \ + `_ + or `GET /api/v1/courses/:course_id/outcome_groups/:id/subgroups \ + `_ + + :returns: Paginated List of OutcomeGroups linked to the current group. + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.outcome.OutcomeGroup` + """ + warnings.warn( + "`list_subgroups` is being deprecated and will be removed in a future version." + " Use `get_subgroups` instead - ", + DeprecationWarning + ) + + return self.get_subgroups(**kwargs) + + def get_subgroups(self, **kwargs): """ List subgroups. diff --git a/tests/test_outcome.py b/tests/test_outcome.py index 625a7bea..d466d9fc 100644 --- a/tests/test_outcome.py +++ b/tests/test_outcome.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi import Canvas from canvasapi.outcome import Outcome, OutcomeGroup, OutcomeLink @@ -203,17 +204,47 @@ def test_list_linked_outcomes(self, m): ] }, m) - result = self.account_outcome_group.list_linked_outcomes() + with warnings.catch_warnings(record=True) as warning_list: + result = self.account_outcome_group.list_linked_outcomes() + self.assertIsInstance(result[0], OutcomeLink) + self.assertEqual(result[0].outcome_group['id'], 2) + self.assertEqual(result[0].outcome_group['title'], "Account Test Outcome Group") + + result = self.canvas_outcome_group.list_linked_outcomes() + self.assertIsInstance(result[0], OutcomeLink) + self.assertEqual(result[0].outcome_group['id'], 2) + self.assertEqual(result[0].outcome_group['title'], "Global Test Outcome Group") + + result = self.course_outcome_group.list_linked_outcomes() + self.assertIsInstance(result[0], OutcomeLink) + self.assertEqual(result[0].outcome_group['id'], 2) + self.assertEqual(result[0].outcome_group['title'], "Course Test Outcome Group") + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_linked_outcomes() + def test_get_linked_outcomes(self, m): + register_uris( + { + 'outcome': [ + 'outcome_group_list_linked_outcomes_account', + 'outcome_group_list_linked_outcomes_global', + 'outcome_group_list_linked_outcomes_courses' + ] + }, m) + + result = self.account_outcome_group.get_linked_outcomes() self.assertIsInstance(result[0], OutcomeLink) self.assertEqual(result[0].outcome_group['id'], 2) self.assertEqual(result[0].outcome_group['title'], "Account Test Outcome Group") - result = self.canvas_outcome_group.list_linked_outcomes() + result = self.canvas_outcome_group.get_linked_outcomes() self.assertIsInstance(result[0], OutcomeLink) self.assertEqual(result[0].outcome_group['id'], 2) self.assertEqual(result[0].outcome_group['title'], "Global Test Outcome Group") - result = self.course_outcome_group.list_linked_outcomes() + result = self.course_outcome_group.get_linked_outcomes() self.assertIsInstance(result[0], OutcomeLink) self.assertEqual(result[0].outcome_group['id'], 2) self.assertEqual(result[0].outcome_group['title'], "Course Test Outcome Group") @@ -326,7 +357,46 @@ def test_list_subgroups(self, m): ] }, m) - result = self.canvas_outcome_group.list_subgroups() + with warnings.catch_warnings(record=True) as warning_list: + result = self.canvas_outcome_group.list_subgroups() + self.assertIsInstance(result[0], OutcomeGroup) + self.assertEqual(result[0].id, 2) + self.assertEqual(result[0].title, "Global Listed Subgroup Title 1") + self.assertIsInstance(result[1], OutcomeGroup) + self.assertEqual(result[1].id, 3) + self.assertEqual(result[1].title, "Global Listed Subgroup Title 2") + + result = self.account_outcome_group.list_subgroups() + self.assertIsInstance(result[0], OutcomeGroup) + self.assertEqual(result[0].id, 2) + self.assertEqual(result[0].title, "Account Listed Subgroup Title 1") + self.assertIsInstance(result[1], OutcomeGroup) + self.assertEqual(result[1].id, 3) + self.assertEqual(result[1].title, "Account Listed Subgroup Title 2") + + result = self.course_outcome_group.list_subgroups() + self.assertIsInstance(result[0], OutcomeGroup) + self.assertEqual(result[0].id, 2) + self.assertEqual(result[0].title, "Course Listed Subgroup Title 1") + self.assertIsInstance(result[1], OutcomeGroup) + self.assertEqual(result[1].id, 3) + self.assertEqual(result[1].title, "Course Listed Subgroup Title 2") + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_subgroups() + def test_get_subgroups(self, m): + register_uris( + { + 'outcome': [ + 'outcome_group_list_subgroups_global', + 'outcome_group_list_subgroups_account', + 'outcome_group_list_subgroups_course' + ] + }, m) + + result = self.canvas_outcome_group.get_subgroups() self.assertIsInstance(result[0], OutcomeGroup) self.assertEqual(result[0].id, 2) self.assertEqual(result[0].title, "Global Listed Subgroup Title 1") @@ -334,7 +404,7 @@ def test_list_subgroups(self, m): self.assertEqual(result[1].id, 3) self.assertEqual(result[1].title, "Global Listed Subgroup Title 2") - result = self.account_outcome_group.list_subgroups() + result = self.account_outcome_group.get_subgroups() self.assertIsInstance(result[0], OutcomeGroup) self.assertEqual(result[0].id, 2) self.assertEqual(result[0].title, "Account Listed Subgroup Title 1") @@ -342,7 +412,7 @@ def test_list_subgroups(self, m): self.assertEqual(result[1].id, 3) self.assertEqual(result[1].title, "Account Listed Subgroup Title 2") - result = self.course_outcome_group.list_subgroups() + result = self.course_outcome_group.get_subgroups() self.assertIsInstance(result[0], OutcomeGroup) self.assertEqual(result[0].id, 2) self.assertEqual(result[0].title, "Course Listed Subgroup Title 1") From dd0387561f19024c89c0294590f8b3988ce440f5 Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 14:33:44 -0500 Subject: [PATCH 40/91] Deprecated list_ methods in page --- canvasapi/page.py | 23 +++++++++++++++++++++++ tests/test_page.py | 18 +++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/canvasapi/page.py b/canvasapi/page.py index 7b747965..1557c6ca 100644 --- a/canvasapi/page.py +++ b/canvasapi/page.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from six import python_2_unicode_compatible +import warnings from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList @@ -155,6 +156,28 @@ def list_revisions(self, **kwargs): """ List the revisions of a page. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.page.Page.get_revisions` instead. + + :calls: `GET /api/v1/courses/:course_id/pages/:url/revisions \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.pagerevision.PageRevision` + """ + warnings.warn( + "`list_revisions` is being deprecated and will be removed in a future version." + " Use `get_revisions` instead", + DeprecationWarning + ) + + return self.get_revisions(**kwargs) + + def get_revisions(self, **kwargs): + """ + List the revisions of a page. + :calls: `GET /api/v1/courses/:course_id/pages/:url/revisions \ `_ diff --git a/tests/test_page.py b/tests/test_page.py index 11435595..c60df479 100644 --- a/tests/test_page.py +++ b/tests/test_page.py @@ -2,6 +2,7 @@ import unittest import requests_mock +import warnings from canvasapi.canvas import Canvas from canvasapi.course import Course @@ -53,10 +54,25 @@ def test_delete(self, m): self.assertIsInstance(deleted_page, Page) + # list_revisions() def test_list_revisions(self, m): register_uris({'page': ['list_revisions', 'list_revisions2']}, m) - revisions = self.page_course.list_revisions() + with warnings.catch_warnings(record=True) as warning_list: + revisions = self.page_course.list_revisions() + rev_list = [rev for rev in revisions] + + self.assertEqual(len(rev_list), 4) + self.assertIsInstance(rev_list[0], PageRevision) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_revisions() + def test_get_revisions(self, m): + register_uris({'page': ['list_revisions', 'list_revisions2']}, m) + + revisions = self.page_course.get_revisions() rev_list = [rev for rev in revisions] self.assertEqual(len(rev_list), 4) From e57198cef9f76917dd65f7eb8275ad0d4809071a Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 14:35:45 -0500 Subject: [PATCH 41/91] Deprecated list_ methods in section --- canvasapi/section.py | 24 ++++++++++++++++++++++++ tests/test_section.py | 22 ++++++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/canvasapi/section.py b/canvasapi/section.py index 707bb60e..7a9a95fe 100644 --- a/canvasapi/section.py +++ b/canvasapi/section.py @@ -181,6 +181,30 @@ def list_multiple_submissions(self, **kwargs): List submissions for multiple assignments. Get all existing submissions for a given set of students and assignments. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.section.Section.get_multiple_submissions` instead. + + :calls: `GET /api/v1/sections/:section_id/students/submissions \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.submission.Submission` + """ + warnings.warn( + "`list_multiple_submissions`" + " is being deprecated and will be removed in a future version." + " Use `get_multiple_submissions` instead", + DeprecationWarning + ) + + return self.get_multiple_submissions(**kwargs) + + def get_multiple_submissions(self, **kwargs): + """ + List submissions for multiple assignments. + Get all existing submissions for a given set of students and assignments. + :calls: `GET /api/v1/sections/:section_id/students/submissions \ `_ diff --git a/tests/test_section.py b/tests/test_section.py index 07112264..92b500c2 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -30,7 +30,7 @@ def test__str__(self, m): string = str(self.section) self.assertIsInstance(string, str) - # list_enrollments() + # get_enrollments() def test_get_enrollments(self, m): register_uris({'section': ['list_enrollments', 'list_enrollments_2']}, m) @@ -153,18 +153,32 @@ def test_list_submissions(self, m): def test_list_multiple_submissions(self, m): register_uris({'section': ['list_multiple_submissions']}, m) - submissions = self.section.list_multiple_submissions() + with warnings.catch_warnings(record=True) as warning_list: + submissions = self.section.list_multiple_submissions() + submission_list = [submission for submission in submissions] + + self.assertEqual(len(submission_list), 2) + self.assertIsInstance(submission_list[0], Submission) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_multiple_submission() + def test_get_multiple_submissions(self, m): + register_uris({'section': ['list_multiple_submissions']}, m) + + submissions = self.section.get_multiple_submissions() submission_list = [submission for submission in submissions] self.assertEqual(len(submission_list), 2) self.assertIsInstance(submission_list[0], Submission) - def test_list_multiple_submissions_grouped_param(self, m): + def test_get_multiple_submissions_grouped_param(self, m): register_uris({'section': ['list_multiple_submissions']}, m) with warnings.catch_warnings(record=True) as warning_list: warnings.simplefilter('always') - submissions = self.section.list_multiple_submissions(grouped=True) + submissions = self.section.get_multiple_submissions(grouped=True) submission_list = [submission for submission in submissions] # Ensure using the `grouped` param raises a warning From 02f3b4db1b7907e8fe781dd5ad4f6b32cfb0efcc Mon Sep 17 00:00:00 2001 From: Elli Howard Date: Fri, 9 Mar 2018 14:59:23 -0500 Subject: [PATCH 42/91] Deprecated list_ methods in user --- canvasapi/user.py | 139 +++++++++++++++++++++++++- tests/test_communication_channel.py | 2 +- tests/test_notification_preference.py | 2 +- tests/test_user.py | 101 +++++++++++++++++-- 4 files changed, 232 insertions(+), 12 deletions(-) diff --git a/canvasapi/user.py b/canvasapi/user.py index a91a32f3..22dbc93e 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals from six import python_2_unicode_compatible +import warnings from canvasapi.calendar_event import CalendarEvent from canvasapi.canvas_object import CanvasObject @@ -299,6 +300,29 @@ def list_calendar_events_for_user(self, **kwargs): """ List calendar events that the current user can view or manage. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_calendar_events_for_user` instead. + + :calls: `GET /api/v1/users/:user_id/calendar_events \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.calendar_event.CalendarEvent` + """ + warnings.warn( + "`list_calendar_events_for_user`" + " is being deprecated and will be removed in a future version." + " Use `get_calendar_events_for_user` instead", + DeprecationWarning + ) + + return self.get_calendar_events_for_user(**kwargs) + + def get_calendar_events_for_user(self, **kwargs): + """ + List calendar events that the current user can view or manage. + :calls: `GET /api/v1/users/:user_id/calendar_events \ `_ @@ -318,6 +342,30 @@ def list_communication_channels(self, **kwargs): List communication channels for the specified user, sorted by position. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_communication_channels` instead. + + :calls: `GET /api/v1/users/:user_id/communication_channels \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.communication_channel.CommunicationChannel` + """ + warnings.warn( + "`list_communication_channels`" + " is being deprecated and will be removed in a future version." + " Use `get_communication_channels` instead", + DeprecationWarning + ) + + return self.get_communication_channels(**kwargs) + + def get_communication_channels(self, **kwargs): + """ + List communication channels for the specified user, sorted by + position. + :calls: `GET /api/v1/users/:user_id/communication_channels \ `_ @@ -336,6 +384,28 @@ def list_files(self, **kwargs): """ Returns the paginated list of files for the user. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_files` instead. + + :calls: `GET api/v1/courses/:user_id/files \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.file.File` + """ + warnings.warn( + "`list_files` is being deprecated and will be removed in a future version." + " Use `get_files` instead", + DeprecationWarning + ) + + return self.get_files(**kwargs) + + def get_files(self, **kwargs): + """ + Returns the paginated list of files for the user. + :calls: `GET api/v1/courses/:user_id/files \ `_ @@ -397,7 +467,30 @@ def get_folder(self, folder): ) return Folder(self._requester, response.json()) - def list_folders(self): + def list_folders(self, **kwargs): + """ + Returns the paginated list of all folders for the given user. This will be returned as a + flat list containing all subfolders as well. + + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_folders` instead. + + :calls: `GET /api/v1/users/:user_id/folders \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.folder.Folder` + """ + warnings.warn( + "`list_folders` is being deprecated and will be removed in a future version." + " Use `get_folders` instead", + DeprecationWarning + ) + + return self.get_folders(**kwargs) + + def get_folders(self, **kwargs): """ Returns the paginated list of all folders for the given user. This will be returned as a flat list containing all subfolders as well. @@ -438,6 +531,28 @@ def list_user_logins(self, **kwargs): """ Given a user ID, return that user's logins for the given account. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_user_logins` instead. + + :calls: `GET /api/v1/users/:user_id/logins \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.login.Login` + """ + warnings.warn( + "`list_user_logins` is being deprecated and will be removed in a future version." + " Use `get_user_logins` instead", + DeprecationWarning + ) + + return self. get_user_logins(**kwargs) + + def get_user_logins(self, **kwargs): + """ + Given a user ID, return that user's logins for the given account. + :calls: `GET /api/v1/users/:user_id/logins \ `_ @@ -458,6 +573,28 @@ def list_observees(self, **kwargs): """ List the users that the given user is observing + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.user.User.get_observees` instead. + + :calls: `GET /api/v1/users/:user_id/observees \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.user.User` + """ + warnings.warn( + "`list_observees` is being deprecated and will be removed in a future version." + " Use `get_observees` instead", + DeprecationWarning + ) + + return self.get_observees(**kwargs) + + def get_observees(self, **kwargs): + """ + List the users that the given user is observing + :calls: `GET /api/v1/users/:user_id/observees \ `_ diff --git a/tests/test_communication_channel.py b/tests/test_communication_channel.py index f72fc513..4fea89ff 100644 --- a/tests/test_communication_channel.py +++ b/tests/test_communication_channel.py @@ -20,7 +20,7 @@ def setUp(self): register_uris({'user': ['get_by_id', 'list_comm_channels']}, m) self.user = self.canvas.get_user(1) - self.comm_chan = self.user.list_communication_channels()[0] + self.comm_chan = self.user.get_communication_channels()[0] # __str__() def test__str__(self, m): diff --git a/tests/test_notification_preference.py b/tests/test_notification_preference.py index 56655765..41ad6bdd 100644 --- a/tests/test_notification_preference.py +++ b/tests/test_notification_preference.py @@ -22,7 +22,7 @@ def setUp(self): }, m) self.user = self.canvas.get_user(1) - self.comm_chan = self.user.list_communication_channels()[0] + self.comm_chan = self.user.get_communication_channels()[0] self.notif_pref = self.comm_chan.get_preference('new_announcement') # __str__() diff --git a/tests/test_user.py b/tests/test_user.py index eb5fbd82..6b1fccd3 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, division, print_function, unicode_literals import unittest import uuid +import warnings import requests_mock @@ -186,8 +187,8 @@ def test_user_get_assignments(self, m): self.assertIsInstance(assignments_by_obj[0], Assignment) self.assertEqual(len(assignment_list), 4) - # list_enrollments() - def test_list_enrollments(self, m): + # get_enrollments() + def test_get_enrollments(self, m): register_uris({'user': ['list_enrollments', 'list_enrollments_2']}, m) enrollments = self.user.get_enrollments() @@ -216,7 +217,20 @@ def test_upload(self, m): def test_list_calendar_events_for_user(self, m): register_uris({'user': ['list_calendar_events_for_user']}, m) - cal_events = self.user.list_calendar_events_for_user() + with warnings.catch_warnings(record=True) as warning_list: + cal_events = self.user.list_calendar_events_for_user() + cal_event_list = [cal_event for cal_event in cal_events] + self.assertEqual(len(cal_event_list), 2) + self.assertIsInstance(cal_event_list[0], CalendarEvent) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_calendar_events_for_user() + def test_get_calendar_events_for_user(self, m): + register_uris({'user': ['list_calendar_events_for_user']}, m) + + cal_events = self.user.get_calendar_events_for_user() cal_event_list = [cal_event for cal_event in cal_events] self.assertEqual(len(cal_event_list), 2) self.assertIsInstance(cal_event_list[0], CalendarEvent) @@ -225,16 +239,42 @@ def test_list_calendar_events_for_user(self, m): def test_list_communication_channels(self, m): register_uris({'user': ['list_comm_channels', 'list_comm_channels2']}, m) - comm_channels = self.user.list_communication_channels() + with warnings.catch_warnings(record=True) as warning_list: + comm_channels = self.user.list_communication_channels() + channel_list = [channel for channel in comm_channels] + self.assertEqual(len(channel_list), 4) + self.assertIsInstance(channel_list[0], CommunicationChannel) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_communication_channels() + def test_get_communication_channels(self, m): + register_uris({'user': ['list_comm_channels', 'list_comm_channels2']}, m) + + comm_channels = self.user.get_communication_channels() channel_list = [channel for channel in comm_channels] self.assertEqual(len(channel_list), 4) self.assertIsInstance(channel_list[0], CommunicationChannel) # list_files() - def test_user_files(self, m): + def test_list_files(self, m): register_uris({'user': ['get_user_files', 'get_user_files2']}, m) - files = self.user.list_files() + with warnings.catch_warnings(record=True) as warning_list: + files = self.user.list_files() + file_list = [file for file in files] + self.assertEqual(len(file_list), 4) + self.assertIsInstance(file_list[0], File) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_files() + def test_get_files(self, m): + register_uris({'user': ['get_user_files', 'get_user_files2']}, m) + + files = self.user.get_files() file_list = [file for file in files] self.assertEqual(len(file_list), 4) self.assertIsInstance(file_list[0], File) @@ -269,7 +309,20 @@ def test_get_folder(self, m): def test_list_folders(self, m): register_uris({'user': ['list_folders']}, m) - folders = self.user.list_folders() + with warnings.catch_warnings(record=True) as warning_list: + folders = self.user.list_folders() + folder_list = [folder for folder in folders] + self.assertEqual(len(folder_list), 2) + self.assertIsInstance(folder_list[0], Folder) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_folders() + def test_get_folders(self, m): + register_uris({'user': ['list_folders']}, m) + + folders = self.user.get_folders() folder_list = [folder for folder in folders] self.assertEqual(len(folder_list), 2) self.assertIsInstance(folder_list[0], Folder) @@ -287,7 +340,22 @@ def test_list_user_logins(self, m): requires = {'user': ['list_user_logins', 'list_user_logins_2']} register_uris(requires, m) - response = self.user.list_user_logins() + with warnings.catch_warnings(record=True) as warning_list: + response = self.user.list_user_logins() + login_list = [login for login in response] + + self.assertIsInstance(login_list[0], Login) + self.assertEqual(len(login_list), 2) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_user_logins() + def test_get_user_logins(self, m): + requires = {'user': ['list_user_logins', 'list_user_logins_2']} + register_uris(requires, m) + + response = self.user.get_user_logins() login_list = [login for login in response] self.assertIsInstance(login_list[0], Login) @@ -298,7 +366,22 @@ def test_list_observees(self, m): requires = {'user': ['list_observees', 'list_observees_2']} register_uris(requires, m) - response = self.user.list_observees() + with warnings.catch_warnings(record=True) as warning_list: + response = self.user.list_observees() + observees_list = [observees for observees in response] + + self.assertIsInstance(observees_list[0], User) + self.assertEqual(len(observees_list), 4) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + # get_observees() + def test_get_observees(self, m): + requires = {'user': ['list_observees', 'list_observees_2']} + register_uris(requires, m) + + response = self.user.get_observees() observees_list = [observees for observees in response] self.assertIsInstance(observees_list[0], User) From 74a1770060cb7cc7b02fc1dad206bd772431ead8 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Thu, 22 Mar 2018 11:25:12 -0400 Subject: [PATCH 43/91] Added docs for content-migrations --- docs/content-migration-ref.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/content-migration-ref.rst b/docs/content-migration-ref.rst index 64016172..ce926377 100644 --- a/docs/content-migration-ref.rst +++ b/docs/content-migration-ref.rst @@ -4,3 +4,9 @@ ContentMigration .. autoclass:: canvasapi.content_migration.ContentMigration :members: + +.. autoclass:: canvasapi.content_migration.MigrationIssue + :members: + +.. autoclass:: canvasapi.content_migration.Migrator + :members: From 396bcad54220f02d9e16655edba7659830d43e52 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Thu, 22 Mar 2018 11:36:22 -0400 Subject: [PATCH 44/91] Removed .coverage file from docs dir --- docs/.coverage | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/.coverage diff --git a/docs/.coverage b/docs/.coverage deleted file mode 100644 index fe8af938..00000000 --- a/docs/.coverage +++ /dev/null @@ -1 +0,0 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{}} \ No newline at end of file From 29aed0ab605bce073f5350d2c5cc975b5121c290 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Thu, 22 Mar 2018 11:38:23 -0400 Subject: [PATCH 45/91] Changed .gitignore to ignore all .coverage files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 580a6fdc..cb6af1d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ *.pyc *~ +.coverage .DS_Store /*.egg-info -/.coverage /.project /.pyproject /_build/ From 20c0944a996e2713929f641f6db109d313677cdc Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Thu, 22 Mar 2018 11:56:00 -0400 Subject: [PATCH 46/91] Added file reference to docs. Updated param in copy_file to better reflect Canvas docs. Minor docstring fixes. --- canvasapi/canvas.py | 2 +- canvasapi/folder.py | 7 +++++-- docs/class-reference.rst | 1 + docs/file-ref.rst | 6 ++++++ docs/folder-ref.rst | 4 ++-- 5 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 docs/file-ref.rst diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index f43c0073..b1c806bd 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -467,7 +467,7 @@ def create_conversation(self, recipients, body, **kwargs): :param recipients: An array of recipient ids. These may be user ids or course/group ids prefixed - with 'course\_' or 'group\_' respectively, + with 'course\\_' or 'group\\_' respectively, e.g. recipients=['1', '2', 'course_3'] :type recipients: `list` of `str` :param body: The body of the message being added. diff --git a/canvasapi/folder.py b/canvasapi/folder.py index 6a52d0e2..d660cfd0 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -106,18 +106,21 @@ def update(self, **kwargs): return Folder(self._requester, response.json()) - def copy_file(self, file, **kwargs): + def copy_file(self, source_file, **kwargs): """ Copies a file into the current folder. :calls: `POST /api/v1/folders/:dest_folder_id/copy_file \ `_ + :param source_file: The object or id of the source file. + :type source_file: int or :class:`canvasapi.file.File` + :rtype: :class:`canvasapi.folder.Folder` """ from canvasapi.file import File - file_id = obj_or_id(file, "file", (File,)) + file_id = obj_or_id(source_file, "source_file", (File,)) kwargs['source_file_id'] = file_id response = self._requester.request( diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 20f8b717..3a1095a9 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -18,6 +18,7 @@ Class Reference discussion-topic-ref enrollment-term-ref external-tool-ref + file-ref folder-ref group-ref login-ref diff --git a/docs/file-ref.rst b/docs/file-ref.rst new file mode 100644 index 00000000..0a14adfd --- /dev/null +++ b/docs/file-ref.rst @@ -0,0 +1,6 @@ +==== +File +==== + +.. autoclass:: canvasapi.file.File + :members: diff --git a/docs/folder-ref.rst b/docs/folder-ref.rst index b8f513b4..a47e3526 100644 --- a/docs/folder-ref.rst +++ b/docs/folder-ref.rst @@ -1,6 +1,6 @@ -============= +====== Folder -============= +====== .. autoclass:: canvasapi.folder.Folder :members: From 10227cd7b2a600b55a1edf92b14c095971be3caa Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Thu, 22 Mar 2018 12:35:06 -0400 Subject: [PATCH 47/91] Adjust warning catching to work for py2 --- tests/test_canvas.py | 8 ++++++++ tests/test_discussion_topic.py | 4 ++++ tests/test_outcome.py | 16 ++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/tests/test_canvas.py b/tests/test_canvas.py index 6cb0953e..73e0f297 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -584,6 +584,10 @@ def test_list_user_participants(self, m): users_list_by_id = [user for user in users_by_id] self.assertEqual(len(users_list_by_id), 2) + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: appointment_group_for_obj = self.canvas.get_appointment_group(222) users_by_id = self.canvas.list_user_participants(appointment_group_for_obj) users_list_by_id = [user for user in users_by_id] @@ -626,6 +630,10 @@ def test_list_group_participants(self, m): groups_list_by_id = [group for group in groups_by_id] self.assertEqual(len(groups_list_by_id), 2) + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: appointment_group_for_obj = self.canvas.get_appointment_group(222) groups_by_obj = self.canvas.list_group_participants(appointment_group_for_obj) groups_list_by_obj = [group for group in groups_by_obj] diff --git a/tests/test_discussion_topic.py b/tests/test_discussion_topic.py index 317e0460..174deff6 100644 --- a/tests/test_discussion_topic.py +++ b/tests/test_discussion_topic.py @@ -107,6 +107,10 @@ def test_list_entries(self, m): self.assertTrue(hasattr(entry_by_id, 'message')) self.assertEqual(entry_by_id.message, 'Lower level entry') + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: entries_by_obj = self.discussion_topic.list_entries(entries_by_id) entry_list_by_obj = [entry for entry in entries_by_obj] self.assertTrue(len(entry_list_by_obj), 3) diff --git a/tests/test_outcome.py b/tests/test_outcome.py index d466d9fc..1f46c10f 100644 --- a/tests/test_outcome.py +++ b/tests/test_outcome.py @@ -210,11 +210,19 @@ def test_list_linked_outcomes(self, m): self.assertEqual(result[0].outcome_group['id'], 2) self.assertEqual(result[0].outcome_group['title'], "Account Test Outcome Group") + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: result = self.canvas_outcome_group.list_linked_outcomes() self.assertIsInstance(result[0], OutcomeLink) self.assertEqual(result[0].outcome_group['id'], 2) self.assertEqual(result[0].outcome_group['title'], "Global Test Outcome Group") + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: result = self.course_outcome_group.list_linked_outcomes() self.assertIsInstance(result[0], OutcomeLink) self.assertEqual(result[0].outcome_group['id'], 2) @@ -366,6 +374,10 @@ def test_list_subgroups(self, m): self.assertEqual(result[1].id, 3) self.assertEqual(result[1].title, "Global Listed Subgroup Title 2") + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: result = self.account_outcome_group.list_subgroups() self.assertIsInstance(result[0], OutcomeGroup) self.assertEqual(result[0].id, 2) @@ -374,6 +386,10 @@ def test_list_subgroups(self, m): self.assertEqual(result[1].id, 3) self.assertEqual(result[1].title, "Account Listed Subgroup Title 2") + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) + + with warnings.catch_warnings(record=True) as warning_list: result = self.course_outcome_group.list_subgroups() self.assertIsInstance(result[0], OutcomeGroup) self.assertEqual(result[0].id, 2) From 9c0d7c3d9a67428d6f1a5e88098a3c620913796a Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Mon, 26 Mar 2018 12:12:44 -0400 Subject: [PATCH 48/91] Created new Tab.update method. Deprecated Course.update_tab. --- canvasapi/course.py | 19 ++++++++++++++----- canvasapi/group.py | 1 + canvasapi/tab.py | 28 ++++++++++++++++++++++++++++ docs/class-reference.rst | 1 + docs/tab-ref.rst | 6 ++++++ tests/test_course.py | 11 ++++++++--- tests/test_tab.py | 25 +++++++++++++++++++++---- 7 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 docs/tab-ref.rst diff --git a/canvasapi/course.py b/canvasapi/course.py index 44a72e2f..17d16129 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -1583,6 +1583,7 @@ def list_tabs(self, **kwargs): self._requester, 'GET', 'courses/{}/tabs'.format(self.id), + {'course_id': self.id}, _kwargs=combine_kwargs(**kwargs) ) @@ -1590,6 +1591,10 @@ def update_tab(self, tab_id, **kwargs): """ Update a tab for a course. + .. warning:: + .. deprecated:: 0.10.0 + Use :func:`canvasapi.tab.Tab.update` instead. + :calls: `PUT /api/v1/courses/:course_id/tabs/:tab_id \ `_ @@ -1598,13 +1603,17 @@ def update_tab(self, tab_id, **kwargs): :rtype: :class:`canvasapi.tab.Tab` """ - response = self._requester.request( - 'PUT', - 'courses/{}/tabs/{}'.format(self.id, tab_id), - _kwargs=combine_kwargs(**kwargs) + warnings.warn( + "`Course.update_tab()` is being deprecated and will be removed in " + "a future version. Use `Tab.update()` instead", + DeprecationWarning ) - return Tab(self._requester, response.json()) + tab = Tab(self._requester, { + 'course_id': self.id, + 'id': tab_id + }) + return tab.update(**kwargs) def get_rubric(self, rubric_id, **kwargs): """ diff --git a/canvasapi/group.py b/canvasapi/group.py index c39c8bae..c0223945 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -664,6 +664,7 @@ def list_tabs(self, **kwargs): self._requester, 'GET', 'groups/{}/tabs'.format(self.id), + {'group_id': self.id}, _kwargs=combine_kwargs(**kwargs) ) diff --git a/canvasapi/tab.py b/canvasapi/tab.py index d345e40d..88ce8dc9 100644 --- a/canvasapi/tab.py +++ b/canvasapi/tab.py @@ -3,6 +3,7 @@ from six import python_2_unicode_compatible from canvasapi.canvas_object import CanvasObject +from canvasapi.util import combine_kwargs @python_2_unicode_compatible @@ -10,3 +11,30 @@ class Tab(CanvasObject): def __str__(self): return "{} ({})".format(self.label, self.id) + + def update(self, **kwargs): + """ + Update a tab for a course. + + Note: Home and Settings tabs are not manageable, and can't be + hidden or moved. + + :calls: `PUT /api/v1/courses/:course_id/tabs/:tab_id \ + `_ + + :rtype: :class:`canvasapi.tab.Tab` + """ + if not hasattr(self, 'course_id'): + raise ValueError('Can only update tabs from a Course.') + + response = self._requester.request( + 'PUT', + 'courses/{}/tabs/{}'.format(self.course_id, self.id), + _kwargs=combine_kwargs(**kwargs) + ) + response_json = response.json() + response_json.update({'course_id': self.course_id}) + + super(Tab, self).set_attributes(response.json()) + + return self diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 4ca9cd4c..3ab4956b 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -29,5 +29,6 @@ Class Reference rubric-ref section-ref submission-ref + tab-ref upload-ref user-ref diff --git a/docs/tab-ref.rst b/docs/tab-ref.rst new file mode 100644 index 00000000..32f45ffb --- /dev/null +++ b/docs/tab-ref.rst @@ -0,0 +1,6 @@ +=== +Tab +=== + +.. autoclass:: canvasapi.tab.Tab + :members: diff --git a/tests/test_course.py b/tests/test_course.py index 2509579c..f653318e 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -1026,10 +1026,15 @@ def test_update_tab(self, m): tab_id = "pages" new_position = 3 - tab = self.course.update_tab(tab_id, position=new_position) - self.assertIsInstance(tab, Tab) - self.assertEqual(tab.position, 3) + with warnings.catch_warnings(record=True) as warning_list: + tab = self.course.update_tab(tab_id, position=new_position) + + self.assertIsInstance(tab, Tab) + self.assertEqual(tab.position, 3) + + self.assertEqual(len(warning_list), 1) + self.assertEqual(warning_list[-1].category, DeprecationWarning) # get_rubric def test_get_rubric(self, m): diff --git a/tests/test_tab.py b/tests/test_tab.py index 91eaabc5..4c6caf9d 100644 --- a/tests/test_tab.py +++ b/tests/test_tab.py @@ -4,6 +4,7 @@ import requests_mock from canvasapi import Canvas +from canvasapi.tab import Tab from tests import settings from tests.util import register_uris @@ -16,17 +17,33 @@ def setUp(self): with requests_mock.Mocker() as m: register_uris({ - 'course': ['get_by_id', 'list_tabs'] + 'course': ['get_by_id', 'list_tabs'], + 'group': ['get_by_id', 'list_tabs'] }, m) self.course = self.canvas.get_course(1) - tabs = self.course.list_tabs() - tab_list = [tab for tab in tabs] + self.tab = tabs[1] - self.tab = tab_list[0] + self.group = self.canvas.get_group(1) + group_tabs = self.group.list_tabs() + self.tab_group = group_tabs[1] # __str__() def test__str__(self, m): string = str(self.tab) self.assertIsInstance(string, str) + + # update() + def test_update_course(self, m): + register_uris({'course': ['update_tab']}, m) + + new_position = 3 + self.tab.update(position=new_position) + + self.assertIsInstance(self.tab, Tab) + self.assertEqual(self.tab.position, 3) + + def test_update_group(self, m): + with self.assertRaises(ValueError): + self.tab_group.update(position=1) From 9f95820c2868534e55c9a11b35d11cc3f46fd017 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Wed, 28 Mar 2018 13:29:11 -0400 Subject: [PATCH 49/91] Formatting nitpicks on a few warning strings. Fixed a few missing kwargs lines --- canvasapi/account.py | 31 +++++++++++++++---------------- canvasapi/canvas.py | 14 ++++++-------- canvasapi/course.py | 24 ++++++++++++------------ canvasapi/current_user.py | 8 ++++---- canvasapi/discussion_topic.py | 12 ++++++------ canvasapi/folder.py | 8 ++++---- canvasapi/group.py | 28 ++++++++++++++-------------- canvasapi/module.py | 4 ++-- canvasapi/outcome.py | 11 ++++++----- canvasapi/page.py | 4 ++-- canvasapi/user.py | 15 ++++++++------- 11 files changed, 79 insertions(+), 80 deletions(-) diff --git a/canvasapi/account.py b/canvasapi/account.py index 3e3a6351..2466e247 100644 --- a/canvasapi/account.py +++ b/canvasapi/account.py @@ -622,7 +622,7 @@ def create_group_category(self, name, **kwargs): def list_group_categories(self, **kwargs): """ - List group categories for a context + List group categories for a context. .. warning:: .. deprecated:: 0.10.0 @@ -634,10 +634,9 @@ def list_group_categories(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.GroupCategory` """ - warnings.warn( - "`list_group_categories` is being deprecated and will be removed in a future version." - " Use `get_group_categories` instead", + "`list_group_categories` is being deprecated and will be removed " + "in a future version. Use `get_group_categories` instead", DeprecationWarning ) @@ -645,7 +644,7 @@ def list_group_categories(self, **kwargs): def get_group_categories(self, **kwargs): """ - List group categories for a context + List group categories for a context. :calls: `GET /api/v1/accounts/:account_id/group_categories \ `_ @@ -716,7 +715,7 @@ def create_enrollment_term(self, **kwargs): def list_enrollment_terms(self, **kwargs): """ - List enrollment terms for a context + List enrollment terms for a context. .. warning:: .. deprecated:: 0.10.0 @@ -729,8 +728,8 @@ def list_enrollment_terms(self, **kwargs): :class:`canvasapi.enrollment_term.EnrollmentTerm` """ warnings.warn( - "`list_enrollment_terms` is being deprecated and will be removed in a future version." - " Use `get_enrollment_terms` instead", + "`list_enrollment_terms` is being deprecated and will be removed " + "in a future version. Use `get_enrollment_terms` instead", DeprecationWarning ) @@ -738,7 +737,7 @@ def list_enrollment_terms(self, **kwargs): def get_enrollment_terms(self, **kwargs): """ - List enrollment terms for a context + List enrollment terms for a context. :calls: `GET /api/v1/accounts/:account_id/terms \ `_ @@ -773,8 +772,8 @@ def list_user_logins(self, **kwargs): :class:`canvasapi.login.Login` """ warnings.warn( - "`list_user_logins` is being deprecated and will be removed in a future version." - " Use `get_user_logins` instead", + "`list_user_logins` is being deprecated and will be removed in a " + "future version. Use `get_user_logins` instead", DeprecationWarning ) @@ -1027,9 +1026,9 @@ def list_authentication_providers(self, **kwargs): :class:`canvasapi.authentication_provider.AuthenticationProvider` """ warnings.warn( - "`list_authentication_providers`" - " is being deprecated and will be removed in a future version." - " Use `get_authentication_providers` instead", + "`list_authentication_providers` is being deprecated and will be " + "removed in a future version. Use `get_authentication_providers` " + "instead.", DeprecationWarning ) @@ -1305,8 +1304,8 @@ def list_rubrics(self, **kwargs): :class:`canvasapi.rubric.Rubric` """ warnings.warn( - "`list_rubrics` is being deprecated and will be removed in a future version." - " Use `get_rubrics` instead", + "`list_rubrics` is being deprecated and will be removed in a " + "future version. Use `get_rubrics` instead.", DeprecationWarning ) diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 51c1d3dc..285afb1e 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -676,8 +676,8 @@ def list_calendar_events(self, **kwargs): :class:`canvasapi.calendar_event.CalendarEvent` """ warnings.warn( - "`list_calendar_events` is being deprecated and will be removed in a future version." - " Use `get_calendar_events` instead", + "`list_calendar_events` is being deprecated and will be removed " + "in a future version. Use `get_calendar_events` instead", DeprecationWarning ) @@ -773,9 +773,8 @@ def list_appointment_groups(self, **kwargs): :class:`canvasapi.appointment_group.AppointmentGroup` """ warnings.warn( - "`list_appointment_groups`" - " is being deprecated and will be removed in a future version." - " Use `get_appointment_groups` instead", + "`list_appointment_groups` is being deprecated and will be removed" + " in a future version. Use `get_appointment_groups` instead.", DeprecationWarning ) @@ -934,9 +933,8 @@ def list_group_participants(self, appointment_group, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` """ warnings.warn( - "`list_group_participants`" - " is being deprecated and will be removed in a future version." - " Use `get_group_participants` instead", + "`list_group_participants` is being deprecated and will be removed " + "in a future version. Use `get_group_participants` instead", DeprecationWarning ) diff --git a/canvasapi/course.py b/canvasapi/course.py index 26a9b532..7336b8a9 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -964,8 +964,8 @@ def list_assignment_groups(self, **kwargs): :class:`canvasapi.assignment.AssignmentGroup` """ warnings.warn( - "`list_assignment_groups` is being deprecated and will be removed in a future version." - " Use `get_assignment_groups` instead", + "`list_assignment_groups` is being deprecated and will be removed " + "in a future version. Use `get_assignment_groups` instead", DeprecationWarning ) @@ -1536,8 +1536,8 @@ def list_external_feeds(self, **kwargs): :class:`canvasapi.external_feed.ExternalFeed` """ warnings.warn( - "`list_external_feeds` is being deprecated and will be removed in a future version." - " Use `get_external_feeds` instead", + "`list_external_feeds` is being deprecated and will be removed in " + "a future version. Use `get_external_feeds` instead", DeprecationWarning ) @@ -1619,8 +1619,8 @@ def list_files(self, **kwargs): :class:`canvasapi.file.File` """ warnings.warn( - "`list_files` is being deprecated and will be removed in a future version." - " Use `get_files` instead", + "`list_files` is being deprecated and will be removed in a future " + "version. Use `get_files` instead", DeprecationWarning ) @@ -1682,8 +1682,8 @@ def list_folders(self, **kwargs): :class:`canvasapi.folder.Folder` """ warnings.warn( - "`list_folders` is being deprecated and will be removed in a future version." - " Use `get_folders` instead", + "`list_folders` is being deprecated and will be removed in a " + "future version. Use `get_folders` instead", DeprecationWarning ) @@ -1743,8 +1743,8 @@ def list_tabs(self, **kwargs): :class:`canvasapi.tab.Tab` """ warnings.warn( - "`list_tabs` is being deprecated and will be removed in a future version." - " Use `get_tabs` instead", + "`list_tabs` is being deprecated and will be removed in a future " + "version. Use `get_tabs` instead", DeprecationWarning ) @@ -1823,8 +1823,8 @@ def list_rubrics(self, **kwargs): :class:`canvasapi.rubric.Rubric` """ warnings.warn( - "`list_rubrics` is being deprecated and will be removed in a future version." - " Use `get_rubrics` instead", + "`list_rubrics` is being deprecated and will be removed in a " + "future version. Use `get_rubrics` instead", DeprecationWarning ) diff --git a/canvasapi/current_user.py b/canvasapi/current_user.py index 6c34417e..e1cca6ae 100644 --- a/canvasapi/current_user.py +++ b/canvasapi/current_user.py @@ -39,8 +39,8 @@ def list_groups(self, **kwargs): :rtype: :class:`canvasapi.paginated_list.PaginatedList` of :class:`canvasapi.group.Group` """ warnings.warn( - "`list_groups` is being deprecated and will be removed in a future version." - " Use `get_groups` instead", + "`list_groups` is being deprecated and will be removed in a " + "future version. Use `get_groups` instead", DeprecationWarning ) @@ -80,8 +80,8 @@ def list_bookmarks(self, **kwargs): :class:`canvasapi.bookmark.Bookmark` """ warnings.warn( - "`list_bookmarks` is being deprecated and will be removed in a future version." - " Use `get_bookmarks` instead", + "`list_bookmarks` is being deprecated and will be removed in a " + "future version. Use `get_bookmarks` instead", DeprecationWarning ) diff --git a/canvasapi/discussion_topic.py b/canvasapi/discussion_topic.py index 5ca1db14..0f2a71d5 100644 --- a/canvasapi/discussion_topic.py +++ b/canvasapi/discussion_topic.py @@ -153,8 +153,8 @@ def list_topic_entries(self, **kwargs): :class:`canvasapi.discussion_topic.DiscussionEntry` """ warnings.warn( - "`list_topic_entries` is being deprecated and will be removed in a future version." - " Use `get_topic_entries` instead", + "`list_topic_entries` is being deprecated and will be removed in " + "a future version. Use `get_topic_entries` instead", DeprecationWarning ) @@ -211,8 +211,8 @@ def list_entries(self, ids, **kwargs): :class:`canvasapi.discussion_topic.DiscussionEntry` """ warnings.warn( - "`list_entries` is being deprecated and will be removed in a future version." - " Use `get_entries` instead", + "`list_entries` is being deprecated and will be removed in a " + "future version. Use `get_entries` instead", DeprecationWarning ) @@ -545,8 +545,8 @@ def list_replies(self, **kwargs): :class:`canvasapi.discussion_topic.DiscussionEntry` """ warnings.warn( - "`list_replies` is being deprecated and will be removed in a future version." - " Use `get_replies` instead", + "`list_replies` is being deprecated and will be removed in a " + "future version. Use `get_replies` instead.", DeprecationWarning ) diff --git a/canvasapi/folder.py b/canvasapi/folder.py index 10600069..529f2684 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -30,8 +30,8 @@ def list_files(self, **kwargs): :class:`canvasapi.file.File` """ warnings.warn( - "`list_files` is being deprecated and will be removed in a future version." - " Use `get_files` instead", + "`list_files` is being deprecated and will be removed in a future " + "version. Use `get_files` instead", DeprecationWarning ) @@ -89,8 +89,8 @@ def list_folders(self, **kwargs): :class:`canvasapi.folder.Folder` """ warnings.warn( - "`list_folders` is being deprecated and will be removed in a future version." - " Use `get_folders` instead", + "`list_folders` is being deprecated and will be removed in a " + "future version. Use `get_folders` instead", DeprecationWarning ) diff --git a/canvasapi/group.py b/canvasapi/group.py index f41e1ed0..ec4e0063 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -200,8 +200,8 @@ def list_users(self, **kwargs): :class:`canvasapi.user.User` """ warnings.warn( - "`list_users` is being deprecated and will be removed in a future version." - " Use `get_users` instead", + "`list_users` is being deprecated and will be removed in a future " + "version. Use `get_users` instead", DeprecationWarning ) @@ -322,8 +322,8 @@ def list_memberships(self, **kwargs): :class:`canvasapi.group.GroupMembership` """ warnings.warn( - "`list_memberships` is being deprecated and will be removed in a future version." - " Use `get_memberships` instead", + "`list_memberships` is being deprecated and will be removed in a " + "future version. Use `get_memberships` instead.", DeprecationWarning ) @@ -572,8 +572,8 @@ def list_external_feeds(self, **kwargs): :class:`canvasapi.external_feed.ExternalFeed` """ warnings.warn( - "`list_external_feeds` is being deprecated and will be removed in a future version." - " Use `get_external_feeds` instead", + "`list_external_feeds` is being deprecated and will be removed in " + "a future version. Use `get_external_feeds` instead", DeprecationWarning ) @@ -654,8 +654,8 @@ def list_files(self, **kwargs): :class:`canvasapi.file.File` """ warnings.warn( - "`list_files` is being deprecated and will be removed in a future version." - " Use `get_files` instead", + "`list_files` is being deprecated and will be removed in a future " + "version. Use `get_files` instead.", DeprecationWarning ) @@ -717,8 +717,8 @@ def list_folders(self, **kwargs): :class:`canvasapi.folder.Folder` """ warnings.warn( - "`list_folders` is being deprecated and will be removed in a future version." - " Use `get_folders` instead", + "`list_folders` is being deprecated and will be removed in a " + "future version. Use `get_folders` instead.", DeprecationWarning ) @@ -777,8 +777,8 @@ def list_tabs(self, **kwargs): :class:`canvasapi.tab.Tab` """ warnings.warn( - "`list_tabs` is being deprecated and will be removed in a future version." - " Use `get_tabs` instead", + "`list_tabs` is being deprecated and will be removed in a future " + "version. Use `get_tabs` instead.", DeprecationWarning ) @@ -1035,8 +1035,8 @@ def list_groups(self, **kwargs): :class:`canvasapi.group.Group` """ warnings.warn( - "`list_groups` is being deprecated and will be removed in a future version." - " Use `get_groups` instead", + "`list_groups` is being deprecated and will be removed in a " + "future version. Use `get_groups` instead.", DeprecationWarning ) diff --git a/canvasapi/module.py b/canvasapi/module.py index cf383cd2..277b0e57 100644 --- a/canvasapi/module.py +++ b/canvasapi/module.py @@ -89,8 +89,8 @@ def list_module_items(self, **kwargs): :class:`canvasapi.module.ModuleItem` """ warnings.warn( - "`list_module_items` is being deprecated and will be removed in a future version." - " Use `get_module_items` instead", + "`list_module_items` is being deprecated and will be removed in a " + "future version. Use `get_module_items` instead.", DeprecationWarning ) diff --git a/canvasapi/outcome.py b/canvasapi/outcome.py index 9dc25937..1d15be94 100644 --- a/canvasapi/outcome.py +++ b/canvasapi/outcome.py @@ -176,8 +176,8 @@ def list_linked_outcomes(self, **kwargs): :class:`canvasapi.outcome.OutcomeLink` """ warnings.warn( - "`list_linked_outcomes` is being deprecated and will be removed in a future version." - " Use `get_linked_outcomes` instead", + "`list_linked_outcomes` is being deprecated and will be removed " + "in a future version. Use `get_linked_outcomes` instead.", DeprecationWarning ) @@ -315,8 +315,8 @@ def list_subgroups(self, **kwargs): :class:`canvasapi.outcome.OutcomeGroup` """ warnings.warn( - "`list_subgroups` is being deprecated and will be removed in a future version." - " Use `get_subgroups` instead - ", + "`list_subgroups` is being deprecated and will be removed in a " + "future version. Use `get_subgroups` instead.", DeprecationWarning ) @@ -341,7 +341,8 @@ def get_subgroups(self, **kwargs): OutcomeGroup, self._requester, 'GET', - '{}/outcome_groups/{}/subgroups'.format(self.context_ref(), self.id) + '{}/outcome_groups/{}/subgroups'.format(self.context_ref(), self.id), + _kwargs=combine_kwargs(**kwargs) ) def create_subgroup(self, title, **kwargs): diff --git a/canvasapi/page.py b/canvasapi/page.py index 1557c6ca..d14dc1c5 100644 --- a/canvasapi/page.py +++ b/canvasapi/page.py @@ -167,8 +167,8 @@ def list_revisions(self, **kwargs): :class:`canvasapi.pagerevision.PageRevision` """ warnings.warn( - "`list_revisions` is being deprecated and will be removed in a future version." - " Use `get_revisions` instead", + "`list_revisions` is being deprecated and will be removed in a " + "future version. Use `get_revisions` instead.", DeprecationWarning ) diff --git a/canvasapi/user.py b/canvasapi/user.py index b89aa184..b969a7ef 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -395,8 +395,8 @@ def list_files(self, **kwargs): :class:`canvasapi.file.File` """ warnings.warn( - "`list_files` is being deprecated and will be removed in a future version." - " Use `get_files` instead", + "`list_files` is being deprecated and will be removed in a future " + "version. Use `get_files` instead", DeprecationWarning ) @@ -483,8 +483,8 @@ def list_folders(self, **kwargs): :class:`canvasapi.folder.Folder` """ warnings.warn( - "`list_folders` is being deprecated and will be removed in a future version." - " Use `get_folders` instead", + "`list_folders` is being deprecated and will be removed in a " + "future version. Use `get_folders` instead.", DeprecationWarning ) @@ -505,7 +505,8 @@ def get_folders(self, **kwargs): Folder, self._requester, 'GET', - 'users/{}/folders'.format(self.id) + 'users/{}/folders'.format(self.id), + _kwargs=combine_kwargs(**kwargs) ) def create_folder(self, name, **kwargs): @@ -584,8 +585,8 @@ def list_observees(self, **kwargs): :class:`canvasapi.user.User` """ warnings.warn( - "`list_observees` is being deprecated and will be removed in a future version." - " Use `get_observees` instead", + "`list_observees` is being deprecated and will be removed in a " + "future version. Use `get_observees` instead", DeprecationWarning ) From a3e3decda892cb3a80662ab657066dbe0b0cd43f Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Mon, 2 Apr 2018 11:05:29 -0400 Subject: [PATCH 50/91] Updated docs to include enrollment reference --- docs/class-reference.rst | 1 + docs/enrollment-ref.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 docs/enrollment-ref.rst diff --git a/docs/class-reference.rst b/docs/class-reference.rst index 413646e5..c546217c 100644 --- a/docs/class-reference.rst +++ b/docs/class-reference.rst @@ -18,6 +18,7 @@ Class Reference current-user-ref discussion-entry-ref discussion-topic-ref + enrollment-ref enrollment-term-ref external-tool-ref file-ref diff --git a/docs/enrollment-ref.rst b/docs/enrollment-ref.rst new file mode 100644 index 00000000..0bc49e4b --- /dev/null +++ b/docs/enrollment-ref.rst @@ -0,0 +1,6 @@ +========== +Enrollment +========== + +.. autoclass:: canvasapi.enrollment.Enrollment + :members: From 96d6ccbd41442da4df69b90cd96bd98ac3ebf907 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 3 Apr 2018 16:07:08 -0400 Subject: [PATCH 51/91] Added get_announcements method --- canvasapi/canvas.py | 21 +++++++++++++++++++++ tests/fixtures/announcements.json | 28 ++++++++++++++++++++++++++++ tests/test_canvas.py | 11 +++++++++++ 3 files changed, 60 insertions(+) create mode 100755 tests/fixtures/announcements.json diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 285afb1e..95c37042 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -365,8 +365,10 @@ def clear_course_nicknames(self): `_ :returns: True if the nicknames were cleared, False otherwise. + :rtype: bool """ + response = self.__requester.request( 'DELETE', 'users/self/course_nicknames' @@ -1133,3 +1135,22 @@ def get_progress(self, progress, **kwargs): _kwargs=combine_kwargs(**kwargs) ) return Progress(self.__requester, response.json()) + + def get_announcements(self, **kwargs): + """ + List announcements. + + :calls: `GET /api/v1/announcements \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.discussion_topic.DiscussionTopic` + """ + from canvasapi.discussion_topic import DiscussionTopic + return PaginatedList( + DiscussionTopic, + self.__requester, + 'GET', + 'announcements', + _kwargs=combine_kwargs(**kwargs) + ) diff --git a/tests/fixtures/announcements.json b/tests/fixtures/announcements.json new file mode 100755 index 00000000..b6370504 --- /dev/null +++ b/tests/fixtures/announcements.json @@ -0,0 +1,28 @@ +{ + "list_announcements": { + "method": "GET", + "endpoint": "announcements", + "data": [{ + "id": 1, + "title": "Announcment #1", + "author": { + "id": 1, + "display_name": "John Doe" + }, + "message": "Announcement Message #1", + "subscription_hold": "topic_is_announcement" + }, + { + "id": 2, + "title": "Announcement #2", + "author": { + "id": 2, + "display_name": "John Smith" + }, + "message": "Announcement Message #2", + "subscription_hold": "topic_is_announcement" + } + ], + "status_code": 200 + } +} diff --git a/tests/test_canvas.py b/tests/test_canvas.py index 73e0f297..b9b92da5 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -22,6 +22,8 @@ from canvasapi.progress import Progress from canvasapi.section import Section from canvasapi.user import User +from canvasapi.paginated_list import PaginatedList +from canvasapi.discussion_topic import DiscussionTopic from tests import settings from tests.util import register_uris @@ -736,3 +738,12 @@ def test_get_progress(self, m): self.assertIsInstance(progress, Progress) self.assertTrue(hasattr(progress, 'id')) self.assertEqual(progress.id, 1) + + # get_announcements() + def test_get_announcements(self, m): + register_uris({'announcements': ['list_announcements']},m) + announcements = self.canvas.get_announcements() + announcement_list = [announcement for announcement in announcements] + self.assertIsInstance(announcements, PaginatedList) + self.assertIsInstance(announcement_list[0], DiscussionTopic) + self.assertEqual(len(announcement_list), 2) From 9a6c693707b4f9763ea2494224b05aad890faf53 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 3 Apr 2018 16:07:08 -0400 Subject: [PATCH 52/91] Added get_announcements method --- canvasapi/canvas.py | 21 +++++++++++++++++++++ tests/fixtures/announcements.json | 28 ++++++++++++++++++++++++++++ tests/test_canvas.py | 11 +++++++++++ 3 files changed, 60 insertions(+) create mode 100755 tests/fixtures/announcements.json diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 285afb1e..95c37042 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -365,8 +365,10 @@ def clear_course_nicknames(self): `_ :returns: True if the nicknames were cleared, False otherwise. + :rtype: bool """ + response = self.__requester.request( 'DELETE', 'users/self/course_nicknames' @@ -1133,3 +1135,22 @@ def get_progress(self, progress, **kwargs): _kwargs=combine_kwargs(**kwargs) ) return Progress(self.__requester, response.json()) + + def get_announcements(self, **kwargs): + """ + List announcements. + + :calls: `GET /api/v1/announcements \ + `_ + + :rtype: :class:`canvasapi.paginated_list.PaginatedList` of + :class:`canvasapi.discussion_topic.DiscussionTopic` + """ + from canvasapi.discussion_topic import DiscussionTopic + return PaginatedList( + DiscussionTopic, + self.__requester, + 'GET', + 'announcements', + _kwargs=combine_kwargs(**kwargs) + ) diff --git a/tests/fixtures/announcements.json b/tests/fixtures/announcements.json new file mode 100755 index 00000000..b6370504 --- /dev/null +++ b/tests/fixtures/announcements.json @@ -0,0 +1,28 @@ +{ + "list_announcements": { + "method": "GET", + "endpoint": "announcements", + "data": [{ + "id": 1, + "title": "Announcment #1", + "author": { + "id": 1, + "display_name": "John Doe" + }, + "message": "Announcement Message #1", + "subscription_hold": "topic_is_announcement" + }, + { + "id": 2, + "title": "Announcement #2", + "author": { + "id": 2, + "display_name": "John Smith" + }, + "message": "Announcement Message #2", + "subscription_hold": "topic_is_announcement" + } + ], + "status_code": 200 + } +} diff --git a/tests/test_canvas.py b/tests/test_canvas.py index 73e0f297..b9b92da5 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -22,6 +22,8 @@ from canvasapi.progress import Progress from canvasapi.section import Section from canvasapi.user import User +from canvasapi.paginated_list import PaginatedList +from canvasapi.discussion_topic import DiscussionTopic from tests import settings from tests.util import register_uris @@ -736,3 +738,12 @@ def test_get_progress(self, m): self.assertIsInstance(progress, Progress) self.assertTrue(hasattr(progress, 'id')) self.assertEqual(progress.id, 1) + + # get_announcements() + def test_get_announcements(self, m): + register_uris({'announcements': ['list_announcements']},m) + announcements = self.canvas.get_announcements() + announcement_list = [announcement for announcement in announcements] + self.assertIsInstance(announcements, PaginatedList) + self.assertIsInstance(announcement_list[0], DiscussionTopic) + self.assertEqual(len(announcement_list), 2) From ad7903a772cb10362ac25733a3ff69e7ec2b1fe0 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 3 Apr 2018 16:24:03 -0400 Subject: [PATCH 53/91] Resolved flake8 error --- tests/test_canvas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_canvas.py b/tests/test_canvas.py index b9b92da5..a1a4058b 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -741,7 +741,7 @@ def test_get_progress(self, m): # get_announcements() def test_get_announcements(self, m): - register_uris({'announcements': ['list_announcements']},m) + register_uris({'announcements': ['list_announcements']}, m) announcements = self.canvas.get_announcements() announcement_list = [announcement for announcement in announcements] self.assertIsInstance(announcements, PaginatedList) From 51069d39bc02a20ed0b79ee86edd2a60623d6528 Mon Sep 17 00:00:00 2001 From: Jesse McBride Date: Tue, 10 Apr 2018 15:41:39 -0400 Subject: [PATCH 54/91] Reorder imports to be alphabetical --- tests/test_canvas.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_canvas.py b/tests/test_canvas.py index a1a4058b..058bb466 100644 --- a/tests/test_canvas.py +++ b/tests/test_canvas.py @@ -14,16 +14,16 @@ from canvasapi.calendar_event import CalendarEvent from canvasapi.conversation import Conversation from canvasapi.course import Course, CourseNickname +from canvasapi.discussion_topic import DiscussionTopic from canvasapi.exceptions import RequiredFieldMissing from canvasapi.file import File from canvasapi.group import Group, GroupCategory from canvasapi.exceptions import ResourceDoesNotExist from canvasapi.outcome import Outcome, OutcomeGroup +from canvasapi.paginated_list import PaginatedList from canvasapi.progress import Progress from canvasapi.section import Section from canvasapi.user import User -from canvasapi.paginated_list import PaginatedList -from canvasapi.discussion_topic import DiscussionTopic from tests import settings from tests.util import register_uris From 4f31a7db3167be2c5c0716317c5da588ec1d2c69 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Mon, 30 Apr 2018 15:32:24 -0400 Subject: [PATCH 55/91] wrote unit test for endpoint tester --- tests/test_endpoint_docstrings.py | 6 ++ tests/test_test_endpoint_docstrings.py | 79 ++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tests/test_endpoint_docstrings.py create mode 100644 tests/test_test_endpoint_docstrings.py diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py new file mode 100644 index 00000000..66723d79 --- /dev/null +++ b/tests/test_endpoint_docstrings.py @@ -0,0 +1,6 @@ +def test_methods(*testMethods): + #For each: + #check if method contains an API call + #check if API call matches live documentation + #check if Mocker recieves a requst matching above two + return False diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py new file mode 100644 index 00000000..6576038b --- /dev/null +++ b/tests/test_test_endpoint_docstrings.py @@ -0,0 +1,79 @@ +import unittest +import requests_mock +from canvasapi.canvas_object import CanvasObject + +from tests.test_endpoint_docstrings import test_methods + +class TestTestEndpointDocstrings(unittest.TestCase): + @requests_mock.Mocker() + def test_test_methods(self, m): + assert not test_methods(ExampleMethods.example_method_should_fail_online_documentation) + assert not test_methods(ExampleMethods.example_method_should_fail_implementation_verb) + assert not test_methods(ExampleMethods.example_method_should_fail_implementation_URL) + assert test_methods(ExampleMethods.example_method_should_pass_no_api_call) + assert test_methods(ExampleMethods.example_method_should_pass_all_correct) + +class ExampleMethods(CanvasObject): + def example_method_should_fail_online_documentation(self): + """ + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def example_method_should_fail_implementation_verb(self): + """ + Delete this file. + + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'POST', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def example_method_should_fail_implementation_URL(self): + """ + Delete this file. + + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'fils/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def example_method_should_pass_no_api_call(self): + """ + Empty docstring. + """ + return False + + def example_method_should_pass_all_correct(self): + """ + Delete this file. + + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) From d5fae1cb4a4fff80c70b85c489bee0808abc2967 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Mon, 30 Apr 2018 15:34:17 -0400 Subject: [PATCH 56/91] removed unesed Mocker from unit test --- tests/test_test_endpoint_docstrings.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index 6576038b..35a3698e 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -5,8 +5,7 @@ from tests.test_endpoint_docstrings import test_methods class TestTestEndpointDocstrings(unittest.TestCase): - @requests_mock.Mocker() - def test_test_methods(self, m): + def test_test_methods(self): assert not test_methods(ExampleMethods.example_method_should_fail_online_documentation) assert not test_methods(ExampleMethods.example_method_should_fail_implementation_verb) assert not test_methods(ExampleMethods.example_method_should_fail_implementation_URL) From 9b21728abd97d67a8ec050ffaf77942c2ac98d6b Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Mon, 30 Apr 2018 17:32:44 -0400 Subject: [PATCH 57/91] Added preliminary code for test_methods --- tests/test_endpoint_docstrings.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 66723d79..b1df09d7 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,6 +1,23 @@ +import inspect +import re def test_methods(*testMethods): - #For each: - #check if method contains an API call - #check if API call matches live documentation - #check if Mocker recieves a requst matching above two + failures = 0 + for testMethod in testMethods: + # Check if docstring contains a calls line + if re.search(":calls:", inspect.getdoc(testMethod)): + callLine = re.search(":calls:[^_]*_", inspect.getdoc(testMethod)).group() + callGroups = re.search("(POST |GET |PUT |PATCH |DELETE )[^/]*(/[^ ]*)[^<]*<([^>]*)>",callLine).groups() + if checkUsage(callGroups) or checkAPIDoc(callGroups) + failures += 1 + + #TODO change to error codes based on results + return False + +#TODO implement +def checkAPIDoc(groups): + verb, apiURL, docURL = groups + return False + +#TODO implement +def checkUsage(groups): return False From cced6d78f5d644dc5b6f22d9074b3c52925b3532 Mon Sep 17 00:00:00 2001 From: Tobiaqs Date: Tue, 1 May 2018 17:43:58 +0200 Subject: [PATCH 58/91] Fix typo (submision => submission) --- canvasapi/assignment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py index 3dcec1d7..bbc4bdac 100644 --- a/canvasapi/assignment.py +++ b/canvasapi/assignment.py @@ -128,7 +128,7 @@ def submit(self, submission, **kwargs): :rtype: :class:`canvasapi.submission.Submission` """ if isinstance(submission, dict) and 'submission_type' in submission: - kwargs['submision'] = submission + kwargs['submission'] = submission else: raise RequiredFieldMissing( "Dictionary with key 'submission_type' is required." From 7b325d475d7cba524b2b8a894d0e05c36130c949 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 1 May 2018 14:13:03 -0400 Subject: [PATCH 59/91] test_methods passes unit test --- tests/test_endpoint_docstrings.py | 78 ++++++++++++++++++++------ tests/test_test_endpoint_docstrings.py | 15 ++--- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index b1df09d7..03dc092e 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,23 +1,65 @@ import inspect import re -def test_methods(*testMethods): - failures = 0 - for testMethod in testMethods: - # Check if docstring contains a calls line - if re.search(":calls:", inspect.getdoc(testMethod)): - callLine = re.search(":calls:[^_]*_", inspect.getdoc(testMethod)).group() - callGroups = re.search("(POST |GET |PUT |PATCH |DELETE )[^/]*(/[^ ]*)[^<]*<([^>]*)>",callLine).groups() - if checkUsage(callGroups) or checkAPIDoc(callGroups) - failures += 1 +import requests - #TODO change to error codes based on results - return False +def test_methods(testMethod, quiet=False): + # Check if docstring contains a calls line; automatic pass if not + if re.search(":calls:", inspect.getdoc(testMethod)): + callLine = re.search(":calls:[^_]*_", inspect.getdoc(testMethod)).group() + docStringVerb, apiURL, docURL = re.search("(POST|GET|PUT|PATCH|DELETE)[^/]*(/[^ ]*)[^<]*<([^>]*)>",callLine).groups() + fileURL, endpointName = re.search("([^#]*)#(.*)",docURL).groups() + docResponse = requests.get(fileURL) + if docResponse.status_code != requests.codes.ok: + if not quiet: + print "%s Docstring URL request returned %d" % (testMethod.__name__, docResponse.status_code) + return False -#TODO implement -def checkAPIDoc(groups): - verb, apiURL, docURL = groups - return False + endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) + if not endpointName: -#TODO implement -def checkUsage(groups): - return False + if not quiet: + print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ + % testMethod.__name__ + return False + if not endpointHeading: + if not quiet: + print "%s Docstring refers to %s in %s, not found" % (testMethod.__name__, endpointName, fileURL) + return False + + endpointRegex = re.compile('

[\S\s]*<\/h3>') + + endpointElement = endpointRegex.search(docResponse.text, endpointHeading.end()) + if not endpointElement: + if not quiet: + print "Found no endpoint after %s in %s" % (endpointName, fileURL) + return False + docVerb, docEndpointURL = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", endpointElement.group()).groups() + if docVerb != docStringVerb: + if not quiet: + print "%s Docstring verb is %s, corresponding API documentation is %s" \ + % (testMethod.__name__, docStringVerb, docVerb) + return False + if docEndpointURL != apiURL: + + if not quiet: + print "%s Docstring API URL is %s, corresponding API documentation is %s" \ + % (testMethod.__name__, apiURL, docEndpointURL) + return False + + source = inspect.getsource(testMethod) + implementation = re.search("request\([\s\S]*[\'\"](POST|GET|PUT|PATCH|DELETE)[\'\"],[^\'\"]*[\'\"]([^\'\"]*)[\'\"]",source) + if not implementation: + if not quiet: + print s + print "%s Docstring refers to %s %s but implementation was not found." % (testMethod.__name__, docStringVerb, apiURL) + return False + if implementation.group(1) != docVerb: + if not quiet: + print "%s Docstring verb is %s but implementation is %s" % (testMethod.__name__, docStringVerb, implementation.group(1)) + return False + apiShortURL = re.sub("\/api\/v[0-9]*\/","",apiURL) + if implementation.group(2) != re.sub(":[^\/]*","{}",apiShortURL): + if not quiet: + print "%s Docstring refers to %s URL but implementation is %s" %(testMethod.__name__, apiShortURL, implementation.group(2)) + return False + return True diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index 35a3698e..81e0b97a 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -6,17 +6,18 @@ class TestTestEndpointDocstrings(unittest.TestCase): def test_test_methods(self): - assert not test_methods(ExampleMethods.example_method_should_fail_online_documentation) - assert not test_methods(ExampleMethods.example_method_should_fail_implementation_verb) - assert not test_methods(ExampleMethods.example_method_should_fail_implementation_URL) - assert test_methods(ExampleMethods.example_method_should_pass_no_api_call) - assert test_methods(ExampleMethods.example_method_should_pass_all_correct) + assert not test_methods(ExampleMethods.example_method_should_fail_online_documentation, True) + assert not test_methods(ExampleMethods.example_method_should_fail_implementation_verb, True) + assert not test_methods(ExampleMethods.example_method_should_fail_implementation_URL, True) + assert test_methods(ExampleMethods.example_method_should_pass_no_api_call, True) + assert test_methods(ExampleMethods.example_method_should_pass_all_correct, True) +#TODO REPLACE file:// with live documentation once local debugging is complete class ExampleMethods(CanvasObject): def example_method_should_fail_online_documentation(self): """ - :calls: `DELETE /api/v1/files/:id \ - `_ + :calls: `PUT /api/v1/files/:id \ + `_ :rtype: :class:`canvasapi.file.File` """ From cb87b542cc4033b73c7c0b94119d7e3848b29ebb Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 1 May 2018 17:08:21 -0400 Subject: [PATCH 60/91] tests all canvasapi methods, many false positives --- tests/test_endpoint_docstrings.py | 47 ++++++++++++++++++-------- tests/test_test_endpoint_docstrings.py | 16 ++++----- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 03dc092e..85d9e6e1 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,17 +1,24 @@ import inspect import re import requests +import os +import sys -def test_methods(testMethod, quiet=False): +def test_method(testMethod, quiet=False): # Check if docstring contains a calls line; automatic pass if not + if not inspect.getdoc(testMethod): + return True if re.search(":calls:", inspect.getdoc(testMethod)): - callLine = re.search(":calls:[^_]*_", inspect.getdoc(testMethod)).group() - docStringVerb, apiURL, docURL = re.search("(POST|GET|PUT|PATCH|DELETE)[^/]*(/[^ ]*)[^<]*<([^>]*)>",callLine).groups() + callGroups = re.search("`(POST|GET|PUT|PATCH|DELETE) ([\S<]*)[\S\s]*<([^>`]*)>?`_",inspect.getdoc(testMethod)) + if not callGroups: + print "%s Syntax error in :calls: line: %s" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, callLine) + return False + docStringVerb, apiURL, docURL = callGroups.groups() fileURL, endpointName = re.search("([^#]*)#(.*)",docURL).groups() docResponse = requests.get(fileURL) if docResponse.status_code != requests.codes.ok: if not quiet: - print "%s Docstring URL request returned %d" % (testMethod.__name__, docResponse.status_code) + print "%s Docstring URL request returned %d" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docResponse.status_code) return False endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) @@ -19,15 +26,16 @@ def test_methods(testMethod, quiet=False): if not quiet: print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ - % testMethod.__name__ + % inspect.getfile(testMethod) +" "+ testMethod.__name__ return False if not endpointHeading: if not quiet: - print "%s Docstring refers to %s in %s, not found" % (testMethod.__name__, endpointName, fileURL) + print "%s Docstring refers to %s in %s, not found" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, endpointName, fileURL) return False endpointRegex = re.compile('

[\S\s]*<\/h3>') - + endpointStart = endpointHeading.end() + endpointEnd = endpointElement = endpointRegex.search(docResponse.text, endpointHeading.end()) if not endpointElement: if not quiet: @@ -37,29 +45,40 @@ def test_methods(testMethod, quiet=False): if docVerb != docStringVerb: if not quiet: print "%s Docstring verb is %s, corresponding API documentation is %s" \ - % (testMethod.__name__, docStringVerb, docVerb) + % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, docVerb) return False if docEndpointURL != apiURL: if not quiet: print "%s Docstring API URL is %s, corresponding API documentation is %s" \ - % (testMethod.__name__, apiURL, docEndpointURL) + % (inspect.getfile(testMethod) +" "+ testMethod.__name__, apiURL, docEndpointURL) return False - + return True source = inspect.getsource(testMethod) implementation = re.search("request\([\s\S]*[\'\"](POST|GET|PUT|PATCH|DELETE)[\'\"],[^\'\"]*[\'\"]([^\'\"]*)[\'\"]",source) if not implementation: if not quiet: - print s - print "%s Docstring refers to %s %s but implementation was not found." % (testMethod.__name__, docStringVerb, apiURL) + print "%s Docstring refers to %s %s but implementation was not found." % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, apiURL) return False if implementation.group(1) != docVerb: if not quiet: - print "%s Docstring verb is %s but implementation is %s" % (testMethod.__name__, docStringVerb, implementation.group(1)) + print "%s Docstring verb is %s but implementation is %s" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, implementation.group(1)) return False apiShortURL = re.sub("\/api\/v[0-9]*\/","",apiURL) if implementation.group(2) != re.sub(":[^\/]*","{}",apiShortURL): if not quiet: - print "%s Docstring refers to %s URL but implementation is %s" %(testMethod.__name__, apiShortURL, implementation.group(2)) + print "%s Docstring refers to %s URL but implementation is %s" %(inspect.getfile(testMethod) +" "+ testMethod.__name__, apiShortURL, implementation.group(2)) return False return True + +def test_methods(): + path = os.path.join(os.path.dirname(os.path.dirname(__file__)),"canvasapi") + + for py in [f[:-3] for f in os.listdir(path) if f.endswith('.py') and f != '__init__.py']: + mod = __import__('canvasapi.'+py) + classes = [getattr(mod, x) for x in dir(mod) if isinstance(getattr(mod, x), type)] + for cls in classes: + setattr(sys.modules[__name__], cls.__name__, cls) + methods = inspect.getmembers(cls, inspect.ismethod) + for methodname, method in methods: + test_method(method) diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index 81e0b97a..af739aa6 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -2,17 +2,17 @@ import requests_mock from canvasapi.canvas_object import CanvasObject -from tests.test_endpoint_docstrings import test_methods +from tests.test_endpoint_docstrings import test_method, test_methods class TestTestEndpointDocstrings(unittest.TestCase): - def test_test_methods(self): - assert not test_methods(ExampleMethods.example_method_should_fail_online_documentation, True) - assert not test_methods(ExampleMethods.example_method_should_fail_implementation_verb, True) - assert not test_methods(ExampleMethods.example_method_should_fail_implementation_URL, True) - assert test_methods(ExampleMethods.example_method_should_pass_no_api_call, True) - assert test_methods(ExampleMethods.example_method_should_pass_all_correct, True) + def test_test_method(self): + assert not test_method(ExampleMethods.example_method_should_fail_online_documentation, True) + #assert not test_method(ExampleMethods.example_method_should_fail_implementation_verb, True) + #assert not test_method(ExampleMethods.example_method_should_fail_implementation_URL, True) + assert test_method(ExampleMethods.example_method_should_pass_no_api_call, True) + assert test_method(ExampleMethods.example_method_should_pass_all_correct, True) + test_methods() -#TODO REPLACE file:// with live documentation once local debugging is complete class ExampleMethods(CanvasObject): def example_method_should_fail_online_documentation(self): """ From c03943a9351cb41863f2b1c373d22d94536c00fc Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 15:32:30 -0400 Subject: [PATCH 61/91] tests all canvasapi methods correctly --- tests/test_endpoint_docstrings.py | 135 +++++++++++++------------ tests/test_test_endpoint_docstrings.py | 83 ++++++++++----- 2 files changed, 123 insertions(+), 95 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 85d9e6e1..55ff191f 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,84 +1,85 @@ import inspect import re import requests -import os -import sys +import canvasapi def test_method(testMethod, quiet=False): # Check if docstring contains a calls line; automatic pass if not if not inspect.getdoc(testMethod): return True - if re.search(":calls:", inspect.getdoc(testMethod)): - callGroups = re.search("`(POST|GET|PUT|PATCH|DELETE) ([\S<]*)[\S\s]*<([^>`]*)>?`_",inspect.getdoc(testMethod)) - if not callGroups: - print "%s Syntax error in :calls: line: %s" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, callLine) - return False - docStringVerb, apiURL, docURL = callGroups.groups() - fileURL, endpointName = re.search("([^#]*)#(.*)",docURL).groups() - docResponse = requests.get(fileURL) - if docResponse.status_code != requests.codes.ok: - if not quiet: - print "%s Docstring URL request returned %d" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docResponse.status_code) + if not re.search(":calls:", inspect.getdoc(testMethod)): + return True + callLines = re.findall("`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(testMethod)) + if len(callLines) == 0: + if not quiet: + print "%s Syntax error in :calls: line" % (inspect.getfile(testMethod) +" "+ testMethod.__name__) + return False + for callLine in callLines: + if not test_docString(testMethod, callLine, quiet): return False + return True - endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) - if not endpointName: +def test_docString(testMethod, callLine, quiet): + docStringVerb, apiURL, docURL = callLine + apiURL = ''.join(apiURL.split()) + if apiURL[-1] == '/': + apiURL = apiURL[0:-1] + fileURL, endpointName = re.search("([^#]*)#(.*)",docURL).groups() + docResponse = requests.get(fileURL) + if docResponse.status_code != requests.codes.ok: + if not quiet: + print "%s Docstring URL request returned %d" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docResponse.status_code) + return False - if not quiet: - print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ - % inspect.getfile(testMethod) +" "+ testMethod.__name__ - return False - if not endpointHeading: - if not quiet: - print "%s Docstring refers to %s in %s, not found" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, endpointName, fileURL) - return False + endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) + if not endpointName: - endpointRegex = re.compile('

[\S\s]*<\/h3>') - endpointStart = endpointHeading.end() - endpointEnd = - endpointElement = endpointRegex.search(docResponse.text, endpointHeading.end()) - if not endpointElement: - if not quiet: - print "Found no endpoint after %s in %s" % (endpointName, fileURL) - return False - docVerb, docEndpointURL = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", endpointElement.group()).groups() + if not quiet: + print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ + % inspect.getfile(testMethod) +" "+ testMethod.__name__ + return False + if not endpointHeading: + if not quiet: + print "%s Docstring refers to %s in %s, not found" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, endpointName, fileURL) + return False + + endpointRegex = re.compile('

[^<]*<\/h3>') + endpointStart = endpointRegex.search(docResponse.text, endpointHeading.end()).start() + endpointEndRegex = re.compile('<[^h\/]') + endpointEnd = endpointEndRegex.search(docResponse.text, endpointStart) + if not endpointEnd: + endpointEndPos = len(docReseponse.text) + else: + endpointEndPos = endpointEnd.start() + endpointElement = endpointRegex.search(docResponse.text, endpointStart, endpointEndPos) + endpointElements= [] + while endpointElement: + endpointElements.append(endpointElement.group()) + endpointElement = endpointRegex.search(docResponse.text, endpointElement.end(), endpointEndPos) + if len(endpointElements) == 0: + if not quiet: + print "Found no endpoint after %s in %s" % (endpointName, fileURL) + return False + docLines = [] + for endpointElementStr in endpointElements: + docMatch = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", endpointElementStr) + docLines.append(docMatch.group()) + docVerb, docEndpointURL = docMatch.groups() if docVerb != docStringVerb: - if not quiet: - print "%s Docstring verb is %s, corresponding API documentation is %s" \ - % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, docVerb) - return False + continue if docEndpointURL != apiURL: - - if not quiet: - print "%s Docstring API URL is %s, corresponding API documentation is %s" \ - % (inspect.getfile(testMethod) +" "+ testMethod.__name__, apiURL, docEndpointURL) - return False + continue return True - source = inspect.getsource(testMethod) - implementation = re.search("request\([\s\S]*[\'\"](POST|GET|PUT|PATCH|DELETE)[\'\"],[^\'\"]*[\'\"]([^\'\"]*)[\'\"]",source) - if not implementation: - if not quiet: - print "%s Docstring refers to %s %s but implementation was not found." % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, apiURL) - return False - if implementation.group(1) != docVerb: - if not quiet: - print "%s Docstring verb is %s but implementation is %s" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb, implementation.group(1)) - return False - apiShortURL = re.sub("\/api\/v[0-9]*\/","",apiURL) - if implementation.group(2) != re.sub(":[^\/]*","{}",apiShortURL): - if not quiet: - print "%s Docstring refers to %s URL but implementation is %s" %(inspect.getfile(testMethod) +" "+ testMethod.__name__, apiShortURL, implementation.group(2)) - return False - return True + if not quiet: + print "%s Docstring %s not found in API documentation (%s)" \ + % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb + " " + apiURL, str(docLines)) + return False def test_methods(): - path = os.path.join(os.path.dirname(os.path.dirname(__file__)),"canvasapi") - - for py in [f[:-3] for f in os.listdir(path) if f.endswith('.py') and f != '__init__.py']: - mod = __import__('canvasapi.'+py) - classes = [getattr(mod, x) for x in dir(mod) if isinstance(getattr(mod, x), type)] - for cls in classes: - setattr(sys.modules[__name__], cls.__name__, cls) - methods = inspect.getmembers(cls, inspect.ismethod) - for methodname, method in methods: - test_method(method) + methods = set() + for _, module in inspect.getmembers(canvasapi, inspect.ismodule): + for _, theclass in inspect.getmembers(module, inspect.isclass): + for _, method in inspect.getmembers(theclass, inspect.ismethod): + methods.add(method) + for methodToTest in methods: + test_method(methodToTest) diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index af739aa6..a1a08540 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -1,20 +1,20 @@ import unittest -import requests_mock from canvasapi.canvas_object import CanvasObject - from tests.test_endpoint_docstrings import test_method, test_methods +#test_endpoint_docstrings class TestTestEndpointDocstrings(unittest.TestCase): def test_test_method(self): - assert not test_method(ExampleMethods.example_method_should_fail_online_documentation, True) - #assert not test_method(ExampleMethods.example_method_should_fail_implementation_verb, True) - #assert not test_method(ExampleMethods.example_method_should_fail_implementation_URL, True) - assert test_method(ExampleMethods.example_method_should_pass_no_api_call, True) - assert test_method(ExampleMethods.example_method_should_pass_all_correct, True) - test_methods() + assert not test_method(ExampleMethods.fails_wrong_docstring_verb, True) + assert not test_method(ExampleMethods.fails_invalid_docstring_verb, True) + assert test_method(ExampleMethods.passes_no_api_call, True) + assert test_method(ExampleMethods.passes_good_docstring, True) + assert test_method(ExampleMethods.passes_multiple_endpoints, True) + assert test_method(ExampleMethods.passes_multiline_URL, True) + #test_methods() class ExampleMethods(CanvasObject): - def example_method_should_fail_online_documentation(self): + def fails_wrong_docstring_verb(self): """ :calls: `PUT /api/v1/files/:id \ `_ @@ -27,22 +27,13 @@ def example_method_should_fail_online_documentation(self): ) return ExampleMethods(self._requester, response.json()) - def example_method_should_fail_implementation_verb(self): + def passes_no_api_call(self): """ - Delete this file. - - :calls: `DELETE /api/v1/files/:id \ - `_ - - :rtype: :class:`canvasapi.file.File` + Empty docstring. """ - response = self._requester.request( - 'POST', - 'files/{}'.format(self.id) - ) - return ExampleMethods(self._requester, response.json()) + return False - def example_method_should_fail_implementation_URL(self): + def passes_good_docstring(self): """ Delete this file. @@ -53,21 +44,35 @@ def example_method_should_fail_implementation_URL(self): """ response = self._requester.request( 'DELETE', - 'fils/{}'.format(self.id) + 'files/{}'.format(self.id) ) return ExampleMethods(self._requester, response.json()) - def example_method_should_pass_no_api_call(self): + def passes_multiple_endpoints(self): """ - Empty docstring. + Return the details for a folder + + :calls: `GET /api/v1/folders/:id \ + `_ + + :param folder: The object or ID of the folder to retrieve. + :type folder: :class:`canvasapi.folder.Folder` or int + + :rtype: :class:`canvasapi.folder.Folder` """ - return False + folder_id = obj_or_id(folder, "folder", (Folder,)) - def example_method_should_pass_all_correct(self): + response = self.__requester.request( + 'GET', + 'folders/{}'.format(folder_id) + ) + return Folder(self.__requester, response.json()) + + def fails_invalid_docstring_verb(self): """ Delete this file. - :calls: `DELETE /api/v1/files/:id \ + :calls: `BELETE /api/v1/files/:id \ `_ :rtype: :class:`canvasapi.file.File` @@ -77,3 +82,25 @@ def example_method_should_pass_all_correct(self): 'files/{}'.format(self.id) ) return ExampleMethods(self._requester, response.json()) + + def passes_multiline_URL(self): + """ + Fetch all preferences for the given communication channel. + + :calls: `GET + /api/v1/users/:user_id/communication_channels/:communication_channel_id/ \ + notification_preferences \ + `_ + + :rtype: `list` + """ + response = self._requester.request( + 'GET', + 'users/{}/communication_channels/{}/notification_preferences'.format( + self.user_id, + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + + return response.json()['notification_preferences'] From bcb95f699a24c2ec0c72f5a50926a28b34438cb3 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 15:44:20 -0400 Subject: [PATCH 62/91] resolved flake8 errors --- tests/test_test_endpoint_docstrings.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index a1a08540..781c91ef 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -1,8 +1,12 @@ import unittest from canvasapi.canvas_object import CanvasObject -from tests.test_endpoint_docstrings import test_method, test_methods +from canvasapi.folder import Folder +from canvasapi.util import combine_kwargs, obj_or_id +from tests.test_endpoint_docstrings import test_method +# from tests.test_endpoint_docstrings import test_methods -#test_endpoint_docstrings + +# test_endpoint_docstrings class TestTestEndpointDocstrings(unittest.TestCase): def test_test_method(self): assert not test_method(ExampleMethods.fails_wrong_docstring_verb, True) @@ -11,7 +15,8 @@ def test_test_method(self): assert test_method(ExampleMethods.passes_good_docstring, True) assert test_method(ExampleMethods.passes_multiple_endpoints, True) assert test_method(ExampleMethods.passes_multiline_URL, True) - #test_methods() + # test_methods() + class ExampleMethods(CanvasObject): def fails_wrong_docstring_verb(self): @@ -48,7 +53,7 @@ def passes_good_docstring(self): ) return ExampleMethods(self._requester, response.json()) - def passes_multiple_endpoints(self): + def passes_multiple_endpoints(self, folder): """ Return the details for a folder @@ -83,7 +88,7 @@ def fails_invalid_docstring_verb(self): ) return ExampleMethods(self._requester, response.json()) - def passes_multiline_URL(self): + def passes_multiline_URL(self, **kwargs): """ Fetch all preferences for the given communication channel. From 5f5b6366bc8875f30b0670fd1e3d40d809dcb44b Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 16:12:09 -0400 Subject: [PATCH 63/91] downgraded "syntax error" to "failed to parse", tests pass if no URL --- tests/test_endpoint_docstrings.py | 29 +++++++++++++++++--------- tests/test_test_endpoint_docstrings.py | 17 +++++++++++++-- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 55ff191f..39eec9c8 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,25 +1,34 @@ +import canvasapi import inspect import re import requests -import canvasapi + def test_method(testMethod, quiet=False): - # Check if docstring contains a calls line; automatic pass if not + # No docstring means no erroneous docstrings if not inspect.getdoc(testMethod): return True + + # Docstrings without API calls can't be checked this way if not re.search(":calls:", inspect.getdoc(testMethod)): return True + if not re.search("<\S*>", inspect.getdoc(testMethod)): + return True + + method_string = inspect.getfile(testMethod) +" "+ testMethod.__name__ callLines = re.findall("`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(testMethod)) if len(callLines) == 0: if not quiet: - print "%s Syntax error in :calls: line" % (inspect.getfile(testMethod) +" "+ testMethod.__name__) + # Docstring exists, has a :calls: line, contains a URL, but could + # not be parsed; + print "%s Failed to parse :calls: line." % (method_string) return False for callLine in callLines: - if not test_docString(testMethod, callLine, quiet): + if not test_docString(method_string, callLine, quiet): return False return True -def test_docString(testMethod, callLine, quiet): +def test_docString(method_string, callLine, quiet): docStringVerb, apiURL, docURL = callLine apiURL = ''.join(apiURL.split()) if apiURL[-1] == '/': @@ -28,7 +37,7 @@ def test_docString(testMethod, callLine, quiet): docResponse = requests.get(fileURL) if docResponse.status_code != requests.codes.ok: if not quiet: - print "%s Docstring URL request returned %d" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docResponse.status_code) + print "%s Docstring URL request returned %d" % (method_string, docResponse.status_code) return False endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) @@ -36,11 +45,11 @@ def test_docString(testMethod, callLine, quiet): if not quiet: print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ - % inspect.getfile(testMethod) +" "+ testMethod.__name__ + % method_string return False if not endpointHeading: if not quiet: - print "%s Docstring refers to %s in %s, not found" % (inspect.getfile(testMethod) +" "+ testMethod.__name__, endpointName, fileURL) + print "%s Docstring refers to %s in %s, not found" % (method_string, endpointName, fileURL) return False endpointRegex = re.compile('

[^<]*<\/h3>') @@ -71,8 +80,8 @@ def test_docString(testMethod, callLine, quiet): continue return True if not quiet: - print "%s Docstring %s not found in API documentation (%s)" \ - % (inspect.getfile(testMethod) +" "+ testMethod.__name__, docStringVerb + " " + apiURL, str(docLines)) + print "%s Docstring %s not found in %s (found %s)" \ + % (method_string, docStringVerb + " " + apiURL, fileURL, str(docLines)) return False def test_methods(): diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index 781c91ef..84e9433c 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -3,7 +3,7 @@ from canvasapi.folder import Folder from canvasapi.util import combine_kwargs, obj_or_id from tests.test_endpoint_docstrings import test_method -# from tests.test_endpoint_docstrings import test_methods +from tests.test_endpoint_docstrings import test_methods # test_endpoint_docstrings @@ -15,7 +15,8 @@ def test_test_method(self): assert test_method(ExampleMethods.passes_good_docstring, True) assert test_method(ExampleMethods.passes_multiple_endpoints, True) assert test_method(ExampleMethods.passes_multiline_URL, True) - # test_methods() + assert test_method(ExampleMethods.passes_calls_but_not_api, True) + test_methods() class ExampleMethods(CanvasObject): @@ -109,3 +110,15 @@ def passes_multiline_URL(self, **kwargs): ) return response.json()['notification_preferences'] + + def passes_calls_but_not_api(): + """ + Kick off uploading process. Handles open/closing file if a path + is passed. + + :calls: request_upload_token + :returns: True if the file uploaded successfully, False \ + otherwise, and the JSON response from the API. + :rtype: tuple + """ + pass From 0cd5e81b9a2e95afe4684217a5bd1857f69facda Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 16:12:43 -0400 Subject: [PATCH 64/91] downgraded "syntax error" to "failed to parse", test pass if no URL --- tests/test_test_endpoint_docstrings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_test_endpoint_docstrings.py b/tests/test_test_endpoint_docstrings.py index 84e9433c..8b7acb18 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/tests/test_test_endpoint_docstrings.py @@ -3,7 +3,7 @@ from canvasapi.folder import Folder from canvasapi.util import combine_kwargs, obj_or_id from tests.test_endpoint_docstrings import test_method -from tests.test_endpoint_docstrings import test_methods +# from tests.test_endpoint_docstrings import test_methods # test_endpoint_docstrings @@ -16,7 +16,7 @@ def test_test_method(self): assert test_method(ExampleMethods.passes_multiple_endpoints, True) assert test_method(ExampleMethods.passes_multiline_URL, True) assert test_method(ExampleMethods.passes_calls_but_not_api, True) - test_methods() + # test_methods() class ExampleMethods(CanvasObject): From c1b00213dd782c6773ffc36342569327c9e0582d Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 16:33:45 -0400 Subject: [PATCH 65/91] replaced camelCase with snake_case --- tests/test_endpoint_docstrings.py | 77 +++++++++++++++---------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 39eec9c8..5495ae8b 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -16,72 +16,71 @@ def test_method(testMethod, quiet=False): return True method_string = inspect.getfile(testMethod) +" "+ testMethod.__name__ - callLines = re.findall("`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(testMethod)) - if len(callLines) == 0: + call_lines = re.findall("`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(testMethod)) + if len(call_lines) == 0: if not quiet: # Docstring exists, has a :calls: line, contains a URL, but could # not be parsed; print "%s Failed to parse :calls: line." % (method_string) return False - for callLine in callLines: - if not test_docString(method_string, callLine, quiet): + for call_line in call_lines: + if not test_docString(method_string, call_line, quiet): return False return True -def test_docString(method_string, callLine, quiet): - docStringVerb, apiURL, docURL = callLine - apiURL = ''.join(apiURL.split()) - if apiURL[-1] == '/': - apiURL = apiURL[0:-1] - fileURL, endpointName = re.search("([^#]*)#(.*)",docURL).groups() - docResponse = requests.get(fileURL) - if docResponse.status_code != requests.codes.ok: +def test_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 = api_URL[0:-1] + docfile_URL, endpointName = 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 "%s Docstring URL request returned %d" % (method_string, docResponse.status_code) + print "%s Docstring URL request returned %d" % (method_string, html_doc_response.status_code) return False - endpointHeading = re.search("name=[\'\"]%s[\'\"]" % endpointName, docResponse.text) + endpoint_h2 = re.search("name=[\'\"]%s[\'\"]" % endpointName, html_doc_response.text) if not endpointName: - if not quiet: print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ % method_string return False - if not endpointHeading: + if not endpoint_h2: if not quiet: - print "%s Docstring refers to %s in %s, not found" % (method_string, endpointName, fileURL) + print "%s Docstring refers to %s in %s, not found" % (method_string, endpointName, docfile_URL) return False - endpointRegex = re.compile('

[^<]*<\/h3>') - endpointStart = endpointRegex.search(docResponse.text, endpointHeading.end()).start() - endpointEndRegex = re.compile('<[^h\/]') - endpointEnd = endpointEndRegex.search(docResponse.text, endpointStart) - if not endpointEnd: - endpointEndPos = len(docReseponse.text) + endpoint_element_re = re.compile('

[^<]*<\/h3>') + endpoint_search_start_pos = endpoint_element_re.search(html_doc_response.text, endpoint_h2.end()).start() + after_endpoint_re = re.compile('<[^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: - endpointEndPos = endpointEnd.start() - endpointElement = endpointRegex.search(docResponse.text, endpointStart, endpointEndPos) - endpointElements= [] - while endpointElement: - endpointElements.append(endpointElement.group()) - endpointElement = endpointRegex.search(docResponse.text, endpointElement.end(), endpointEndPos) - if len(endpointElements) == 0: + 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) + endpoint_element_list = [] + while endpoint_element_match: + endpoint_element_list.append(endpoint_element_match.group()) + endpoint_element_match = endpoint_element_re.search(html_doc_response.text, endpoint_element_match.end(), endpoint_search_stop_pos) + if len(endpoint_element_list) == 0: if not quiet: - print "Found no endpoint after %s in %s" % (endpointName, fileURL) + print "Found no endpoint after %s in %s" % (endpointName, docfile_URL) return False - docLines = [] - for endpointElementStr in endpointElements: - docMatch = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", endpointElementStr) - docLines.append(docMatch.group()) - docVerb, docEndpointURL = docMatch.groups() - if docVerb != docStringVerb: + docfile_lines = [] + for endpoint_element_str in endpoint_element_list: + docfile_match = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", endpoint_element_str) + docfile_lines.append(docfile_match.group()) + docfile_verb, docfile_API_URL = docfile_match.groups() + if docfile_verb != docstring_verb: continue - if docEndpointURL != apiURL: + if docfile_API_URL != api_URL: continue return True if not quiet: print "%s Docstring %s not found in %s (found %s)" \ - % (method_string, docStringVerb + " " + apiURL, fileURL, str(docLines)) + % (method_string, docstring_verb + " " + api_URL, docfile_URL, str(docfile_lines)) return False def test_methods(): From dbfd65aac1acfdb835cc16e518d3ae82b24f8c90 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 3 May 2018 16:37:45 -0400 Subject: [PATCH 66/91] Stopped capitalizing docstring (to match contributor guide) --- tests/test_endpoint_docstrings.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index 5495ae8b..fee62b90 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -37,18 +37,18 @@ def test_docString(method_string, call_line, quiet): html_doc_response = requests.get(docfile_URL) if html_doc_response.status_code != requests.codes.ok: if not quiet: - print "%s Docstring URL request returned %d" % (method_string, html_doc_response.status_code) + print "%s docstring URL request returned %d" % (method_string, html_doc_response.status_code) return False endpoint_h2 = re.search("name=[\'\"]%s[\'\"]" % endpointName, html_doc_response.text) if not endpointName: if not quiet: - print "%s Docstring URL does not contain an endpoint name in link to API documentation" \ + print "%s docstring URL does not contain an endpoint name in link to API documentation" \ % method_string return False if not endpoint_h2: if not quiet: - print "%s Docstring refers to %s in %s, not found" % (method_string, endpointName, docfile_URL) + print "%s docstring refers to %s in %s, not found" % (method_string, endpointName, docfile_URL) return False endpoint_element_re = re.compile('

[^<]*<\/h3>') @@ -79,7 +79,7 @@ def test_docString(method_string, call_line, quiet): continue return True if not quiet: - print "%s Docstring %s not found in %s (found %s)" \ + print "%s docstring %s not found in %s (found %s)" \ % (method_string, docstring_verb + " " + api_URL, docfile_URL, str(docfile_lines)) return False From e8ce9cee55ba89d75f9a2df4fcac16de6da1fa54 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Fri, 4 May 2018 15:21:28 -0400 Subject: [PATCH 67/91] resolved flake8 errors --- tests/test_endpoint_docstrings.py | 78 ++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/tests/test_endpoint_docstrings.py b/tests/test_endpoint_docstrings.py index fee62b90..6a7cf2e4 100644 --- a/tests/test_endpoint_docstrings.py +++ b/tests/test_endpoint_docstrings.py @@ -1,76 +1,96 @@ -import canvasapi import inspect import re import requests +import canvasapi + -def test_method(testMethod, quiet=False): +def test_method(themethod, quiet=False): # No docstring means no erroneous docstrings - if not inspect.getdoc(testMethod): + if not inspect.getdoc(themethod): return True # Docstrings without API calls can't be checked this way - if not re.search(":calls:", inspect.getdoc(testMethod)): + if not re.search(":calls:", inspect.getdoc(themethod)): return True - if not re.search("<\S*>", inspect.getdoc(testMethod)): + if not re.search("<\S*>", inspect.getdoc(themethod)): return True - method_string = inspect.getfile(testMethod) +" "+ testMethod.__name__ - call_lines = re.findall("`(POST|GET|PUT|PATCH|DELETE)([^<]*)<([^>]*)>`_", inspect.getdoc(testMethod)) - if len(call_lines) == 0: + method_string = inspect.getfile(themethod) + " " + themethod.__name__ + call_lines = re.findall("`(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 "%s Failed to parse :calls: line." % (method_string) return False for call_line in call_lines: - if not test_docString(method_string, call_line, quiet): + if not test_docstring(method_string, call_line, quiet): return False return True -def test_docString(method_string, call_line, quiet): + +def test_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 = api_URL[0:-1] - docfile_URL, endpointName = 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 "%s docstring URL request returned %d" % (method_string, html_doc_response.status_code) + print "%s docstring URL request returned %d" % \ + (method_string, html_doc_response.status_code) return False - endpoint_h2 = re.search("name=[\'\"]%s[\'\"]" % endpointName, html_doc_response.text) - if not endpointName: + endpoint_h2 = re.search("name=[\'\"]%s[\'\"]" % endpoint_name, + html_doc_response.text) + if not endpoint_name: if not quiet: - print "%s docstring URL does not contain an endpoint name in link to API documentation" \ - % method_string + print "%s docstring URL does not contain an endpoint name in link \ + to API documentation" % method_string return False if not endpoint_h2: if not quiet: - print "%s docstring refers to %s in %s, not found" % (method_string, endpointName, docfile_URL) + print "%s docstring refers to %s in %s, not found" \ + % (method_string, endpoint_name, docfile_URL) return False - endpoint_element_re = re.compile('

[^<]*<\/h3>') - endpoint_search_start_pos = endpoint_element_re.search(html_doc_response.text, endpoint_h2.end()).start() + endpoint_element_re = \ + re.compile('

[^<]*<\/h3>') + endpoint_search_start_pos = \ + endpoint_element_re.search(html_doc_response.text, + endpoint_h2.end()).start() + after_endpoint_re = re.compile('<[^h\/]') - endpoint_search_end = after_endpoint_re.search(html_doc_response.text, endpoint_search_start_pos) + 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) + endpoint_element_match = \ + endpoint_element_re.search(html_doc_response.text, + endpoint_search_start_pos, + endpoint_search_stop_pos) endpoint_element_list = [] while endpoint_element_match: endpoint_element_list.append(endpoint_element_match.group()) - endpoint_element_match = endpoint_element_re.search(html_doc_response.text, endpoint_element_match.end(), endpoint_search_stop_pos) - if len(endpoint_element_list) == 0: + endpoint_element_match = \ + endpoint_element_re.search(html_doc_response.text, + endpoint_element_match.end(), + endpoint_search_stop_pos) + + if not endpoint_element_list: if not quiet: - print "Found no endpoint after %s in %s" % (endpointName, docfile_URL) + print "Found no endpoint after %s in %s" % \ + (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) + docfile_match = re.search("(POST|GET|PUT|PATCH|DELETE) (.*)", + endpoint_element_str) docfile_lines.append(docfile_match.group()) docfile_verb, docfile_API_URL = docfile_match.groups() if docfile_verb != docstring_verb: @@ -80,14 +100,16 @@ def test_docString(method_string, call_line, quiet): return True if not quiet: print "%s docstring %s not found in %s (found %s)" \ - % (method_string, docstring_verb + " " + api_URL, docfile_URL, str(docfile_lines)) + % (method_string, docstring_verb + " " + api_URL, docfile_URL, + str(docfile_lines)) return False + def test_methods(): methods = set() for _, module in inspect.getmembers(canvasapi, inspect.ismodule): for _, theclass in inspect.getmembers(module, inspect.isclass): for _, method in inspect.getmembers(theclass, inspect.ismethod): methods.add(method) - for methodToTest in methods: - test_method(methodToTest) + for method_to_test in methods: + test_method(method_to_test) From 8d59fcacc667b2ac5faa0730964816349d8a29ac Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Fri, 4 May 2018 17:02:29 -0400 Subject: [PATCH 68/91] corrected erroneous docstrings --- canvasapi/canvas.py | 6 +++--- canvasapi/communication_channel.py | 16 ++++++++-------- canvasapi/content_migration.py | 16 ++++++++-------- canvasapi/course.py | 6 +++--- canvasapi/discussion_topic.py | 10 +++++----- canvasapi/folder.py | 4 ++-- canvasapi/group.py | 12 ++++++------ canvasapi/page.py | 4 ++-- canvasapi/quiz.py | 2 +- canvasapi/user.py | 8 ++++---- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py index 95c37042..ca9a6e8d 100644 --- a/canvasapi/canvas.py +++ b/canvasapi/canvas.py @@ -135,7 +135,7 @@ def get_course(self, course, use_sis_id=False, **kwargs): """ Retrieve a course by its ID. - :calls: `GET /courses/:id \ + :calls: `GET /api/v1/courses/:id \ `_ :param course: The object or ID of the course to retrieve. @@ -169,7 +169,7 @@ def get_user(self, user, id_type=None): `User `_ example to see the ID types a user can be retrieved with. - :calls: `GET /users/:id \ + :calls: `GET /api/v1/users/:id \ `_ :param user: The user's object or ID. @@ -362,7 +362,7 @@ def clear_course_nicknames(self): Remove all stored course nicknames. :calls: `DELETE /api/v1/users/self/course_nicknames \ - `_ + `_ :returns: True if the nicknames were cleared, False otherwise. diff --git a/canvasapi/communication_channel.py b/canvasapi/communication_channel.py index 142308b6..78dda140 100644 --- a/canvasapi/communication_channel.py +++ b/canvasapi/communication_channel.py @@ -73,8 +73,8 @@ def list_preference_categories(self, **kwargs): instead. :calls: `GET - /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ - notification_preference_categories \ + /api/v1/users/:user_id/communication_channels/ \ + :communication_channel_id/notification_preference_categories \ `_ :rtype: `list` @@ -94,8 +94,8 @@ def get_preference_categories(self, **kwargs): channel. :calls: `GET - /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ - notification_preference_categories \ + /api/v1/users/:user_id/communication_channels/ \ + :communication_channel_id/notification_preference_categories \ `_ :rtype: `list` @@ -116,8 +116,8 @@ def get_preference(self, notification): communication channel. :calls: `GET - /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ - notification_preferences/:notification \ + /api/v1/users/:user_id/communication_channels/ \ + :communication_channel_id/notification_preferences/:notification \ `_ :param notification: The name of the notification. @@ -140,7 +140,7 @@ def update_preference(self, notification, frequency, **kwargs): Update the preference for the given notification for the given communication channel. :calls: `PUT - /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ + /api/v1/users/self/communication_channels/:communication_channel_id/ \ notification_preferences/:notification \ `_ @@ -170,7 +170,7 @@ def update_preferences_by_catagory(self, category, frequency, **kwargs): for a single communication channel. :calls: `PUT - /api/v1/users/:u_id/communication_channels/:communication_channel_id/ \ + /api/v1/users/self/communication_channels/:communication_channel_id/ \ notification_preference_categories/:category \ `_ diff --git a/canvasapi/content_migration.py b/canvasapi/content_migration.py index 9323ab7f..126d4020 100644 --- a/canvasapi/content_migration.py +++ b/canvasapi/content_migration.py @@ -56,19 +56,19 @@ def get_migration_issue(self, migration_issue, **kwargs): List a single issue for this content migration. :calls: `GET - /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id `_ or `GET - /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id `_ :param migration_issue: The object or ID of the issue to retrieve. @@ -104,19 +104,19 @@ def get_migration_issues(self, **kwargs): :calls: `GET - /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues/:id + /api/v1/accounts/:account_id/content_migrations/:content_migration_id/migration_issues `_ or `GET - /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues/:id + /api/v1/courses/:course_id/content_migrations/:content_migration_id/migration_issues `_ or `GET - /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues/:id + /api/v1/groups/:group_id/content_migrations/:content_migration_id/migration_issues `_ or `GET - /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues/:id + /api/v1/users/:user_id/content_migrations/:content_migration_id/migration_issues `_ :rtype: :class:`canvasapi.content_migration.MigrationIssue` diff --git a/canvasapi/course.py b/canvasapi/course.py index a8bb7407..725506c0 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -88,7 +88,7 @@ def get_user(self, user, user_id_type=None): several different ids that can pull the same user record from Canvas. :calls: `GET /api/v1/courses/:course_id/users/:id \ - `_ + `_ :param user: The object or ID of the user to retrieve. :type user: :class:`canvasapi.user.User` or int @@ -1612,7 +1612,7 @@ def list_files(self, **kwargs): .. deprecated:: 0.10.0 Use :func:`canvasapi.course.Course.get_files` instead. - :calls: `GET api/v1/courses/:course_id/files \ + :calls: `GET /api/v1/courses/:course_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -1630,7 +1630,7 @@ def get_files(self, **kwargs): """ Returns the paginated list of files for the course. - :calls: `GET api/v1/courses/:course_id/files \ + :calls: `GET /api/v1/courses/:course_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of diff --git a/canvasapi/discussion_topic.py b/canvasapi/discussion_topic.py index 0f2a71d5..e20a8f7e 100644 --- a/canvasapi/discussion_topic.py +++ b/canvasapi/discussion_topic.py @@ -282,10 +282,10 @@ def mark_as_unread(self): Mark the initial text of the discussion topic as unread. :calls: `DELETE /api/v1/courses/:course_id/discussion_topics/:topic_id/read \ - `_ + `_ or `DELETE /api/v1/groups/:group_id/discussion_topics/:topic_id/read \ - `_ + `_ :rtype: bool """ @@ -324,13 +324,13 @@ def mark_entries_as_read(self, **kwargs): def mark_entries_as_unread(self, **kwargs): """ - Mark the discussion topic and all its entries as read. + Mark the discussion topic and all its entries as unread. :calls: `DELETE /api/v1/courses/:course_id/discussion_topics/:topic_id/read_all \ - `_ + `_ or `DELETE /api/v1/groups/:group_id/discussion_topics/:topic_id/read_all \ - `_ + `_ :rtype: bool """ diff --git a/canvasapi/folder.py b/canvasapi/folder.py index 529f2684..cdd19d91 100644 --- a/canvasapi/folder.py +++ b/canvasapi/folder.py @@ -23,7 +23,7 @@ def list_files(self, **kwargs): .. deprecated:: 0.10.0 Use :func:`canvasapi.folder.Folder.get_files` instead. - :calls: `GET api/v1/folders/:id/files \ + :calls: `GET /api/v1/folders/:id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -41,7 +41,7 @@ def get_files(self, **kwargs): """ Returns the paginated list of files for the folder. - :calls: `GET api/v1/folders/:id/files \ + :calls: `GET /api/v1/folders/:id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of diff --git a/canvasapi/group.py b/canvasapi/group.py index d0f91587..7cb56ba9 100644 --- a/canvasapi/group.py +++ b/canvasapi/group.py @@ -230,7 +230,7 @@ def remove_user(self, user): """ Leave a group if allowed. - :calls: `DELETE /api/v1/groups/:group_id/:type/:id \ + :calls: `DELETE /api/v1/groups/:group_id/users/:user_id \ `_ :param user: The user object or ID to remove from the group. @@ -297,7 +297,7 @@ def get_activity_stream_summary(self): Return a summary of the current user's global activity stream. :calls: `GET /api/v1/groups/:group_id/activity_stream/summary \ - `_ + `_ :rtype: dict """ @@ -647,7 +647,7 @@ def list_files(self, **kwargs): .. deprecated:: 0.10.0 Use :func:`canvasapi.group.Group.get_files` instead. - :calls: `GET api/v1/groups/:group_id/files \ + :calls: `GET /api/v1/groups/:group_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -665,7 +665,7 @@ def get_files(self, **kwargs): """ Returns the paginated list of files for the group. - :calls: `GET api/v1/groups/:group_id/files \ + :calls: `GET /api/v1/groups/:group_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -932,7 +932,7 @@ def remove_user(self, user): """ Remove user from membership. - :calls: `DELETE /api/v1/groups/:group_id/:type/:id \ + :calls: `DELETE /api/v1/groups/:group_id/users/:user_id \ `_ :param user: The user object or ID to remove from the group. @@ -955,7 +955,7 @@ def remove_self(self): """ Leave a group if allowed. - :calls: `DELETE /api/v1/groups/:group_id/:type/:id \ + :calls: `DELETE /api/v1/groups/:group_id/memberships/:membership_id \ `_ :returns: An empty dictionary diff --git a/canvasapi/page.py b/canvasapi/page.py index d14dc1c5..83606145 100644 --- a/canvasapi/page.py +++ b/canvasapi/page.py @@ -85,7 +85,7 @@ def get_parent(self): :calls: `GET /api/v1/groups/:group_id \ `_ - or :calls: `GET /api/v1/courses/:course_id \ + or :calls: `GET /api/v1/courses/:id \ `_ :rtype: :class:`canvasapi.group.Group` or :class:`canvasapi.course.Course` @@ -264,7 +264,7 @@ def get_parent(self): :calls: `GET /api/v1/groups/:group_id \ `_ - or :calls: `GET /api/v1/courses/:course_id \ + or :calls: `GET /api/v1/courses/:id \ `_ :rtype: :class:`canvasapi.group.Group` or :class:`canvasapi.course.Course` diff --git a/canvasapi/quiz.py b/canvasapi/quiz.py index 73dcab62..24c32346 100644 --- a/canvasapi/quiz.py +++ b/canvasapi/quiz.py @@ -83,7 +83,7 @@ def create_question_group(self, quiz_groups, **kwargs): """ Create a new question group for the given quiz id - :calls: `POST /api/v1/courses/:course_id/quizzes/:quiz_id/groups/:id \ + :calls: `POST /api/v1/courses/:course_id/quizzes/:quiz_id/groups \ `_ :param quiz_groups: The name, pick count, question points, diff --git a/canvasapi/user.py b/canvasapi/user.py index b969a7ef..d18fdc81 100644 --- a/canvasapi/user.py +++ b/canvasapi/user.py @@ -22,7 +22,7 @@ def get_profile(self, **kwargs): """ Retrieve this user's profile. - :calls: `GET /api/v1/user/:id \ + :calls: `GET /api/v1/users/:user_id/profile \ `_ :rtype: dict @@ -388,7 +388,7 @@ def list_files(self, **kwargs): .. deprecated:: 0.10.0 Use :func:`canvasapi.user.User.get_files` instead. - :calls: `GET api/v1/courses/:user_id/files \ + :calls: `GET /api/v1/users/:user_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -406,7 +406,7 @@ def get_files(self, **kwargs): """ Returns the paginated list of files for the user. - :calls: `GET api/v1/courses/:user_id/files \ + :calls: `GET /api/v1/users/:user_id/files \ `_ :rtype: :class:`canvasapi.paginated_list.PaginatedList` of @@ -426,7 +426,7 @@ def get_file(self, file, **kwargs): """ Return the standard attachment json object for a file. - :calls: `GET /api/v1/users/:group_id/files/:id \ + :calls: `GET /api/v1/users/:user_id/files/:id \ `_ :param file: The object or ID of the file to retrieve. From 540b1c55a51ba3e86e7354b1254ab641cb7b7f52 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 8 May 2018 11:43:00 -0400 Subject: [PATCH 69/91] Moved test scripts to /scripts --- .../test_endpoint_docstrings.py | 10 ++++-- .../test_test_endpoint_docstrings.py | 33 ++++++++++--------- 2 files changed, 25 insertions(+), 18 deletions(-) rename {tests => scripts}/test_endpoint_docstrings.py (98%) rename {tests => scripts}/test_test_endpoint_docstrings.py (78%) diff --git a/tests/test_endpoint_docstrings.py b/scripts/test_endpoint_docstrings.py similarity index 98% rename from tests/test_endpoint_docstrings.py rename to scripts/test_endpoint_docstrings.py index 6a7cf2e4..31af0c95 100644 --- a/tests/test_endpoint_docstrings.py +++ b/scripts/test_endpoint_docstrings.py @@ -1,8 +1,11 @@ import inspect import re import requests +import sys -import canvasapi +sys.path.append('../') + +import canvasapi # noqa def test_method(themethod, quiet=False): @@ -100,7 +103,7 @@ def test_docstring(method_string, call_line, quiet): return True if not quiet: print "%s docstring %s not found in %s (found %s)" \ - % (method_string, docstring_verb + " " + api_URL, docfile_URL, + % (method_string, docstring_verb + " " + api_URL, doc_URL, str(docfile_lines)) return False @@ -113,3 +116,6 @@ def test_methods(): methods.add(method) for method_to_test in methods: test_method(method_to_test) + + +test_methods() diff --git a/tests/test_test_endpoint_docstrings.py b/scripts/test_test_endpoint_docstrings.py similarity index 78% rename from tests/test_test_endpoint_docstrings.py rename to scripts/test_test_endpoint_docstrings.py index 8b7acb18..82cb8f33 100644 --- a/tests/test_test_endpoint_docstrings.py +++ b/scripts/test_test_endpoint_docstrings.py @@ -1,22 +1,20 @@ -import unittest -from canvasapi.canvas_object import CanvasObject -from canvasapi.folder import Folder -from canvasapi.util import combine_kwargs, obj_or_id -from tests.test_endpoint_docstrings import test_method -# from tests.test_endpoint_docstrings import test_methods +import sys +sys.path.append('../') +from canvasapi.canvas_object import CanvasObject # noqa +from canvasapi.folder import Folder # noqa +from canvasapi.util import combine_kwargs, obj_or_id # noqa +from test_endpoint_docstrings import test_method # noqa # test_endpoint_docstrings -class TestTestEndpointDocstrings(unittest.TestCase): - def test_test_method(self): - assert not test_method(ExampleMethods.fails_wrong_docstring_verb, True) - assert not test_method(ExampleMethods.fails_invalid_docstring_verb, True) - assert test_method(ExampleMethods.passes_no_api_call, True) - assert test_method(ExampleMethods.passes_good_docstring, True) - assert test_method(ExampleMethods.passes_multiple_endpoints, True) - assert test_method(ExampleMethods.passes_multiline_URL, True) - assert test_method(ExampleMethods.passes_calls_but_not_api, True) - # test_methods() +def test_test_method(): + assert not test_method(ExampleMethods.fails_wrong_docstring_verb, True) + assert not test_method(ExampleMethods.fails_invalid_docstring_verb, True) + assert test_method(ExampleMethods.passes_no_api_call, True) + assert test_method(ExampleMethods.passes_good_docstring, True) + assert test_method(ExampleMethods.passes_multiple_endpoints, True) + assert test_method(ExampleMethods.passes_multiline_URL, True) + assert test_method(ExampleMethods.passes_calls_but_not_api, True) class ExampleMethods(CanvasObject): @@ -122,3 +120,6 @@ def passes_calls_but_not_api(): :rtype: tuple """ pass + + +test_test_method() From 3d1ffe7c90554cf344fc341946cc760921a9b51a Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 8 May 2018 15:36:13 -0400 Subject: [PATCH 70/91] broke infinite loop when slicing paginated_list past end of list --- canvasapi/paginated_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/canvasapi/paginated_list.py b/canvasapi/paginated_list.py index 56732fb1..2adcb742 100644 --- a/canvasapi/paginated_list.py +++ b/canvasapi/paginated_list.py @@ -103,7 +103,7 @@ def __iter__(self): while not self._finished(index): if self._list._is_larger_than(index): yield self._list[index] - index += self._step + index += self._step def _finished(self, index): return self._stop is not None and index >= self._stop From 9b26736e0fd3595d145bd76163eec0a5565750d2 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Wed, 9 May 2018 15:23:24 -0400 Subject: [PATCH 71/91] added tests for edge cases in slicing PaginatedLists --- tests/test_paginated_list.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_paginated_list.py b/tests/test_paginated_list.py index 43dad819..74bae5bb 100644 --- a/tests/test_paginated_list.py +++ b/tests/test_paginated_list.py @@ -192,6 +192,38 @@ def test_slice_end(self, m): self.assertTrue(hasattr(item_list[0], 'id')) self.assertEqual(item_list[0].id, '5') + def test_slice_oversize(self, m): + requires = { + 'paginated_list': ['4_2_pages_p1', '4_2_pages_p2'] + } + register_uris(requires, m) + + pag_list = PaginatedList( + User, + self.requester, + 'GET', + 'four_objects_two_pages' + ) + oversized_slice = pag_list[0:10] + item_list = [item for item in oversized_slice] + self.assertEqual(len(item_list), 4) + + def test_slice_out_of_bounds(self, m): + requires = { + 'paginated_list': ['4_2_pages_p1', '4_2_pages_p2'] + } + register_uris(requires, m) + + pag_list = PaginatedList( + User, + self.requester, + 'GET', + 'four_objects_two_pages' + ) + out_of_bounds = pag_list[4:5] + item_list = [item for item in out_of_bounds] + self.assertEqual(len(item_list), 0) + # __repr__() def test_repr(self, m): requires = { From e77e082c869a5cbef21b543fa92d01aaab8e40a5 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Wed, 9 May 2018 15:34:01 -0400 Subject: [PATCH 72/91] Tweaked slicing iterator fix to return immediately instead of repeatedly incrementing index. Added fix for slices that start past the end of the list. --- canvasapi/paginated_list.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/canvasapi/paginated_list.py b/canvasapi/paginated_list.py index 2adcb742..0e9c4094 100644 --- a/canvasapi/paginated_list.py +++ b/canvasapi/paginated_list.py @@ -102,8 +102,13 @@ def __iter__(self): index = self._start while not self._finished(index): if self._list._is_larger_than(index): - yield self._list[index] - index += self._step + try: + yield self._list[index] + except IndexError: + return + index += self._step + else: + return def _finished(self, index): return self._stop is not None and index >= self._stop From 902b5305556504329c7ea6ee41588e66ea91a76c Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 10 May 2018 14:55:17 -0400 Subject: [PATCH 73/91] presentational improvements, replace first test with validate in name --- scripts/test_endpoint_docstrings.py | 121 ---------------- ...strings.py => test_validate_docstrings.py} | 31 ++-- scripts/validate_docstrings.py | 137 ++++++++++++++++++ 3 files changed, 151 insertions(+), 138 deletions(-) delete mode 100644 scripts/test_endpoint_docstrings.py rename scripts/{test_test_endpoint_docstrings.py => test_validate_docstrings.py} (80%) create mode 100644 scripts/validate_docstrings.py diff --git a/scripts/test_endpoint_docstrings.py b/scripts/test_endpoint_docstrings.py deleted file mode 100644 index 31af0c95..00000000 --- a/scripts/test_endpoint_docstrings.py +++ /dev/null @@ -1,121 +0,0 @@ -import inspect -import re -import requests -import sys - -sys.path.append('../') - -import canvasapi # noqa - - -def test_method(themethod, quiet=False): - # No docstring means no erroneous docstrings - if not inspect.getdoc(themethod): - return True - - # Docstrings without API calls can't be checked this way - if not re.search(":calls:", inspect.getdoc(themethod)): - return True - if not re.search("<\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)) - if not call_lines: - if not quiet: - # Docstring exists, has a :calls: line, contains a URL, but could - # not be parsed; - print "%s Failed to parse :calls: line." % (method_string) - return False - for call_line in call_lines: - if not test_docstring(method_string, call_line, quiet): - return False - return True - - -def test_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 = api_URL[0:-1] - 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 "%s docstring URL request returned %d" % \ - (method_string, html_doc_response.status_code) - return False - - endpoint_h2 = re.search("name=[\'\"]%s[\'\"]" % endpoint_name, - html_doc_response.text) - if not endpoint_name: - if not quiet: - print "%s docstring URL does not contain an endpoint name in link \ - to API documentation" % method_string - return False - if not endpoint_h2: - if not quiet: - print "%s docstring refers to %s in %s, not found" \ - % (method_string, endpoint_name, docfile_URL) - return False - - endpoint_element_re = \ - re.compile('

[^<]*<\/h3>') - endpoint_search_start_pos = \ - endpoint_element_re.search(html_doc_response.text, - endpoint_h2.end()).start() - - after_endpoint_re = re.compile('<[^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) - endpoint_element_list = [] - while endpoint_element_match: - endpoint_element_list.append(endpoint_element_match.group()) - endpoint_element_match = \ - endpoint_element_re.search(html_doc_response.text, - endpoint_element_match.end(), - endpoint_search_stop_pos) - - if not endpoint_element_list: - if not quiet: - print "Found no endpoint after %s in %s" % \ - (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) - docfile_lines.append(docfile_match.group()) - docfile_verb, docfile_API_URL = docfile_match.groups() - if docfile_verb != docstring_verb: - continue - if docfile_API_URL != api_URL: - continue - return True - if not quiet: - print "%s docstring %s not found in %s (found %s)" \ - % (method_string, docstring_verb + " " + api_URL, doc_URL, - str(docfile_lines)) - return False - - -def test_methods(): - methods = set() - for _, module in inspect.getmembers(canvasapi, inspect.ismodule): - for _, theclass in inspect.getmembers(module, inspect.isclass): - for _, method in inspect.getmembers(theclass, inspect.ismethod): - methods.add(method) - for method_to_test in methods: - test_method(method_to_test) - - -test_methods() diff --git a/scripts/test_test_endpoint_docstrings.py b/scripts/test_validate_docstrings.py similarity index 80% rename from scripts/test_test_endpoint_docstrings.py rename to scripts/test_validate_docstrings.py index 82cb8f33..58e5f183 100644 --- a/scripts/test_test_endpoint_docstrings.py +++ b/scripts/test_validate_docstrings.py @@ -1,20 +1,20 @@ -import sys -sys.path.append('../') -from canvasapi.canvas_object import CanvasObject # noqa -from canvasapi.folder import Folder # noqa -from canvasapi.util import combine_kwargs, obj_or_id # noqa -from test_endpoint_docstrings import test_method # noqa +from __future__ import absolute_import, division, print_function, unicode_literals + +from canvasapi.canvas_object import CanvasObject +from canvasapi.folder import Folder +from canvasapi.util import combine_kwargs, obj_or_id +from validate_docstrings import validate_method # test_endpoint_docstrings -def test_test_method(): - assert not test_method(ExampleMethods.fails_wrong_docstring_verb, True) - assert not test_method(ExampleMethods.fails_invalid_docstring_verb, True) - assert test_method(ExampleMethods.passes_no_api_call, True) - assert test_method(ExampleMethods.passes_good_docstring, True) - assert test_method(ExampleMethods.passes_multiple_endpoints, True) - assert test_method(ExampleMethods.passes_multiline_URL, True) - assert test_method(ExampleMethods.passes_calls_but_not_api, True) +def test_validate_method(): + assert not validate_method(ExampleMethods.fails_wrong_docstring_verb, True) + assert not validate_method(ExampleMethods.fails_invalid_docstring_verb, True) + assert validate_method(ExampleMethods.passes_no_api_call, True) + assert validate_method(ExampleMethods.passes_good_docstring, True) + assert validate_method(ExampleMethods.passes_multiple_endpoints, True) + assert validate_method(ExampleMethods.passes_multiline_URL, True) + assert validate_method(ExampleMethods.passes_calls_but_not_api, True) class ExampleMethods(CanvasObject): @@ -120,6 +120,3 @@ def passes_calls_but_not_api(): :rtype: tuple """ pass - - -test_test_method() diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py new file mode 100644 index 00000000..38213bfc --- /dev/null +++ b/scripts/validate_docstrings.py @@ -0,0 +1,137 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +import inspect +import re +import requests + +import canvasapi + + +def validate_method(themethod, quiet=False): + # No docstring means no erroneous docstrings + if not inspect.getdoc(themethod): + return True + + # Docstrings without API calls can't be checked this way + if not re.search(r':calls:', inspect.getdoc(themethod)): + return True + 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) + ) + 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)) + return False + for call_line in call_lines: + if not validate_docstring(method_string, call_line, quiet): + return False + return True + + +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 = api_URL[0:-1] + 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 + )) + return False + + endpoint_h2 = re.search(r'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)) + return False + if not endpoint_h2: + if not quiet: + print('{} docstring refers to {} in {}, not found'.format( + method_string, endpoint_name, docfile_URL + )) + return False + + endpoint_element_re = re.compile( + r'

[^<]*<\/h3>' + ) + endpoint_search_start_pos = endpoint_element_re.search( + html_doc_response.text, + endpoint_h2.end() + ).start() + + 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 + ) + endpoint_element_list = [] + while endpoint_element_match: + endpoint_element_list.append(endpoint_element_match.group()) + endpoint_element_match = endpoint_element_re.search( + html_doc_response.text, + endpoint_element_match.end(), + endpoint_search_stop_pos + ) + + if not endpoint_element_list: + if not quiet: + print('Found no endpoint after %s in %s'.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 + ) + docfile_lines.append(docfile_match.group()) + docfile_verb, docfile_API_URL = docfile_match.groups() + if docfile_verb != docstring_verb: + continue + if docfile_API_URL != api_URL: + continue + return True + if not quiet: + print('{} docstring {} not found in {} (found {})'.format( + method_string, + docstring_verb + ' ' + api_URL, + doc_URL, + str(docfile_lines) + )) + return False + + +def test_methods(): + methods = set() + for _, module in inspect.getmembers(canvasapi, inspect.ismodule): + for _, theclass in inspect.getmembers(module, inspect.isclass): + for _, method in inspect.getmembers(theclass, inspect.ismethod): + methods.add(method) + for method_to_test in methods: + validate_method(method_to_test) + + +# test_methods() From 8ba4f22b456479b7f24775ecc638244a8494fec7 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Thu, 10 May 2018 16:11:50 -0400 Subject: [PATCH 74/91] made test_validate_docstrings use the unit testing framework --- scripts/__init__.py | 0 tests/test_validate_docstrings.py | 141 ++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 scripts/__init__.py create mode 100644 tests/test_validate_docstrings.py diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py new file mode 100644 index 00000000..80061570 --- /dev/null +++ b/tests/test_validate_docstrings.py @@ -0,0 +1,141 @@ +from __future__ import absolute_import, division, print_function, unicode_literals + +from canvasapi.canvas_object import CanvasObject +from canvasapi.folder import Folder +from canvasapi.util import combine_kwargs, obj_or_id +from scripts.validate_docstrings import validate_method + +import unittest + +import requests_mock +# test_endpoint_docstrings +@requests_mock.Mocker() +class TestValidateDocstrings(unittest.TestCase): + def test_validate_method_verd_mismatch(self, m): + self.assertFalse(validate_method(ExampleMethods.verb_mismatch, True)) + + def test_validate_method_invalid_verb(self, m): + self.assertFalse(validate_method(ExampleMethods.invalid_verb, True)) + + def test_validate_method_no_api_call(self, m): + self.assertTrue(validate_method(ExampleMethods.no_api_call, True)) + + def test_validate_method_good_docstring(self, m): + self.assertTrue(validate_method(ExampleMethods.good_docstring, True)) + + def test_validate_method_multiple_endpoints(self, m): + self.assertTrue(validate_method(ExampleMethods.multiple_endpoints, + True + )) + + def test_validate_method_multiline_URL(self, m): + self.assertTrue(validate_method(ExampleMethods.multiline_URL, True)) + + def test_validate_method_multiline_URL(self, m): + self.assertTrue(validate_method(ExampleMethods.non_api_call, True)) + + +class ExampleMethods(CanvasObject): + def verb_mismatch(self): + """ + :calls: `PUT /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def invalid_verb(self): + """ + Delete this file. + + :calls: `BELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def no_api_call(self): + """ + Empty docstring. + """ + return False + + def good_docstring(self): + """ + Delete this file. + + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def multiple_endpoints(self, folder): + """ + Return the details for a folder + + :calls: `GET /api/v1/folders/:id \ + `_ + + :param folder: The object or ID of the folder to retrieve. + :type folder: :class:`canvasapi.folder.Folder` or int + + :rtype: :class:`canvasapi.folder.Folder` + """ + folder_id = obj_or_id(folder, "folder", (Folder,)) + + response = self.__requester.request( + 'GET', + 'folders/{}'.format(folder_id) + ) + return Folder(self.__requester, response.json()) + + + def multiline_URL(self, **kwargs): + """ + Fetch all preferences for the given communication channel. + + :calls: `GET + /api/v1/users/:user_id/communication_channels/:communication_channel_id/ \ + notification_preferences \ + `_ + + :rtype: `list` + """ + response = self._requester.request( + 'GET', + 'users/{}/communication_channels/{}/notification_preferences'.format( + self.user_id, + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + + return response.json()['notification_preferences'] + + def non_api_call(self): + """ + Kick off uploading process. Handles open/closing file if a path + is passed. + + :calls: request_upload_token + :returns: True if the file uploaded successfully, False \ + otherwise, and the JSON response from the API. + :rtype: tuple + """ + pass From f19c8ac2121efb5f3b44e68f479c406f48c3f74d Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Fri, 11 May 2018 13:07:57 -0400 Subject: [PATCH 75/91] test_validate_docstrings now use mocker objects --- scripts/test_validate_docstrings.py | 122 ------------------ .../fixtures/files.method.files.destroy.html | 98 ++++++++++++++ tests/fixtures/files.method.folders.show.html | 84 ++++++++++++ ...method.notification_preferences.index.html | 61 +++++++++ tests/test_validate_docstrings.py | 33 ++++- 5 files changed, 270 insertions(+), 128 deletions(-) delete mode 100644 scripts/test_validate_docstrings.py create mode 100644 tests/fixtures/files.method.files.destroy.html create mode 100644 tests/fixtures/files.method.folders.show.html create mode 100644 tests/fixtures/notification_preferences.method.notification_preferences.index.html diff --git a/scripts/test_validate_docstrings.py b/scripts/test_validate_docstrings.py deleted file mode 100644 index 58e5f183..00000000 --- a/scripts/test_validate_docstrings.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -from canvasapi.canvas_object import CanvasObject -from canvasapi.folder import Folder -from canvasapi.util import combine_kwargs, obj_or_id -from validate_docstrings import validate_method - - -# test_endpoint_docstrings -def test_validate_method(): - assert not validate_method(ExampleMethods.fails_wrong_docstring_verb, True) - assert not validate_method(ExampleMethods.fails_invalid_docstring_verb, True) - assert validate_method(ExampleMethods.passes_no_api_call, True) - assert validate_method(ExampleMethods.passes_good_docstring, True) - assert validate_method(ExampleMethods.passes_multiple_endpoints, True) - assert validate_method(ExampleMethods.passes_multiline_URL, True) - assert validate_method(ExampleMethods.passes_calls_but_not_api, True) - - -class ExampleMethods(CanvasObject): - def fails_wrong_docstring_verb(self): - """ - :calls: `PUT /api/v1/files/:id \ - `_ - - :rtype: :class:`canvasapi.file.File` - """ - response = self._requester.request( - 'DELETE', - 'files/{}'.format(self.id) - ) - return ExampleMethods(self._requester, response.json()) - - def passes_no_api_call(self): - """ - Empty docstring. - """ - return False - - def passes_good_docstring(self): - """ - Delete this file. - - :calls: `DELETE /api/v1/files/:id \ - `_ - - :rtype: :class:`canvasapi.file.File` - """ - response = self._requester.request( - 'DELETE', - 'files/{}'.format(self.id) - ) - return ExampleMethods(self._requester, response.json()) - - def passes_multiple_endpoints(self, folder): - """ - Return the details for a folder - - :calls: `GET /api/v1/folders/:id \ - `_ - - :param folder: The object or ID of the folder to retrieve. - :type folder: :class:`canvasapi.folder.Folder` or int - - :rtype: :class:`canvasapi.folder.Folder` - """ - folder_id = obj_or_id(folder, "folder", (Folder,)) - - response = self.__requester.request( - 'GET', - 'folders/{}'.format(folder_id) - ) - return Folder(self.__requester, response.json()) - - def fails_invalid_docstring_verb(self): - """ - Delete this file. - - :calls: `BELETE /api/v1/files/:id \ - `_ - - :rtype: :class:`canvasapi.file.File` - """ - response = self._requester.request( - 'DELETE', - 'files/{}'.format(self.id) - ) - return ExampleMethods(self._requester, response.json()) - - def passes_multiline_URL(self, **kwargs): - """ - Fetch all preferences for the given communication channel. - - :calls: `GET - /api/v1/users/:user_id/communication_channels/:communication_channel_id/ \ - notification_preferences \ - `_ - - :rtype: `list` - """ - response = self._requester.request( - 'GET', - 'users/{}/communication_channels/{}/notification_preferences'.format( - self.user_id, - self.id - ), - _kwargs=combine_kwargs(**kwargs) - ) - - return response.json()['notification_preferences'] - - def passes_calls_but_not_api(): - """ - Kick off uploading process. Handles open/closing file if a path - is passed. - - :calls: request_upload_token - :returns: True if the file uploaded successfully, False \ - otherwise, and the JSON response from the API. - :rtype: tuple - """ - pass diff --git a/tests/fixtures/files.method.files.destroy.html b/tests/fixtures/files.method.files.destroy.html new file mode 100644 index 00000000..f6c27125 --- /dev/null +++ b/tests/fixtures/files.method.files.destroy.html @@ -0,0 +1,98 @@ + + + + + + + Files - Canvas LMS REST API Documentation + + + + + + + + + + +
+

+ + Delete file + + + FilesController#destroy + +

+ + + +

+ DELETE /api/v1/files/:id +

+ + +

Remove the specified file

+ +

Request Parameters:

+ + + + + + + + + + + + + + + + + + + +
ParameterTypeDescription
replace + + boolean + +

This action is irreversible. If replace is set to true the file contents + will be replaced with a generic “file has been removed” file. This + also destroys any previews that have been generated for the file. Must + have manage files and become other users permissions

+ + +
+ +
+

Example Request:

+ +

+
curl -X DELETE 'https://<canvas>/api/v1/files/<file_id>' \
+     -H 'Authorization: Bearer <token>'
+ +
+ + + + Returns a + + File + +
+ + + diff --git a/tests/fixtures/files.method.folders.show.html b/tests/fixtures/files.method.folders.show.html new file mode 100644 index 00000000..7919b614 --- /dev/null +++ b/tests/fixtures/files.method.folders.show.html @@ -0,0 +1,84 @@ + + + + + +Files - Canvas LMS REST API Documentation + + + + + + + + +
+

+ + Get folder + + + FoldersController#show + +

+ + + +

+ GET /api/v1/courses/:course_id/folders/:id +

+ + +

+ GET /api/v1/users/:user_id/folders/:id +

+ + +

+ GET /api/v1/groups/:group_id/folders/:id +

+ + +

+ GET /api/v1/folders/:id +

+ + +

Returns the details for a folder

+ +

You can get the root folder from a context by using 'root' as the +:id. For example, you could get the root folder for a course like:

+ + +
+

Example Request:

+ +

+
curl 'https://<canvas>/api/v1/courses/1337/folders/root' \
+     -H 'Authorization: Bearer <token>'
+ +

+
curl 'https://<canvas>/api/v1/folders/<folder_id>' \
+     -H 'Authorization: Bearer <token>'
+ +
+ + + + Returns a + +Folder + +
+ + diff --git a/tests/fixtures/notification_preferences.method.notification_preferences.index.html b/tests/fixtures/notification_preferences.method.notification_preferences.index.html new file mode 100644 index 00000000..f645e3d5 --- /dev/null +++ b/tests/fixtures/notification_preferences.method.notification_preferences.index.html @@ -0,0 +1,61 @@ + + + + + + +Notification Preferences - Canvas LMS REST API Documentation + + + + + + + + +
+

+ + List preferences + + + NotificationPreferencesController#index + +

+ + + +

+ GET /api/v1/users/:user_id/communication_channels/:communication_channel_id/notification_preferences +

+ + +

+ GET /api/v1/users/:user_id/communication_channels/:type/:address/notification_preferences +

+ + +

Fetch all preferences for the given communication channel

+ + + + + Returns a list of + +NotificationPreferences + +
+ + + + diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index 80061570..75a83a88 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -1,5 +1,8 @@ from __future__ import absolute_import, division, print_function, unicode_literals +import re +import io + from canvasapi.canvas_object import CanvasObject from canvasapi.folder import Folder from canvasapi.util import combine_kwargs, obj_or_id @@ -11,29 +14,47 @@ # test_endpoint_docstrings @requests_mock.Mocker() class TestValidateDocstrings(unittest.TestCase): - def test_validate_method_verd_mismatch(self, m): + def test_validate_method_verb_mismatch(self, m): + url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy>' + register_html_uri(url, m) self.assertFalse(validate_method(ExampleMethods.verb_mismatch, True)) def test_validate_method_invalid_verb(self, m): + url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy' + register_html_uri(url, m) self.assertFalse(validate_method(ExampleMethods.invalid_verb, True)) def test_validate_method_no_api_call(self, m): self.assertTrue(validate_method(ExampleMethods.no_api_call, True)) def test_validate_method_good_docstring(self, m): + url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy' + register_html_uri(url, m) self.assertTrue(validate_method(ExampleMethods.good_docstring, True)) def test_validate_method_multiple_endpoints(self, m): - self.assertTrue(validate_method(ExampleMethods.multiple_endpoints, - True - )) + url = 'https://canvas.instructure.com/doc/api/files.html#method.folders.show' + register_html_uri(url, m) + self.assertTrue(validate_method(ExampleMethods.multiple_endpoints, True)) def test_validate_method_multiline_URL(self, m): + url = 'https://canvas.instructure.com/doc/api/notification_preferences.html#method.notification_preferences.index' + register_html_uri(url, m) self.assertTrue(validate_method(ExampleMethods.multiline_URL, True)) - def test_validate_method_multiline_URL(self, m): - self.assertTrue(validate_method(ExampleMethods.non_api_call, True)) +def register_html_uri(url, m): + url_groups = re.search('(.*\/)([^\/]*)\.html#([^>]*)', url) + file_name = url_groups.group(2) + method_name = url_groups.group(3) + + file = io.open('tests/fixtures/{}.{}.html'.format(file_name, method_name), + mode='r', + encoding='utf-8' + ) + data = file.read() + + m.register_uri('GET', url_groups.group(1) + url_groups.group(2) + '.html', text=data) class ExampleMethods(CanvasObject): def verb_mismatch(self): From 8969b54b26474c3efc3028ff1a904f8ed8cf8ea6 Mon Sep 17 00:00:00 2001 From: Henry Acevedo Date: Fri, 11 May 2018 10:31:50 -0700 Subject: [PATCH 76/91] #147 Updated examples.rst with correct method for getting user logins #147 Updated examples.rst with correct method for getting user logins --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 04fdbdf2..08ef5e31 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -107,7 +107,7 @@ List User Logins .. code-block:: python - logins = user.get_logins() + logins = user.get_user_logins() for login in logins: print(login) From 7f1231027657cca4489b13e8462e8d8d23bf7499 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 15 May 2018 11:55:26 -0400 Subject: [PATCH 77/91] improved unit tests --- scripts/validate_docstrings.py | 23 +- tests/fixtures/404..html | 229 ++++++++++ tests/fixtures/files..html | 84 ++++ tests/fixtures/files.invalid.html | 98 +++++ ...on_preferences.NotificationPreference.html | 391 ++++++++++++++++++ tests/test_validate_docstrings.py | 116 +++++- 6 files changed, 925 insertions(+), 16 deletions(-) create mode 100644 tests/fixtures/404..html create mode 100644 tests/fixtures/files..html create mode 100644 tests/fixtures/files.invalid.html create mode 100644 tests/fixtures/notification_preferences.NotificationPreference.html diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 38213bfc..9d7ca0e4 100644 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -40,7 +40,7 @@ def validate_docstring(method_string, call_line, quiet): 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: @@ -57,7 +57,7 @@ def validate_docstring(method_string, call_line, quiet): if not quiet: print(( '{} docstring URL does not contain an endpoint name in link' - 'to API documentation' + ' to API documentation' ).format(method_string)) return False if not endpoint_h2: @@ -70,11 +70,20 @@ def validate_docstring(method_string, call_line, quiet): endpoint_element_re = re.compile( r'

[^<]*<\/h3>' ) - endpoint_search_start_pos = endpoint_element_re.search( + + endpoint_search_start_match=endpoint_element_re.search( html_doc_response.text, endpoint_h2.end() - ).start() + ) + if not endpoint_search_start_match: + if not quiet: + 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) @@ -97,7 +106,7 @@ def validate_docstring(method_string, call_line, quiet): if not endpoint_element_list: if not quiet: - print('Found no endpoint after %s in %s'.format( + print('Found no endpoint after {} in {}'.format( endpoint_name, docfile_URL )) @@ -133,5 +142,5 @@ def test_methods(): for method_to_test in methods: validate_method(method_to_test) - -# test_methods() +if __name__ == '__main__': + test_methods() diff --git a/tests/fixtures/404..html b/tests/fixtures/404..html new file mode 100644 index 00000000..1fbf6c7d --- /dev/null +++ b/tests/fixtures/404..html @@ -0,0 +1,229 @@ + + + + + Page Not Found + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    +
    + Close +
    + +
    +
    +
    +
    +
    +
    +
    +
    +

    Page Not Found

    +

    Oops, we couldn't find that page.

    + Click here to tell us what you were looking for. + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +   +   +   +   +
    Equella is a shared content repository that organizations can use to easily track and reuse content. This OER repository is a collection of free resources provided by Equella.
    +
    + +
    + + + +
    + + + diff --git a/tests/fixtures/files..html b/tests/fixtures/files..html new file mode 100644 index 00000000..7919b614 --- /dev/null +++ b/tests/fixtures/files..html @@ -0,0 +1,84 @@ + + + + + +Files - Canvas LMS REST API Documentation + + + + + + + + +
    +

    + + Get folder + + + FoldersController#show + +

    + + + +

    + GET /api/v1/courses/:course_id/folders/:id +

    + + +

    + GET /api/v1/users/:user_id/folders/:id +

    + + +

    + GET /api/v1/groups/:group_id/folders/:id +

    + + +

    + GET /api/v1/folders/:id +

    + + +

    Returns the details for a folder

    + +

    You can get the root folder from a context by using 'root' as the +:id. For example, you could get the root folder for a course like:

    + + +
    +

    Example Request:

    + +

    +
    curl 'https://<canvas>/api/v1/courses/1337/folders/root' \
    +     -H 'Authorization: Bearer <token>'
    + +

    +
    curl 'https://<canvas>/api/v1/folders/<folder_id>' \
    +     -H 'Authorization: Bearer <token>'
    + +
    + + + + Returns a + +Folder + +
    + + diff --git a/tests/fixtures/files.invalid.html b/tests/fixtures/files.invalid.html new file mode 100644 index 00000000..0b996b97 --- /dev/null +++ b/tests/fixtures/files.invalid.html @@ -0,0 +1,98 @@ + + + + + + + Files - Canvas LMS REST API Documentation + + + + + + + + + + +
    +

    + + Delete file + + + FilesController#destroy + +

    + + + +

    + DELETE /api/v1/files/:id +

    + + +

    Remove the specified file

    + +

    Request Parameters:

    + + + + + + + + + + + + + + + + + + + +
    ParameterTypeDescription
    replace + + boolean + +

    This action is irreversible. If replace is set to true the file contents + will be replaced with a generic “file has been removed” file. This + also destroys any previews that have been generated for the file. Must + have manage files and become other users permissions

    + + +
    + +
    +

    Example Request:

    + +

    +
    curl -X DELETE 'https://<canvas>/api/v1/files/<file_id>' \
    +     -H 'Authorization: Bearer <token>'
    + +
    + + + + Returns a + + File + +
    + + + diff --git a/tests/fixtures/notification_preferences.NotificationPreference.html b/tests/fixtures/notification_preferences.NotificationPreference.html new file mode 100644 index 00000000..1164c7f6 --- /dev/null +++ b/tests/fixtures/notification_preferences.NotificationPreference.html @@ -0,0 +1,391 @@ + + + + + +Notification Preferences - Canvas LMS REST API Documentation + + + + + + + + + + + + + +
    + +

    Notification Preferences API

    + + + + +

    API for managing notification preferences

    + + + + +
    +

    A NotificationPreference object looks like:

    +
    {
    +  "href": "https://canvas.instructure.com/users/1/communication_channels/email/student@example.edu/notification_preferences/new_announcement",
    +  // The notification this preference belongs to
    +  "notification": "new_announcement",
    +  // The category of that notification
    +  "category": "announcement",
    +  // How often to send notifications to this communication channel for the given
    +  // notification. Possible values are 'immediately', 'daily', 'weekly', and 'never'
    +  "frequency": "daily"
    +}
    +
    + + + diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index 75a83a88..e34e303e 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -16,12 +16,12 @@ class TestValidateDocstrings(unittest.TestCase): def test_validate_method_verb_mismatch(self, m): url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy>' - register_html_uri(url, m) + register_doc_uri(url, m) self.assertFalse(validate_method(ExampleMethods.verb_mismatch, True)) def test_validate_method_invalid_verb(self, m): url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy' - register_html_uri(url, m) + register_doc_uri(url, m) self.assertFalse(validate_method(ExampleMethods.invalid_verb, True)) def test_validate_method_no_api_call(self, m): @@ -29,22 +29,43 @@ def test_validate_method_no_api_call(self, m): def test_validate_method_good_docstring(self, m): url = 'https://canvas.instructure.com/doc/api/files.html#method.files.destroy' - register_html_uri(url, m) + register_doc_uri(url, m) self.assertTrue(validate_method(ExampleMethods.good_docstring, True)) def test_validate_method_multiple_endpoints(self, m): url = 'https://canvas.instructure.com/doc/api/files.html#method.folders.show' - register_html_uri(url, m) + register_doc_uri(url, m) self.assertTrue(validate_method(ExampleMethods.multiple_endpoints, True)) def test_validate_method_multiline_URL(self, m): url = 'https://canvas.instructure.com/doc/api/notification_preferences.html#method.notification_preferences.index' - register_html_uri(url, m) + register_doc_uri(url, m) self.assertTrue(validate_method(ExampleMethods.multiline_URL, True)) - -def register_html_uri(url, m): - url_groups = re.search('(.*\/)([^\/]*)\.html#([^>]*)', url) + def test_validate_method_invalid_URL(self, m): + url = 'https://canvas.instructure.com/doc/api/404.html' + register_doc_uri(url, m, code=404) + self.assertFalse(validate_method(ExampleMethods.invalid_URL, True)) + + def test_validate_method_missing_endpoint_URL(self, m): + url = 'https://canvas.instructure.com/doc/api/files.html' + register_doc_uri(url, m) + self.assertFalse(validate_method(ExampleMethods.missing_endpoint_URL, True)) + + def test_validate_method_endpoint_URL_invalid(self, m): + url = 'https://canvas.instructure.com/doc/api/files.html#invalid' + register_doc_uri(url ,m) + self.assertFalse(validate_method(ExampleMethods.endpoint_invalid, True)) + + def test_validate_method_not_an_endpoint(self,m): + url = 'https://canvas.instructure.com/doc/api/notification_preferences.html#NotificationPreference' + register_doc_uri(url, m) + self.assertFalse(validate_method(ExampleMethods.not_an_endpoint, True)) + +def register_doc_uri(url, m, code=200): + url_groups = re.search(r'(.*\/)([^\/]*)\.html#?([^>]*)', url) + if not url_groups: + return file_name = url_groups.group(2) method_name = url_groups.group(3) @@ -54,7 +75,7 @@ def register_html_uri(url, m): ) data = file.read() - m.register_uri('GET', url_groups.group(1) + url_groups.group(2) + '.html', text=data) + m.register_uri('GET', url_groups.group(1) + url_groups.group(2) + '.html', text=data, status_code=code) class ExampleMethods(CanvasObject): def verb_mismatch(self): @@ -160,3 +181,80 @@ def non_api_call(self): :rtype: tuple """ pass + + def invalid_URL(self): + """ + Delete this file. + + :calls: `DELETE /api/v1/files/:id \ + `_ + + :rtype: :class:`canvasapi.file.File` + """ + response = self._requester.request( + 'DELETE', + 'files/{}'.format(self.id) + ) + return ExampleMethods(self._requester, response.json()) + + def missing_endpoint_URL(self, folder): + """ + Return the details for a folder + + :calls: `GET /api/v1/folders/:id \ + `_ + + :param folder: The object or ID of the folder to retrieve. + :type folder: :class:`canvasapi.folder.Folder` or int + + :rtype: :class:`canvasapi.folder.Folder` + """ + folder_id = obj_or_id(folder, "folder", (Folder,)) + + response = self.__requester.request( + 'GET', + 'folders/{}'.format(folder_id) + ) + return Folder(self.__requester, response.json()) + + def endpoint_invalid(self, folder): + """ + Return the details for a folder + + :calls: `GET /api/v1/folders/:id \ + `_ + + :param folder: The object or ID of the folder to retrieve. + :type folder: :class:`canvasapi.folder.Folder` or int + + :rtype: :class:`canvasapi.folder.Folder` + """ + folder_id = obj_or_id(folder, "folder", (Folder,)) + + response = self.__requester.request( + 'GET', + 'folders/{}'.format(folder_id) + ) + return Folder(self.__requester, response.json()) + + def not_an_endpoint(self, **kwargs): + """ + Fetch all preferences for the given communication channel. + + :calls: `GET + /api/v1/users/:user_id/communication_channels/:communication_channel_id/ \ + notification_preferences \ + `_ + + :rtype: `list` + """ + response = self._requester.request( + 'GET', + 'users/{}/communication_channels/{}/notification_preferences'.format( + self.user_id, + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + + return response.json()['notification_preferences'] From ebe02844c530b7ca13573ef7818f51f73dcdc6b9 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 15 May 2018 12:02:14 -0400 Subject: [PATCH 78/91] flake8 improvements --- scripts/validate_docstrings.py | 3 ++- tests/test_validate_docstrings.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 9d7ca0e4..f33647f3 100644 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -71,7 +71,7 @@ def validate_docstring(method_string, call_line, quiet): r'

    [^<]*<\/h3>' ) - endpoint_search_start_match=endpoint_element_re.search( + endpoint_search_start_match = endpoint_element_re.search( html_doc_response.text, endpoint_h2.end() ) @@ -142,5 +142,6 @@ def test_methods(): for method_to_test in methods: validate_method(method_to_test) + if __name__ == '__main__': test_methods() diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index e34e303e..201b04b2 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -11,6 +11,8 @@ import unittest import requests_mock + + # test_endpoint_docstrings @requests_mock.Mocker() class TestValidateDocstrings(unittest.TestCase): @@ -62,6 +64,7 @@ def test_validate_method_not_an_endpoint(self,m): register_doc_uri(url, m) self.assertFalse(validate_method(ExampleMethods.not_an_endpoint, True)) + def register_doc_uri(url, m, code=200): url_groups = re.search(r'(.*\/)([^\/]*)\.html#?([^>]*)', url) if not url_groups: @@ -77,6 +80,7 @@ def register_doc_uri(url, m, code=200): m.register_uri('GET', url_groups.group(1) + url_groups.group(2) + '.html', text=data, status_code=code) + class ExampleMethods(CanvasObject): def verb_mismatch(self): """ @@ -147,7 +151,6 @@ def multiple_endpoints(self, folder): ) return Folder(self.__requester, response.json()) - def multiline_URL(self, **kwargs): """ Fetch all preferences for the given communication channel. From e302d28de6e992afe45afd839e17d51926a993c7 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 15 May 2018 12:20:26 -0400 Subject: [PATCH 79/91] resolved flake8 errors --- scripts/validate_docstrings.py | 9 ++++++--- tests/test_validate_docstrings.py | 24 ++++++++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index f33647f3..5c92fc4d 100644 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -50,7 +50,8 @@ def validate_docstring(method_string, call_line, quiet): )) return False - endpoint_h2 = re.search(r'name=[\'\"]{}[\'\"]'.format(endpoint_name), + endpoint_h2 = re.search( + r'name=[\'\"]{}[\'\"]'.format(endpoint_name), html_doc_response.text ) if not endpoint_name: @@ -91,7 +92,8 @@ def validate_docstring(method_string, call_line, quiet): 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_element_match = endpoint_element_re.search( + html_doc_response.text, endpoint_search_start_pos, endpoint_search_stop_pos ) @@ -113,7 +115,8 @@ def validate_docstring(method_string, call_line, quiet): return False docfile_lines = [] for endpoint_element_str in endpoint_element_list: - docfile_match = re.search('(POST|GET|PUT|PATCH|DELETE) (.*)', + docfile_match = re.search( + '(POST|GET|PUT|PATCH|DELETE) (.*)', endpoint_element_str ) docfile_lines.append(docfile_match.group()) diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index 201b04b2..0e6034ec 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -40,7 +40,10 @@ def test_validate_method_multiple_endpoints(self, m): self.assertTrue(validate_method(ExampleMethods.multiple_endpoints, True)) def test_validate_method_multiline_URL(self, m): - url = 'https://canvas.instructure.com/doc/api/notification_preferences.html#method.notification_preferences.index' + url = ( + 'https://canvas.instructure.com/doc/api/notification_preferences.html' + '#method.notification_preferences.index' + ) register_doc_uri(url, m) self.assertTrue(validate_method(ExampleMethods.multiline_URL, True)) @@ -56,11 +59,14 @@ def test_validate_method_missing_endpoint_URL(self, m): def test_validate_method_endpoint_URL_invalid(self, m): url = 'https://canvas.instructure.com/doc/api/files.html#invalid' - register_doc_uri(url ,m) + register_doc_uri(url, m) self.assertFalse(validate_method(ExampleMethods.endpoint_invalid, True)) - def test_validate_method_not_an_endpoint(self,m): - url = 'https://canvas.instructure.com/doc/api/notification_preferences.html#NotificationPreference' + def test_validate_method_not_an_endpoint(self, m): + url = ( + 'https://canvas.instructure.com/doc/api/notification_preferences.html' + '#NotificationPreference' + ) register_doc_uri(url, m) self.assertFalse(validate_method(ExampleMethods.not_an_endpoint, True)) @@ -72,13 +78,19 @@ def register_doc_uri(url, m, code=200): file_name = url_groups.group(2) method_name = url_groups.group(3) - file = io.open('tests/fixtures/{}.{}.html'.format(file_name, method_name), + file = io.open( + 'tests/fixtures/{}.{}.html'.format(file_name, method_name), mode='r', encoding='utf-8' ) data = file.read() - m.register_uri('GET', url_groups.group(1) + url_groups.group(2) + '.html', text=data, status_code=code) + m.register_uri( + 'GET', + url_groups.group(1) + url_groups.group(2) + '.html', + text=data, + status_code=code + ) class ExampleMethods(CanvasObject): From a2a1ef7b974990dbbf8d078e16e61cd8d65614c6 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 15 May 2018 15:01:14 -0400 Subject: [PATCH 80/91] cleaned test fixtures --- scripts/validate_docstrings.py | 2 +- tests/fixtures/404..html | 229 ---------- tests/fixtures/404.html | 0 tests/fixtures/files..html | 84 ---- tests/fixtures/files.html | 21 + tests/fixtures/files.invalid.html | 98 ----- .../fixtures/files.method.files.destroy.html | 98 ----- tests/fixtures/files.method.folders.show.html | 84 ---- ...on_preferences.NotificationPreference.html | 391 ------------------ tests/fixtures/notification_preferences.html | 11 + ...method.notification_preferences.index.html | 61 --- tests/test_validate_docstrings.py | 5 +- 12 files changed, 35 insertions(+), 1049 deletions(-) delete mode 100644 tests/fixtures/404..html create mode 100644 tests/fixtures/404.html delete mode 100644 tests/fixtures/files..html create mode 100644 tests/fixtures/files.html delete mode 100644 tests/fixtures/files.invalid.html delete mode 100644 tests/fixtures/files.method.files.destroy.html delete mode 100644 tests/fixtures/files.method.folders.show.html delete mode 100644 tests/fixtures/notification_preferences.NotificationPreference.html create mode 100644 tests/fixtures/notification_preferences.html delete mode 100644 tests/fixtures/notification_preferences.method.notification_preferences.index.html diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 5c92fc4d..95859330 100644 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -51,7 +51,7 @@ def validate_docstring(method_string, call_line, quiet): return False endpoint_h2 = re.search( - r'name=[\'\"]{}[\'\"]'.format(endpoint_name), + r']*name=[\'\"]{}[\'\"]'.format(endpoint_name), html_doc_response.text ) if not endpoint_name: diff --git a/tests/fixtures/404..html b/tests/fixtures/404..html deleted file mode 100644 index 1fbf6c7d..00000000 --- a/tests/fixtures/404..html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - Page Not Found - - - - - - - - - - - - - - - - - - - - - - -
      -
      -
      - -
      -
      - Close -
      - -
      -
      -
      -
      -
      -
      -
      -
      -

      Page Not Found

      -

      Oops, we couldn't find that page.

      - Click here to tell us what you were looking for. - -
      -
      -
      -
      - -
      -
      -
      -
      -
      - -   -   -   -   -
      Equella is a shared content repository that organizations can use to easily track and reuse content. This OER repository is a collection of free resources provided by Equella.
      -
      - -
      - - - -
      - - - diff --git a/tests/fixtures/404.html b/tests/fixtures/404.html new file mode 100644 index 00000000..e69de29b diff --git a/tests/fixtures/files..html b/tests/fixtures/files..html deleted file mode 100644 index 7919b614..00000000 --- a/tests/fixtures/files..html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - -Files - Canvas LMS REST API Documentation - - - - - - - - -
      -

      - - Get folder - - - FoldersController#show - -

      - - - -

      - GET /api/v1/courses/:course_id/folders/:id -

      - - -

      - GET /api/v1/users/:user_id/folders/:id -

      - - -

      - GET /api/v1/groups/:group_id/folders/:id -

      - - -

      - GET /api/v1/folders/:id -

      - - -

      Returns the details for a folder

      - -

      You can get the root folder from a context by using 'root' as the -:id. For example, you could get the root folder for a course like:

      - - -
      -

      Example Request:

      - -

      -
      curl 'https://<canvas>/api/v1/courses/1337/folders/root' \
      -     -H 'Authorization: Bearer <token>'
      - -

      -
      curl 'https://<canvas>/api/v1/folders/<folder_id>' \
      -     -H 'Authorization: Bearer <token>'
      - -
      - - - - Returns a - -Folder - -
      - - diff --git a/tests/fixtures/files.html b/tests/fixtures/files.html new file mode 100644 index 00000000..7d2030ce --- /dev/null +++ b/tests/fixtures/files.html @@ -0,0 +1,21 @@ +

      +

      +

      + DELETE /api/v1/files/:id +

      +

      Remove the specified file

      +

      +

      +

      + GET /api/v1/courses/:course_id/folders/:id +

      +

      + GET /api/v1/users/:user_id/folders/:id +

      +

      + GET /api/v1/groups/:group_id/folders/:id +

      +

      + GET /api/v1/folders/:id +

      +

      Returns the details for a folder

      diff --git a/tests/fixtures/files.invalid.html b/tests/fixtures/files.invalid.html deleted file mode 100644 index 0b996b97..00000000 --- a/tests/fixtures/files.invalid.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - Files - Canvas LMS REST API Documentation - - - - - - - - - - -
      -

      - - Delete file - - - FilesController#destroy - -

      - - - -

      - DELETE /api/v1/files/:id -

      - - -

      Remove the specified file

      - -

      Request Parameters:

      - - - - - - - - - - - - - - - - - - - -
      ParameterTypeDescription
      replace - - boolean - -

      This action is irreversible. If replace is set to true the file contents - will be replaced with a generic “file has been removed” file. This - also destroys any previews that have been generated for the file. Must - have manage files and become other users permissions

      - - -
      - -
      -

      Example Request:

      - -

      -
      curl -X DELETE 'https://<canvas>/api/v1/files/<file_id>' \
      -     -H 'Authorization: Bearer <token>'
      - -
      - - - - Returns a - - File - -
      - - - diff --git a/tests/fixtures/files.method.files.destroy.html b/tests/fixtures/files.method.files.destroy.html deleted file mode 100644 index f6c27125..00000000 --- a/tests/fixtures/files.method.files.destroy.html +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - Files - Canvas LMS REST API Documentation - - - - - - - - - - -
      -

      - - Delete file - - - FilesController#destroy - -

      - - - -

      - DELETE /api/v1/files/:id -

      - - -

      Remove the specified file

      - -

      Request Parameters:

      - - - - - - - - - - - - - - - - - - - -
      ParameterTypeDescription
      replace - - boolean - -

      This action is irreversible. If replace is set to true the file contents - will be replaced with a generic “file has been removed” file. This - also destroys any previews that have been generated for the file. Must - have manage files and become other users permissions

      - - -
      - -
      -

      Example Request:

      - -

      -
      curl -X DELETE 'https://<canvas>/api/v1/files/<file_id>' \
      -     -H 'Authorization: Bearer <token>'
      - -
      - - - - Returns a - - File - -
      - - - diff --git a/tests/fixtures/files.method.folders.show.html b/tests/fixtures/files.method.folders.show.html deleted file mode 100644 index 7919b614..00000000 --- a/tests/fixtures/files.method.folders.show.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - -Files - Canvas LMS REST API Documentation - - - - - - - - -
      -

      - - Get folder - - - FoldersController#show - -

      - - - -

      - GET /api/v1/courses/:course_id/folders/:id -

      - - -

      - GET /api/v1/users/:user_id/folders/:id -

      - - -

      - GET /api/v1/groups/:group_id/folders/:id -

      - - -

      - GET /api/v1/folders/:id -

      - - -

      Returns the details for a folder

      - -

      You can get the root folder from a context by using 'root' as the -:id. For example, you could get the root folder for a course like:

      - - -
      -

      Example Request:

      - -

      -
      curl 'https://<canvas>/api/v1/courses/1337/folders/root' \
      -     -H 'Authorization: Bearer <token>'
      - -

      -
      curl 'https://<canvas>/api/v1/folders/<folder_id>' \
      -     -H 'Authorization: Bearer <token>'
      - -
      - - - - Returns a - -Folder - -
      - - diff --git a/tests/fixtures/notification_preferences.NotificationPreference.html b/tests/fixtures/notification_preferences.NotificationPreference.html deleted file mode 100644 index 1164c7f6..00000000 --- a/tests/fixtures/notification_preferences.NotificationPreference.html +++ /dev/null @@ -1,391 +0,0 @@ - - - - - -Notification Preferences - Canvas LMS REST API Documentation - - - - - - - - - - - - - -
      - -

      Notification Preferences API

      - - - - -

      API for managing notification preferences

      - - - - -
      -

      A NotificationPreference object looks like:

      -
      {
      -  "href": "https://canvas.instructure.com/users/1/communication_channels/email/student@example.edu/notification_preferences/new_announcement",
      -  // The notification this preference belongs to
      -  "notification": "new_announcement",
      -  // The category of that notification
      -  "category": "announcement",
      -  // How often to send notifications to this communication channel for the given
      -  // notification. Possible values are 'immediately', 'daily', 'weekly', and 'never'
      -  "frequency": "daily"
      -}
      -
      - - - diff --git a/tests/fixtures/notification_preferences.html b/tests/fixtures/notification_preferences.html new file mode 100644 index 00000000..fd0f145a --- /dev/null +++ b/tests/fixtures/notification_preferences.html @@ -0,0 +1,11 @@ +

      A NotificationPreference object looks like:

      +
      
      +

      +

      +

      + GET /api/v1/users/:user_id/communication_channels/:communication_channel_id/notification_preferences +

      +

      + GET /api/v1/users/:user_id/communication_channels/:type/:address/notification_preferences +

      +

      Fetch all preferences for the given communication channel

      diff --git a/tests/fixtures/notification_preferences.method.notification_preferences.index.html b/tests/fixtures/notification_preferences.method.notification_preferences.index.html deleted file mode 100644 index f645e3d5..00000000 --- a/tests/fixtures/notification_preferences.method.notification_preferences.index.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - -Notification Preferences - Canvas LMS REST API Documentation - - - - - - - - -
      -

      - - List preferences - - - NotificationPreferencesController#index - -

      - - - -

      - GET /api/v1/users/:user_id/communication_channels/:communication_channel_id/notification_preferences -

      - - -

      - GET /api/v1/users/:user_id/communication_channels/:type/:address/notification_preferences -

      - - -

      Fetch all preferences for the given communication channel

      - - - - - Returns a list of - -NotificationPreferences - -
      - - - - diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index 0e6034ec..570f698f 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -72,14 +72,13 @@ def test_validate_method_not_an_endpoint(self, m): def register_doc_uri(url, m, code=200): - url_groups = re.search(r'(.*\/)([^\/]*)\.html#?([^>]*)', url) + url_groups = re.search(r'(.*\/)([^\/]*)\.html', url) if not url_groups: return file_name = url_groups.group(2) - method_name = url_groups.group(3) file = io.open( - 'tests/fixtures/{}.{}.html'.format(file_name, method_name), + 'tests/fixtures/{}.html'.format(file_name), mode='r', encoding='utf-8' ) From 06b73087e3e18de17a9db33cf97d9f4deb40a651 Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Tue, 29 May 2018 10:40:35 -0400 Subject: [PATCH 81/91] added API wrappers for updating multiple submissions --- canvasapi/assignment.py | 24 ++++++++++++++++++++++++ canvasapi/course.py | 22 ++++++++++++++++++++++ canvasapi/section.py | 22 ++++++++++++++++++++++ tests/fixtures/assignment.json | 17 +++++++++++++++++ tests/fixtures/course.json | 17 +++++++++++++++++ tests/fixtures/progress.json | 18 +++++++++++++++++- tests/fixtures/section.json | 17 +++++++++++++++++ tests/test_assignment.py | 18 ++++++++++++++++++ tests/test_course.py | 20 ++++++++++++++++++++ tests/test_section.py | 19 +++++++++++++++++++ 10 files changed, 193 insertions(+), 1 deletion(-) diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py index bbc4bdac..77871a9b 100644 --- a/canvasapi/assignment.py +++ b/canvasapi/assignment.py @@ -5,6 +5,7 @@ from canvasapi.canvas_object import CanvasObject from canvasapi.exceptions import RequiredFieldMissing from canvasapi.paginated_list import PaginatedList +from canvasapi.progress import Progress from canvasapi.submission import Submission from canvasapi.user import UserDisplay from canvasapi.util import combine_kwargs, obj_or_id @@ -144,6 +145,29 @@ def submit(self, submission, **kwargs): return Submission(self._requester, response_json) + def submissions_bulk_update(self, **kwargs): + """ + Update the grading and comments on multiple student's assignment + submissions in an asynchronous job. + + :calls: POST /api/v1/courses/:course_id/assignments/:assignment_id/ \ + submissions/update_grades \ + `_ + + :rtype: :class:`canvasapi.progress.Progress` + """ + response = self._requester.request( + 'POST', + 'courses/{}/assignments/{}/submissions/update_grades'.format( + self.course_id, + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + response_json = response.json() + progress = Progress(self._requester, response_json) + return progress + @python_2_unicode_compatible class AssignmentGroup(CanvasObject): diff --git a/canvasapi/course.py b/canvasapi/course.py index a8bb7407..8fa8c9bc 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -11,6 +11,7 @@ from canvasapi.folder import Folder from canvasapi.page import Page from canvasapi.paginated_list import PaginatedList +from canvasapi.progress import Progress from canvasapi.tab import Tab from canvasapi.submission import Submission from canvasapi.upload import Uploader @@ -2143,6 +2144,27 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def submissions_bulk_update(self, **kwargs): + """ + Update the grading and comments on multiple student's assignment + submissions in an asynchronous job. + + :calls: POST /api/v1/courses/:course_id/submissions/update_grades \ + `_ + + :rtype: :class:`canvasapi.progress.Progress` + """ + response = self._requester.request( + 'POST', + 'courses/{}/submissions/update_grades'.format( + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + response_json = response.json() + progress = Progress(self._requester, response_json) + return progress + @python_2_unicode_compatible class CourseNickname(CanvasObject): diff --git a/canvasapi/section.py b/canvasapi/section.py index 7a9a95fe..6070ec7c 100644 --- a/canvasapi/section.py +++ b/canvasapi/section.py @@ -5,6 +5,7 @@ from canvasapi.canvas_object import CanvasObject from canvasapi.paginated_list import PaginatedList +from canvasapi.progress import Progress from canvasapi.submission import Submission from canvasapi.util import combine_kwargs, obj_or_id @@ -373,3 +374,24 @@ def mark_submission_as_unread(self, assignment, user, **kwargs): 'user_id': user_id }) return submission.mark_unread(**kwargs) + + def submissions_bulk_update(self, **kwargs): + """ + Update the grading and comments on multiple student's assignment + submissions in an asynchronous job. + + :calls: POST /api/v1/courses/:course_id/submissions/update_grades \ + `_ + + :rtype: :class:`canvasapi.progress.Progress` + """ + response = self._requester.request( + 'POST', + 'sections/{}/submissions/update_grades'.format( + self.id + ), + _kwargs=combine_kwargs(**kwargs) + ) + response_json = response.json() + progress = Progress(self._requester, response_json) + return progress diff --git a/tests/fixtures/assignment.json b/tests/fixtures/assignment.json index 47f38b6a..aec13936 100644 --- a/tests/fixtures/assignment.json +++ b/tests/fixtures/assignment.json @@ -84,5 +84,22 @@ "submission_type": "online_upload" }, "status_code": 200 + }, + "update_submissions": { + "method": "POST", + "endpoint": "courses/1/assignments/1/submissions/update_grades", + "data": { + "id": 3, + "context_id": 1, + "context_type": "Course", + "user_id": null, + "tag": "submissions_update", + "completion": null, + "workflow_state": "queued", + "updated_at": "2013-01-15T15:04:00Z", + "message": null, + "url": "https://canvas.example.edu/api/v1/progress/3" + }, + "status_code": 200 } } diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json index 2f30d675..9f1f08ed 100644 --- a/tests/fixtures/course.json +++ b/tests/fixtures/course.json @@ -1458,5 +1458,22 @@ } ], "status_code": 200 + }, + "update_submissions": { + "method": "POST", + "endpoint": "courses/1/submissions/update_grades", + "data": { + "id": 3, + "context_id": 1, + "context_type": "Course", + "user_id": null, + "tag": "submissions_update", + "completion": null, + "workflow_state": "queued", + "updated_at": "2013-01-15T15:04:00Z", + "message": null, + "url": "https://canvas.example.edu/api/v1/progress/3" + }, + "status_code": 200 } } diff --git a/tests/fixtures/progress.json b/tests/fixtures/progress.json index 8642747f..3be42a02 100644 --- a/tests/fixtures/progress.json +++ b/tests/fixtures/progress.json @@ -16,5 +16,21 @@ "url": "https://canvas.example.edu/api/v1/progress/1" }, "status_code": 200 + }, + "course_progress": { + "method": "GET", + "endpoint": "progress/3", + "data":{ + "id": 3, + "context_id": 1, + "context_type": "Course", + "user_id": null, + "tag": "submissions_update", + "completion": 100, + "workflow_state": "completed", + "updated_at": "2013-01-15T15:04:00Z", + "message": null, + "url": "https://canvas.example.edu/api/v1/progress/3" + } } -} \ No newline at end of file +} diff --git a/tests/fixtures/section.json b/tests/fixtures/section.json index 19c860be..c4caecb3 100644 --- a/tests/fixtures/section.json +++ b/tests/fixtures/section.json @@ -182,5 +182,22 @@ "method": "DELETE", "endpoint": "sections/1/assignments/1/submissions/1/read", "status_code": 204 + }, + "update_submissions": { + "method": "POST", + "endpoint": "sections/1/submissions/update_grades", + "data": { + "id": 3, + "context_id": 1, + "context_type": "Course", + "user_id": null, + "tag": "submissions_update", + "completion": null, + "workflow_state": "queued", + "updated_at": "2013-01-15T15:04:00Z", + "message": null, + "url": "https://canvas.example.edu/api/v1/progress/3" + }, + "status_code": 200 } } diff --git a/tests/test_assignment.py b/tests/test_assignment.py index e0adc348..e5fec7cb 100644 --- a/tests/test_assignment.py +++ b/tests/test_assignment.py @@ -6,6 +6,7 @@ from canvasapi import Canvas from canvasapi.assignment import Assignment, AssignmentGroup from canvasapi.exceptions import RequiredFieldMissing +from canvasapi.progress import Progress from canvasapi.submission import Submission from canvasapi.user import UserDisplay from tests import settings @@ -101,6 +102,23 @@ def test__str__(self, m): string = str(self.assignment) self.assertIsInstance(string, str) + # submissions_bulk_update() + def test_submissions_bulk_update(self, m): + register_uris({'assignment': ['update_submissions']}, m) + register_uris({'progress': ['course_progress']}, m) + progress = self.assignment.submissions_bulk_update(grade_data={ + '1': { + 'posted_grade': 97 + }, + '2': { + 'posted_grade': 98 + } + }) + self.assertIsInstance(progress, Progress) + self.assertTrue(progress.context_type == "Course") + progress = progress.query() + self.assertTrue(progress.context_type == "Course") + @requests_mock.Mocker() class TestAssignmentGroup(unittest.TestCase): diff --git a/tests/test_course.py b/tests/test_course.py index e6e728d1..48053dc7 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -22,6 +22,7 @@ from canvasapi.group import Group, GroupCategory from canvasapi.module import Module from canvasapi.outcome import OutcomeGroup, OutcomeLink +from canvasapi.progress import Progress from canvasapi.quiz import Quiz from canvasapi.rubric import Rubric from canvasapi.section import Section @@ -1387,6 +1388,25 @@ def test_get_migration_systems(self, m): self.assertEqual(migration_systems[1].requires_file_upload, False) self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + # submissions_bulk_update() + def test_submissions_bulk_update(self, m): + register_uris({'course': ['update_submissions']}, m) + register_uris({'progress': ['course_progress']}, m) + progress = self.course.submissions_bulk_update(grade_data={ + '1': { + '1': { + 'posted_grade': 97 + }, + '2': { + 'posted_grade': 98 + } + } + }) + self.assertIsInstance(progress, Progress) + self.assertTrue(progress.context_type == "Course") + progress = progress.query() + self.assertTrue(progress.context_type == "Course") + @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): diff --git a/tests/test_section.py b/tests/test_section.py index 92b500c2..eef76949 100644 --- a/tests/test_section.py +++ b/tests/test_section.py @@ -8,6 +8,7 @@ from canvasapi import Canvas from canvasapi.enrollment import Enrollment from canvasapi.exceptions import RequiredFieldMissing +from canvasapi.progress import Progress from canvasapi.section import Section from canvasapi.submission import Submission from tests import settings @@ -319,3 +320,21 @@ def test_mark_submission_as_unread(self, m): self.assertEqual(len(warning_list), 1) self.assertEqual(warning_list[-1].category, DeprecationWarning) + + def test_submissions_bulk_update(self, m): + register_uris({'section': ['update_submissions']}, m) + register_uris({'progress': ['course_progress']}, m) + progress = self.section.submissions_bulk_update(grade_data={ + '1': { + '1': { + 'posted_grade': 97 + }, + '2': { + 'posted_grade': 98 + } + } + }) + self.assertIsInstance(progress, Progress) + self.assertTrue(progress.context_type == "Course") + progress = progress.query() + self.assertTrue(progress.context_type == "Course") From ec997b14a24bad0e1022c86a4d9ce4904bf5094d Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Wed, 30 May 2018 15:02:21 -0400 Subject: [PATCH 82/91] Added Quiz Extensions endpoint and tests. --- canvasapi/quiz.py | 59 +++++++++++++++++++++++++++++++++++ tests/fixtures/quiz.json | 24 +++++++++++++++ tests/test_quiz.py | 66 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/canvasapi/quiz.py b/canvasapi/quiz.py index 73dcab62..c8607a76 100644 --- a/canvasapi/quiz.py +++ b/canvasapi/quiz.py @@ -183,6 +183,65 @@ def get_questions(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def set_extensions(self, quiz_extensions, **kwargs): + """ + Set extensions for student quiz submissions. + + :calls: `POST /api/v1/courses/:course_id/quizzes/:quiz_id/extensions + `_ + + :param quiz_extensions: List of dictionaries representing extensions. + :type quiz_extensions: list + + :rtype: list of :class:`canvasapi.quiz.QuizExtension` + + Example Usage: + + >>> quiz.set_extensions([ + ... { + ... 'user_id': 1, + ... 'extra_time': 60, + ... 'extra_attempts': 1 + ... }, + ... { + ... 'user_id': 2, + ... 'extra_attempts': 3 + ... }, + ... { + ... 'user_id': 3, + ... 'extra_time': 20 + ... } + ... ]) + """ + + if not isinstance(quiz_extensions, list) or not quiz_extensions: + raise ValueError('Param `quiz_extensions` must be a non-empty list.') + + if any(not isinstance(extension, dict) for extension in quiz_extensions): + raise ValueError('Param `quiz_extensions` must only contain dictionaries') + + if any('user_id' not in extension for extension in quiz_extensions): + raise RequiredFieldMissing( + 'Dictionaries in `quiz_extensions` must contain key `user_id`' + ) + + kwargs['quiz_extensions'] = quiz_extensions + + response = self._requester.request( + 'POST', + 'courses/{}/quizzes/{}/extensions'.format(self.course_id, self.id), + _kwargs=combine_kwargs(**kwargs) + ) + extension_list = response.json()['quiz_extensions'] + return [QuizExtension(self._requester, extension) for extension in extension_list] + + +@python_2_unicode_compatible +class QuizExtension(CanvasObject): + + def __str__(self): + return "{}-{}".format(self.quiz_id, self.user_id) + @python_2_unicode_compatible class QuizQuestion(CanvasObject): diff --git a/tests/fixtures/quiz.json b/tests/fixtures/quiz.json index 7f46da48..f3f32c3d 100644 --- a/tests/fixtures/quiz.json +++ b/tests/fixtures/quiz.json @@ -142,5 +142,29 @@ } ], "status_code": 200 + }, + "set_extensions": { + "method": "POST", + "endpoint": "courses/1/quizzes/1/extensions", + "data": { + "quiz_extensions": [ + { + "user_id": "1", + "quiz_id": "1", + "extra_attempts": null, + "extra_time": 60, + "manually_unlocked": null, + "end_at": null + }, + { + "user_id": "2", + "quiz_id": "1", + "extra_attempts": 3, + "extra_time": null, + "manually_unlocked": null, + "end_at": null + } + ] + } } } diff --git a/tests/test_quiz.py b/tests/test_quiz.py index 99a79354..8bece541 100644 --- a/tests/test_quiz.py +++ b/tests/test_quiz.py @@ -5,7 +5,7 @@ from canvasapi import Canvas from canvasapi.exceptions import RequiredFieldMissing -from canvasapi.quiz import Quiz, QuizQuestion +from canvasapi.quiz import Quiz, QuizQuestion, QuizExtension from canvasapi.quiz_group import QuizGroup from tests import settings from tests.util import register_uris @@ -150,6 +150,70 @@ def test_get_questions(self, m): self.assertTrue(hasattr(question_list[1], 'id')) self.assertEqual(question_list[1].id, 2) + # set_extensions() + def test_set_extensions(self, m): + register_uris({'quiz': ['set_extensions']}, m) + + extension = self.quiz.set_extensions([ + { + 'user_id': 1, + 'extra_time': 60 + }, + { + 'user_id': 2, + 'extra_attempts': 3 + } + ]) + + self.assertIsInstance(extension, list) + self.assertEqual(len(extension), 2) + + self.assertIsInstance(extension[0], QuizExtension) + self.assertEqual(extension[0].user_id, "1") + self.assertTrue(hasattr(extension[0], 'extra_time')) + self.assertEqual(extension[0].extra_time, 60) + + self.assertIsInstance(extension[1], QuizExtension) + self.assertEqual(extension[1].user_id, "2") + self.assertTrue(hasattr(extension[1], 'extra_attempts')) + self.assertEqual(extension[1].extra_attempts, 3) + + def test_set_extensions_not_list(self, m): + with self.assertRaises(ValueError): + self.quiz.set_extensions({'user_id': 1, 'extra_time': 60}) + + def test_set_extensions_empty_list(self, m): + with self.assertRaises(ValueError): + self.quiz.set_extensions([]) + + def test_set_extensions_non_dicts(self, m): + with self.assertRaises(ValueError): + self.quiz.set_extensions([('user_id', 1), ('extra_time', 60)]) + + def test_set_extensions_missing_key(self, m): + with self.assertRaises(RequiredFieldMissing): + self.quiz.set_extensions([{'extra_time': 60, 'extra_attempts': 3}]) + + +@requests_mock.Mocker() +class TestQuizExtension(unittest.TestCase): + def setUp(self): + self.canvas = Canvas(settings.BASE_URL, settings.API_KEY) + + self.extension = QuizExtension(self.canvas._Canvas__requester, { + 'user_id': 1, + 'quiz_id': 1, + 'extra_time': 60, + 'extra_attempts': 3, + 'manually_unlocked': None, + 'end_at': None + }) + + # __str__() + def test__str__(self, m): + string = str(self.extension) + self.assertIsInstance(string, str) + @requests_mock.Mocker() class TestQuizQuestion(unittest.TestCase): From 6eaa934e3ab6c4779f03c217de878a3d1cca9919 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Wed, 30 May 2018 15:13:03 -0400 Subject: [PATCH 83/91] Added Course Quiz Extensions endpoint and tests --- canvasapi/course.py | 53 ++++++++++++++++++++++++++++++++++++++ tests/fixtures/course.json | 24 +++++++++++++++++ tests/test_course.py | 46 ++++++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 1 deletion(-) diff --git a/canvasapi/course.py b/canvasapi/course.py index a8bb7407..2f29406f 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -11,6 +11,7 @@ from canvasapi.folder import Folder from canvasapi.page import Page from canvasapi.paginated_list import PaginatedList +from canvasapi.quiz import QuizExtension from canvasapi.tab import Tab from canvasapi.submission import Submission from canvasapi.upload import Uploader @@ -2143,6 +2144,58 @@ def get_migration_systems(self, **kwargs): _kwargs=combine_kwargs(**kwargs) ) + def set_quiz_extensions(self, quiz_extensions, **kwargs): + """ + Set extensions for student all quiz submissions in a course. + + :calls: `POST /api/v1/courses/:course_id/quizzes/:quiz_id/extensions + `_ + + :param quiz_extensions: List of dictionaries representing extensions. + :type quiz_extensions: list + + :rtype: list of :class:`canvasapi.quiz.QuizExtension` + + Example Usage: + + >>> course.set_quiz_extensions([ + ... { + ... 'user_id': 1, + ... 'extra_time': 60, + ... 'extra_attempts': 1 + ... }, + ... { + ... 'user_id': 2, + ... 'extra_attempts': 3 + ... }, + ... { + ... 'user_id': 3, + ... 'extra_time': 20 + ... } + ... ]) + """ + + if not isinstance(quiz_extensions, list) or not quiz_extensions: + raise ValueError('Param `quiz_extensions` must be a non-empty list.') + + if any(not isinstance(extension, dict) for extension in quiz_extensions): + raise ValueError('Param `quiz_extensions` must only contain dictionaries') + + if any('user_id' not in extension for extension in quiz_extensions): + raise RequiredFieldMissing( + 'Dictionaries in `quiz_extensions` must contain key `user_id`' + ) + + kwargs['quiz_extensions'] = quiz_extensions + + response = self._requester.request( + 'POST', + 'courses/{}/quiz_extensions'.format(self.id), + _kwargs=combine_kwargs(**kwargs) + ) + extension_list = response.json()['quiz_extensions'] + return [QuizExtension(self._requester, extension) for extension in extension_list] + @python_2_unicode_compatible class CourseNickname(CanvasObject): diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json index 2f30d675..01ee113d 100644 --- a/tests/fixtures/course.json +++ b/tests/fixtures/course.json @@ -1458,5 +1458,29 @@ } ], "status_code": 200 + }, + "set_quiz_extensions": { + "method": "POST", + "endpoint": "courses/1/quiz_extensions", + "data": { + "quiz_extensions": [ + { + "user_id": "1", + "quiz_id": "1", + "extra_attempts": null, + "extra_time": 60, + "manually_unlocked": null, + "end_at": null + }, + { + "user_id": "2", + "quiz_id": "1", + "extra_attempts": 3, + "extra_time": null, + "manually_unlocked": null, + "end_at": null + } + ] + } } } diff --git a/tests/test_course.py b/tests/test_course.py index e6e728d1..75a2c7fb 100644 --- a/tests/test_course.py +++ b/tests/test_course.py @@ -22,7 +22,7 @@ from canvasapi.group import Group, GroupCategory from canvasapi.module import Module from canvasapi.outcome import OutcomeGroup, OutcomeLink -from canvasapi.quiz import Quiz +from canvasapi.quiz import Quiz, QuizExtension from canvasapi.rubric import Rubric from canvasapi.section import Section from canvasapi.submission import Submission @@ -1387,6 +1387,50 @@ def test_get_migration_systems(self, m): self.assertEqual(migration_systems[1].requires_file_upload, False) self.assertEqual(migration_systems[1].name, "Dummy Importer 02") + # set_quiz_extensions + def test_set_quiz_extensions(self, m): + register_uris({'course': ['set_quiz_extensions']}, m) + + extension = self.course.set_quiz_extensions([ + { + 'user_id': 1, + 'extra_time': 60 + }, + { + 'user_id': 2, + 'extra_attempts': 3 + } + ]) + + self.assertIsInstance(extension, list) + self.assertEqual(len(extension), 2) + + self.assertIsInstance(extension[0], QuizExtension) + self.assertEqual(extension[0].user_id, "1") + self.assertTrue(hasattr(extension[0], 'extra_time')) + self.assertEqual(extension[0].extra_time, 60) + + self.assertIsInstance(extension[1], QuizExtension) + self.assertEqual(extension[1].user_id, "2") + self.assertTrue(hasattr(extension[1], 'extra_attempts')) + self.assertEqual(extension[1].extra_attempts, 3) + + def test_set_extensions_not_list(self, m): + with self.assertRaises(ValueError): + self.course.set_quiz_extensions({'user_id': 1, 'extra_time': 60}) + + def test_set_extensions_empty_list(self, m): + with self.assertRaises(ValueError): + self.course.set_quiz_extensions([]) + + def test_set_extensions_non_dicts(self, m): + with self.assertRaises(ValueError): + self.course.set_quiz_extensions([('user_id', 1), ('extra_time', 60)]) + + def test_set_extensions_missing_key(self, m): + with self.assertRaises(RequiredFieldMissing): + self.course.set_quiz_extensions([{'extra_time': 60, 'extra_attempts': 3}]) + @requests_mock.Mocker() class TestCourseNickname(unittest.TestCase): From 37553793a39073c382957c2839c9f70bc99d7562 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 10:43:17 -0400 Subject: [PATCH 84/91] (re-)added path manipulation to get imports to work for docval script. Made script python3 compatible by using .isroutine over .ismethod --- scripts/validate_docstrings.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/validate_docstrings.py b/scripts/validate_docstrings.py index 95859330..932d96d8 100644 --- a/scripts/validate_docstrings.py +++ b/scripts/validate_docstrings.py @@ -1,10 +1,15 @@ from __future__ import absolute_import, division, print_function, unicode_literals import inspect +import os import re +import sys + import requests -import canvasapi +sys.path.append(os.path.join(sys.path[0], '..')) + +import canvasapi # noqa def validate_method(themethod, quiet=False): @@ -44,7 +49,7 @@ def validate_docstring(method_string, call_line, quiet): 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( + print('{} docstring URL request returned {}'.format( method_string, html_doc_response.status_code )) @@ -140,7 +145,7 @@ def test_methods(): methods = set() for _, module in inspect.getmembers(canvasapi, inspect.ismodule): for _, theclass in inspect.getmembers(module, inspect.isclass): - for _, method in inspect.getmembers(theclass, inspect.ismethod): + for _, method in inspect.getmembers(theclass, inspect.isroutine): methods.add(method) for method_to_test in methods: validate_method(method_to_test) From 0ed58ec4175e0322b31aefd603fdb992fa3155f6 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 11:02:47 -0400 Subject: [PATCH 85/91] Set file in test to close --- tests/test_validate_docstrings.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_validate_docstrings.py b/tests/test_validate_docstrings.py index 570f698f..23bc6305 100644 --- a/tests/test_validate_docstrings.py +++ b/tests/test_validate_docstrings.py @@ -77,12 +77,8 @@ def register_doc_uri(url, m, code=200): return file_name = url_groups.group(2) - file = io.open( - 'tests/fixtures/{}.html'.format(file_name), - mode='r', - encoding='utf-8' - ) - data = file.read() + with io.open('tests/fixtures/{}.html'.format(file_name), 'r', encoding='utf-8') as file: + data = file.read() m.register_uri( 'GET', From 6030f6bcb9de01c536617f4832caf51473195260 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 12:12:29 -0400 Subject: [PATCH 86/91] Updated changelog. Added authors. --- AUTHORS.md | 6 ++++++ CHANGELOG.md | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/AUTHORS.md b/AUTHORS.md index 9106cbfe..8b1c0dba 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -20,14 +20,20 @@ Patches and Suggestions - David Warden [@dfwarden](https://github.com/dfwarden) - Devin Singh [@devints47](https://github.com/devints47) - Elise Heron [@thedarkestknight](https://github.com/thedarkestknight) +- Elli Howard [@qwertynerd97](https://github.com/qwertynerd97) +- Henry Acevedo [@Colombiangmr](https://github.com/Colombiangmr) - Ian Turgeon [@iturgeon](https://github.com/iturgeon) - [@jackrsteiner](https://github.com/jackrsteiner) - Jonathan Guilbe [@JonGuilbe](https://github.com/JonGuilbe) - John Raible [@rebelaide](https://github.com/rebelaide) +- Mark Lalor [@MarkLalor](https://github.com/MarkLalor) - Nathan Dabu [@nathaned](https://github.com/nathaned) - Philip Carter [@phillyc](https://github.com/phillyc) +- Ralph Baird [@rmanbaird](https://github.com/rmanbaird) - Sigurður Baldursson [@sigurdurb](https://github.com/sigurdurb) - Spencer Rogers [@spencer1248](https://github.com/spencer1248) - Stephen Woosley [@stephenwoosley](https://github.com/stephenwoosley) +- [@Tobiaqs](https://github.com/Tobiaqs) - Tuan Pham [@tuanvpham](https://github.com/tuanvpham) - William Funk [@WilliamRADFunk](https://github.com/WilliamRADFunk) +- William Wesley Monroe [@purpleHey](https://github.com/purpleHey) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dfb7292..04778d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Change Log +## [Unreleased] + +### New Endpoint Coverage + +- Content Migrations (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) +- Copy a File (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) +- List Announcements + +### General + +- Lots of docstring fixes. (Thanks, [@rmanbaird](https://github.com/rmanbaird)) + +### Deprecation Warnings + +- All methods starting with `list_` have been deprecated. Each has been replaced with a corresponding method starting with `list_`. For example, `Course.list_groups()` is now `Course.get_groups()`. The `list_` methods will be removed in a future release. (Thanks [@qwertynerd97](https://github.com/qwertynerd97) for doing the bulk of the grunt work.) +- `Course.update_tab()` is now deprecated. Use `Tab.update()` instead. + +### Bugfixes + +- Fixed a bug where taking a slice of a `PaginatedList` where the `start` was larger than the list caused an infinite loop. +- Fixed a typo that prevented `Assignment.submit()` from working properly. (Thanks, [@Tobiaqs](https://github.com/Tobiaqs)) + ## [0.9.0] - 2018-03-01 ### New Endpoint Coverage @@ -11,7 +33,7 @@ - Added example usage for several common endpoints to our documentation. - Updated `PaginatedList` to allow specification of the root element to build the list from when given an atypical JSON response (see [#146](https://github.com/ucfopen/canvasapi/issues/146)). (thanks [@dfwarden](https://github.com/dfwarden)) - Improved keyword argument support for `course.get_section()` (thanks [@andrew-gardener](https://github.com/andrew-gardener)) -- When uploading a file to a submission with `Submission.upload_comment`, it will automatically attached to a new comment. +- When uploading a file to a submission with `Submission.upload_comment()`, it will automatically attached to a new comment. ### Deprecation Warnings From 4b24ddd79386e47e76623451ae33ca86a10a936f Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 12:21:22 -0400 Subject: [PATCH 87/91] Added quiz extensions to changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04778d74..16219b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ - Content Migrations (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) - Copy a File (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) -- List Announcements +- Course Quiz Extensions +- List Announcements (Thanks, [@rmanbaird](https://github.com/rmanbaird)) +- Quiz Extensions ### General From d13a1c60c581a29cff5b2653a29f8185e68317fd Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Fri, 1 Jun 2018 16:57:56 -0400 Subject: [PATCH 88/91] corrected API endpoint, condensed return lines --- canvasapi/assignment.py | 4 +--- canvasapi/course.py | 4 +--- canvasapi/section.py | 6 ++---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py index 77871a9b..ea0078a9 100644 --- a/canvasapi/assignment.py +++ b/canvasapi/assignment.py @@ -164,9 +164,7 @@ def submissions_bulk_update(self, **kwargs): ), _kwargs=combine_kwargs(**kwargs) ) - response_json = response.json() - progress = Progress(self._requester, response_json) - return progress + return Progress(self._requester, response.json()) @python_2_unicode_compatible diff --git a/canvasapi/course.py b/canvasapi/course.py index 8fa8c9bc..a001cc50 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -2161,9 +2161,7 @@ def submissions_bulk_update(self, **kwargs): ), _kwargs=combine_kwargs(**kwargs) ) - response_json = response.json() - progress = Progress(self._requester, response_json) - return progress + return Progress(self._requester, response.json()) @python_2_unicode_compatible diff --git a/canvasapi/section.py b/canvasapi/section.py index 6070ec7c..f33e9908 100644 --- a/canvasapi/section.py +++ b/canvasapi/section.py @@ -380,7 +380,7 @@ def submissions_bulk_update(self, **kwargs): Update the grading and comments on multiple student's assignment submissions in an asynchronous job. - :calls: POST /api/v1/courses/:course_id/submissions/update_grades \ + :calls: POST /api/v1/sections/:section_id/submissions/update_grades \ `_ :rtype: :class:`canvasapi.progress.Progress` @@ -392,6 +392,4 @@ def submissions_bulk_update(self, **kwargs): ), _kwargs=combine_kwargs(**kwargs) ) - response_json = response.json() - progress = Progress(self._requester, response_json) - return progress + return Progress(self._requester, response.json()) From 1a41966592799b6bf40a3840021c28187c7cfc7d Mon Sep 17 00:00:00 2001 From: Ralph Baird Date: Fri, 1 Jun 2018 17:09:48 -0400 Subject: [PATCH 89/91] corrected docstring error --- canvasapi/assignment.py | 2 +- canvasapi/course.py | 2 +- canvasapi/section.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py index ea0078a9..03d1b56b 100644 --- a/canvasapi/assignment.py +++ b/canvasapi/assignment.py @@ -150,7 +150,7 @@ def submissions_bulk_update(self, **kwargs): Update the grading and comments on multiple student's assignment submissions in an asynchronous job. - :calls: POST /api/v1/courses/:course_id/assignments/:assignment_id/ \ + :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/ \ submissions/update_grades \ `_ diff --git a/canvasapi/course.py b/canvasapi/course.py index 0583cd9a..5a6e6aa8 100644 --- a/canvasapi/course.py +++ b/canvasapi/course.py @@ -2149,7 +2149,7 @@ def submissions_bulk_update(self, **kwargs): Update the grading and comments on multiple student's assignment submissions in an asynchronous job. - :calls: POST /api/v1/courses/:course_id/submissions/update_grades \ + :calls: `POST /api/v1/courses/:course_id/submissions/update_grades \ `_ :rtype: :class:`canvasapi.progress.Progress` diff --git a/canvasapi/section.py b/canvasapi/section.py index f33e9908..0d227369 100644 --- a/canvasapi/section.py +++ b/canvasapi/section.py @@ -380,7 +380,7 @@ def submissions_bulk_update(self, **kwargs): Update the grading and comments on multiple student's assignment submissions in an asynchronous job. - :calls: POST /api/v1/sections/:section_id/submissions/update_grades \ + :calls: `POST /api/v1/sections/:section_id/submissions/update_grades \ `_ :rtype: :class:`canvasapi.progress.Progress` From a368bef5c4d284bb5bf32080bf4c5b226ae8410d Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 17:16:27 -0400 Subject: [PATCH 90/91] Added grade/comment on multiple subs to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04778d74..591932b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Content Migrations (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) - Copy a File (Thanks, [@qwertynerd97](https://github.com/qwertynerd97)) +- Grade/Comment on Multiple Submissions (Thanks, [@rmanbaird](https://github.com/rmanbaird)) - List Announcements ### General From b33c1b221595c806273088d16f16fe751cfc58f9 Mon Sep 17 00:00:00 2001 From: Matthew Emond Date: Fri, 1 Jun 2018 17:28:15 -0400 Subject: [PATCH 91/91] Version 0.10.0 --- CHANGELOG.md | 2 +- canvasapi/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6538d916..da8d0591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased] +## [0.10.0] - 2018-06-01 ### New Endpoint Coverage diff --git a/canvasapi/__init__.py b/canvasapi/__init__.py index 3d7c7c78..48442bab 100644 --- a/canvasapi/__init__.py +++ b/canvasapi/__init__.py @@ -6,4 +6,4 @@ __all__ = ["Canvas"] -__version__ = '0.9.0' +__version__ = '0.10.0'