Repository: ucfopen/canvasapi
Pull request #52 (2017-08-15 14:42:40)

Release v0.6.0
Added support for SIS IDs to get accounts, courses, groups and sections.



Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index 61cf082..a73a544 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -9,11 +9,13 @@ Patches and Suggestions
 =======================
 - Adrian Goetz [@a-goetz](https://github.com/a-goetz)
 - Anthony Rodriguez [@AnthonyRodriguez726](https://github.com/AnthonyRodriguez726)
+- Daniel Grobani [@dgrobani](https://github.com/dgrobani)
 - Devin Singh [@devints47](https://github.com/devints47)
 - Elise Heron [@thedarkestknight](https://github.com/thedarkestknight)
 - Ian Turgeon [@iturgeon](https://github.com/iturgeon)
 - John Raible [@rebelaide](https://github.com/rebelaide)
 - Nathan Dabu [@nathaned](https://github.com/nathaned)
 - Philip Carter [@phillyc](https://github.com/phillyc)
+- Sigurður Baldursson [@sigurdurb](https://github.com/sigurdurb)
 - Tuan Pham [@tuanvpham](https://github.com/tuanvpham)
 - William Funk [@WilliamRADFunk](https://github.com/WilliamRADFunk)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b865d34..3c18044 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Change Log
 
+## [0.6.0] - 2017-08-15
+### General
+- Added support for SIS IDs to get accounts, courses, groups and sections. (Thanks for the suggestion, [@sigurdurb](https://github.com/sigurdurb)!)
+
 ## [0.5.1] - 2017-08-02
 ### General
 - Moved documentation to [Read the Docs](http://canvasapi.readthedocs.io).
@@ -107,6 +111,7 @@
 - Fixed some incorrectly defined parameters
 - Fixed an issue where tests would fail due to an improperly configured requires block
 
+[0.6.0]: https://github.com/ucfopen/canvasapi/compare/v0.5.1...v0.6.0
 [0.5.1]: https://github.com/ucfopen/canvasapi/compare/v0.5.0...v0.5.1
 [0.5.0]: https://github.com/ucfopen/canvasapi/compare/v0.4.0...v0.5.0
 [0.4.0]: https://github.com/ucfopen/canvasapi/compare/v0.3.0...v0.4.0
diff --git a/README.md b/README.md
index 9557efd..267fa2c 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,6 @@
 [![CanvasAPI on PyPI](https://img.shields.io/pypi/v/canvasapi.svg)](https://pypi.python.org/pypi/canvasapi)
 [![License](https://img.shields.io/pypi/l/canvasapi.svg)](https://pypi.python.org/pypi/canvasapi)
+[![Python Versions](https://img.shields.io/pypi/pyversions/canvasapi.svg)](https://pypi.python.org/pypi/canvasapi)
 [![Documentation Status](https://readthedocs.org/projects/canvasapi/badge/?version=latest)](http://canvasapi.readthedocs.io/en/latest/?badge=latest)
 [![Build Status](https://travis-ci.org/ucfopen/canvasapi.svg?branch=master)](https://travis-ci.org/ucfopen/canvasapi)
 [![Coverage Status](https://coveralls.io/repos/github/ucfopen/canvasapi/badge.svg?branch=master)](https://coveralls.io/github/ucfopen/canvasapi?branch=master)
diff --git a/canvasapi/__init__.py b/canvasapi/__init__.py
index fd07b62..71c86bc 100644
--- a/canvasapi/__init__.py
+++ b/canvasapi/__init__.py
@@ -6,4 +6,4 @@ from canvasapi.canvas import Canvas
 
 __all__ = ["Canvas"]
 
-__version__ = '0.5.1'
+__version__ = '0.6.0'
diff --git a/canvasapi/canvas.py b/canvasapi/canvas.py
index c295820..64c74a4 100644
--- a/canvasapi/canvas.py
+++ b/canvasapi/canvas.py
@@ -8,6 +8,7 @@ from canvasapi.folder import Folder
 from canvasapi.group import Group, GroupCategory
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.requester import Requester
+from canvasapi.section import Section
 from canvasapi.user import User
 from canvasapi.util import combine_kwargs
 
@@ -42,7 +43,7 @@ class Canvas(object):
         )
         return Account(self.__requester, response.json())
 
-    def get_account(self, account_id):
+    def get_account(self, account_id, use_sis_id=False, **kwargs):
         """
         Retrieve information on an individual account.
 
@@ -50,12 +51,22 @@ class Canvas(object):
         <https://canvas.instructure.com/doc/api/accounts.html#method.accounts.show>`_
 
         :param account_id: The ID of the account to retrieve.
-        :type account_id: int
+        :type account_id: int or str
+        :param use_sis_id: Whether or not account_id is an sis ID.
+            Defaults to `False`.
+        :type use_sis_id: bool
+
         :rtype: :class:`canvasapi.account.Account`
         """
+        if use_sis_id:
+            uri_str = 'accounts/sis_account_id:{}'
+        else:
+            uri_str = 'accounts/{}'
+
         response = self.__requester.request(
             'GET',
-            'accounts/%s' % (account_id)
+            uri_str.format(account_id),
+            **combine_kwargs(**kwargs)
         )
         return Account(self.__requester, response.json())
 
@@ -102,7 +113,7 @@ class Canvas(object):
             'course_accounts',
         )
 
-    def get_course(self, course_id, **kwargs):
+    def get_course(self, course_id, use_sis_id=False, **kwargs):
         """
         Retrieve a course by its ID.
 
@@ -110,12 +121,20 @@ class Canvas(object):
         <https://canvas.instructure.com/doc/api/courses.html#method.courses.show>`_
 
         :param course_id: The ID of the course to retrieve.
-        :type course_id: int
+        :type course_id: int or str
+        :param use_sis_id: Whether or not course_id is an sis ID.
+            Defaults to `False`.
+        :type use_sis_id: bool
         :rtype: :class:`canvasapi.course.Course`
         """
+        if use_sis_id:
+            uri_str = 'courses/sis_course_id:{}'
+        else:
+            uri_str = 'courses/{}'
+
         response = self.__requester.request(
             'GET',
-            'courses/%s' % (course_id),
+            uri_str.format(course_id),
             **combine_kwargs(**kwargs)
         )
         return Course(self.__requester, response.json())
@@ -251,19 +270,30 @@ class Canvas(object):
         )
         return CourseNickname(self.__requester, response.json())
 
-    def get_section(self, section_id):
+    def get_section(self, section_id, use_sis_id=False, **kwargs):
         """
         Get details about a specific section.
 
         :calls: `GET /api/v1/sections/:id \
         <https://canvas.instructure.com/doc/api/sections.html#method.sections.show>`_
 
+        :param section_id: The ID of the section to get.
+        :type section_id: int or str
+        :param use_sis_id: Whether or not section_id is an sis ID.
+            Defaults to `False`.
+        :type use_sis_id: bool
+
         :rtype: :class:`canvasapi.section.Section`
         """
-        from canvasapi.section import Section
+        if use_sis_id:
+            uri_str = 'sections/sis_section_id:{}'
+        else:
+            uri_str = 'sections/{}'
+
         response = self.__requester.request(
             'GET',
-            'sections/%s' % (section_id)
+            uri_str.format(section_id),
+            **combine_kwargs(**kwargs)
         )
         return Section(self.__requester, response.json())
 
@@ -340,7 +370,7 @@ class Canvas(object):
         )
         return Group(self.__requester, response.json())
 
-    def get_group(self, group_id, **kwargs):
+    def get_group(self, group_id, use_sis_id=False, **kwargs):
         """
         Return the data for a single group. If the caller does not
         have permission to view the group a 401 will be returned.
@@ -348,11 +378,22 @@ class Canvas(object):
         :calls: `GET /api/v1/groups/:group_id \
         <https://canvas.instructure.com/doc/api/groups.html#method.groups.show>`_
 
+        :param group_id: The ID of the group to get.
+        :type group_id: int or str
+        :param use_sis_id: Whether or not group_id is an sis ID.
+            Defaults to `False`.
+        :type use_sis_id: bool
+
         :rtype: :class:`canvasapi.group.Group`
         """
+        if use_sis_id:
+            uri_str = 'groups/sis_group_id:{}'
+        else:
+            uri_str = 'groups/{}'
+
         response = self.__requester.request(
             'GET',
-            'groups/%s' % (group_id),
+            uri_str.format(group_id),
             **combine_kwargs(**kwargs)
         )
         return Group(self.__requester, response.json())


Test patch:
diff --git a/tests/fixtures/account.json b/tests/fixtures/account.json
index f8e0456..de829c0 100644
--- a/tests/fixtures/account.json
+++ b/tests/fixtures/account.json
@@ -146,6 +146,21 @@
 		},
 		"status_code": 200
 	},
+	"get_by_sis_id": {
+		"method": "GET",
+		"endpoint": "accounts/sis_account_id:test-sis-id",
+		"data": {
+			"id": 10,
+			"sis_account_id": "test-sis-id",
+			"name": "Account From SIS",
+			"parent_account_id": null,
+			"root_account_id": null,
+			"default_storage_quota_mb": 500,
+			"default_user_storage_quota_mb": 50,
+			"default_group_storage_quota_mb": 50,
+			"default_time_zone": "America/New_York"
+		}
+	},
 	"get_courses": {
 		"method": "GET",
 		"endpoint": "accounts/1/courses",
diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json
index 4a879bc..7ee017d 100644
--- a/tests/fixtures/course.json
+++ b/tests/fixtures/course.json
@@ -159,6 +159,22 @@
 		},
 		"status_code": 200
 	},
+	"get_by_sis_id": {
+		"method": "GET",
+		"endpoint": "courses/sis_course_id:test-sis-id",
+		"data": {
+			"id": 1,
+			"sis_course_id": "test-sis-id",
+			"course_code": "SIS101",
+			"name": "SIS Course",
+			"workflow_state": "available",
+			"account_id": 1,
+			"root_account_id": 1,
+			"enrollment_term_id": 1,
+			"grading_standard_id": 1
+		},
+		"status_code": 200
+	},
 	"get_external_tools": {
 		"method": "GET",
 		"endpoint": "courses/1/external_tools",
diff --git a/tests/fixtures/group.json b/tests/fixtures/group.json
index 0481a4c..6e542af 100644
--- a/tests/fixtures/group.json
+++ b/tests/fixtures/group.json
@@ -19,6 +19,17 @@
 		},
 		"status_code": 200
 	},
+	"get_by_sis_id": {
+		"method": "GET",
+		"endpoint": "groups/sis_group_id:test-sis-id",
+		"data": {
+			"id": 10,
+			"sis_group_id": "test-sis-id",
+			"name": "SIS Group",
+			"description": "best SIS group ever"
+		},
+		"status_code": 200
+	},
 	"pages_get_page": {
 		"method": "GET",
 		"endpoint": "groups/1/pages/my-url",
diff --git a/tests/fixtures/section.json b/tests/fixtures/section.json
index 6d06bcd..19c860b 100644
--- a/tests/fixtures/section.json
+++ b/tests/fixtures/section.json
@@ -9,6 +9,17 @@
 		},
 		"status_code": 200
 	},
+	"get_by_sis_id": {
+		"method": "GET",
+		"endpoint": "sections/sis_section_id:test-sis-id",
+		"data":{
+			"id": 10,
+			"sis_section_id": "test-sis-id",
+			"course_id": 1,
+			"name": "SIS Section"
+		},
+		"status_code": 200
+	},
 	"list_enrollments": {
 		"method": "GET",
 		"endpoint": "sections/1/enrollments",
diff --git a/tests/test_canvas.py b/tests/test_canvas.py
index 17cc875..e2acf32 100644
--- a/tests/test_canvas.py
+++ b/tests/test_canvas.py
@@ -51,6 +51,14 @@ class TestCanvas(unittest.TestCase):
 
         self.assertIsInstance(account, Account)
 
+    def test_get_account_sis_id(self, m):
+        register_uris({'account': ['get_by_sis_id']}, m)
+
+        account = self.canvas.get_account('test-sis-id', use_sis_id=True)
+
+        self.assertIsInstance(account, Account)
+        self.assertEqual(account.name, 'Account From SIS')
+
     def test_get_account_fail(self, m):
         register_uris({'generic': ['not_found']}, m)
 
@@ -82,6 +90,14 @@ class TestCanvas(unittest.TestCase):
         self.assertIsInstance(course, Course)
         self.assertTrue(hasattr(course, 'name'))
 
+    def test_get_course_sis_id(self, m):
+        register_uris({'course': ['get_by_sis_id']}, m)
+
+        course = self.canvas.get_course('test-sis-id', use_sis_id=True)
+
+        self.assertIsInstance(course, Course)
+        self.assertEqual(course.name, 'SIS Course')
+
     def test_get_course_with_start_date(self, m):
         register_uris({'course': ['start_at_date']}, m)
 
@@ -225,6 +241,14 @@ class TestCanvas(unittest.TestCase):
 
         self.assertIsInstance(info, Section)
 
+    def test_get_section_sis_id(self, m):
+        register_uris({'section': ['get_by_sis_id']}, m)
+
+        section = self.canvas.get_section('test-sis-id', use_sis_id=True)
+
+        self.assertIsInstance(section, Section)
+        self.assertEqual(section.name, 'SIS Section')
+
     # create_group()
     def test_create_group(self, m):
         register_uris({'group': ['create']}, m)
@@ -245,6 +269,14 @@ class TestCanvas(unittest.TestCase):
         self.assertTrue(hasattr(group, 'name'))
         self.assertTrue(hasattr(group, 'description'))
 
+    def test_get_group_sis_id(self, m):
+        register_uris({'group': ['get_by_sis_id']}, m)
+
+        group = self.canvas.get_group('test-sis-id', use_sis_id=True)
+
+        self.assertIsInstance(group, Group)
+        self.assertEqual(group.name, 'SIS Group')
+
     # get_group_category()
     def test_get_group_category(self, m):
         register_uris({'group': ['get_category_by_id']}, m)