Repository: ucfopen/canvasapi

Issue:
Submission methods on wrong class
Was working with assignment submissions recently and discovered that many of the methods relating to submissions are in the wrong class. For example, `list_submissions`appears in the `Course` and `Section` classes, but should probably just be in the `Assignment` class.

Need to move them to the appropriate class.

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index 3f01729..51f296b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 language: python
 python:
   - 2.7
-  - 3.3
   - 3.4
   - 3.5
   - 3.6
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1af344a..243fbaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,18 @@
 # Change Log
 
+## Unreleased
+
+### Deprecation Warnings
+
+- **_Dropped support for Python 3.3_**
+    - [Python 3.3 is end-of-life as of September 2017](https://www.python.org/dev/peps/pep-0398/#lifespan)
+    - Should continue to function in 3.3, but compatibility cannot be guaranteed going forward.
+- Several methods in the `Course` and `Section` classes relating to assignments and submissions have been deprecated.
+    - Comparable methods have been implemented in the `Assignment` and `Submission` classes, as appropriate.
+    - The deprecated methods now include a warning in the documentation with reference to the replacement. Additionally, the deprecated methods will raise a `DeprecationWarning`.
+    - These methods will be removed in a future release.
+- `Course.list_sections()` has been deprecated. Use `Course.get_sections()` instead.
+
 ## [0.8.2] - 2018-01-24
 
 ### Bugfixes
diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py
index 90facfc..3dcec1d 100644
--- a/canvasapi/assignment.py
+++ b/canvasapi/assignment.py
@@ -3,7 +3,11 @@ from __future__ import absolute_import, division, print_function, unicode_litera
 from six import python_2_unicode_compatible
 
 from canvasapi.canvas_object import CanvasObject
-from canvasapi.util import combine_kwargs
+from canvasapi.exceptions import RequiredFieldMissing
+from canvasapi.paginated_list import PaginatedList
+from canvasapi.submission import Submission
+from canvasapi.user import UserDisplay
+from canvasapi.util import combine_kwargs, obj_or_id
 
 
 @python_2_unicode_compatible
@@ -12,7 +16,7 @@ class Assignment(CanvasObject):
     def __str__(self):
         return "{} ({})".format(self.name, self.id)
 
-    def delete(self):
+    def delete(self, **kwargs):
         """
         Delete this assignment.
 
@@ -24,6 +28,7 @@ class Assignment(CanvasObject):
         response = self._requester.request(
             'DELETE',
             'courses/{}/assignments/{}'.format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs)
         )
         return Assignment(self._requester, response.json())
 
@@ -47,6 +52,98 @@ class Assignment(CanvasObject):
 
         return Assignment(self._requester, response.json())
 
+    def get_gradeable_students(self, **kwargs):
+        """
+        List students eligible to submit the assignment.
+
+        :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/gradeable_students  \
+        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.gradeable_students>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.user.UserDisplay`
+        """
+        return PaginatedList(
+            UserDisplay,
+            self._requester,
+            'GET',
+            'courses/{}/assignments/{}/gradeable_students'.format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs)
+        )
+
+    def get_submission(self, user, **kwargs):
+        """
+        Get a single submission, based on user id.
+
+        :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id \
+        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.show>`_
+
+        :param user: The object or ID of the related user
+        :type user: :class:`canvasapi.user.User` or int
+
+        :rtype: :class:`canvasapi.submission.Submission`
+        """
+        from canvasapi.user import User
+
+        user_id = obj_or_id(user, "user", (User,))
+
+        response = self._requester.request(
+            'GET',
+            'courses/{}/assignments/{}/submissions/{}'.format(self.course_id, self.id, user_id),
+            _kwargs=combine_kwargs(**kwargs)
+        )
+        response_json = response.json()
+        response_json.update(course_id=self.course_id)
+
+        return Submission(self._requester, response_json)
+
+    def get_submissions(self, **kwargs):
+        """
+        Get all existing submissions for this assignment.
+
+        :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions  \
+        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.index>`_
+
+        :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
+            :class:`canvasapi.submission.Submission`
+        """
+        return PaginatedList(
+            Submission,
+            self._requester,
+            'GET',
+            'courses/{}/assignments/{}/submissions'.format(self.course_id, self.id),
+            {'course_id': self.course_id},
+            _kwargs=combine_kwargs(**kwargs)
+        )
+
+    def submit(self, submission, **kwargs):
+        """
+        Makes a submission for an assignment.
+
+        :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/submissions \
+        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions.create>`_
+
+        :param submission: The attributes of the submission.
+        :type submission: dict
+
+        :rtype: :class:`canvasapi.submission.Submission`
+        """
+        if isinstance(submission, dict) and 'submission_type' in submission:
+            kwargs['submision'] = submission
+        else:
+            raise RequiredFieldMissing(
+                "Dictionary with key 'submission_type' is required."
+            )
+
+        response = self._requester.request(
+            'POST',
+            'courses/{}/assignments/{}/submissions'.format(self.course_id, self.id),
+            _kwargs=combine_kwargs(**kwargs)
+        )
+        response_json = response.json()
+        response_json.update(course_id=self.course_id)
+
+        return Submission(self._requester, response_json)
+
 
 @python_2_unicode_compatible
 class AssignmentGroup(CanvasObject):
diff --git a/canvasapi/course.py b/canvasapi/course.py
index df55c63..73c55e6 100644
--- a/canvasapi/course.py
+++ b/canvasapi/course.py
@@ -14,7 +14,6 @@ from canvasapi.paginated_list import PaginatedList
 from canvasapi.tab import Tab
 from canvasapi.submission import Submission
 from canvasapi.upload import Uploader
-from canvasapi.user import UserDisplay
 from canvasapi.util import combine_kwargs, is_multivalued, obj_or_id
 from canvasapi.rubric import Rubric
 
@@ -25,7 +24,7 @@ warnings.simplefilter('always', DeprecationWarning)
 class Course(CanvasObject):
 
     def __str__(self):
-        return "{} {} ({})".format(self.course_code, self.name, self.id)
+        return '{} {} ({})'.format(self.course_code, self.name, self.id)
 
     def conclude(self):
         """
@@ -698,6 +697,10 @@ class Course(CanvasObject):
         """
         Returns the list of sections for this course.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.course.Course.get_sections` instead.
+
         :calls: `GET /api/v1/courses/:course_id/sections \
         <https://canvas.instructure.com/doc/api/sections.html#method.sections.index>`_
 
@@ -1154,6 +1157,10 @@ class Course(CanvasObject):
         """
         Makes a submission for an assignment.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.submit` instead.
+
         :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/submissions \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions.create>`_
 
@@ -1167,29 +1174,29 @@ class Course(CanvasObject):
         """
         from canvasapi.assignment import Assignment
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        warnings.warn(
+            'Course.submit_assignment() is deprecated and will be removed in '
+            'the future. Use Assignment.submit() instead.',
+            DeprecationWarning
+        )
 
-        if isinstance(submission, dict) and 'submission_type' in submission:
-            kwargs['submision'] = submission
-        else:
-            raise RequiredFieldMissing(
-                "Dictionary with key 'submission_type' is required."
-            )
+        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
 
-        response = self._requester.request(
-            'POST',
-            'courses/{}/assignments/{}/submissions'.format(self.id, assignment_id),
-            _kwargs=combine_kwargs(**kwargs)
+        assignment = Assignment(
+            self._requester,
+            {'course_id': self.id, 'id': assignment_id}
         )
-        response_json = response.json()
-        response_json.update(course_id=self.id)
 
-        return Submission(self._requester, response_json)
+        return assignment.submit(submission, **kwargs)
 
     def list_submissions(self, assignment, **kwargs):
         """
         Get all existing submissions for an assignment.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.get_submissions` instead.
+
         :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions  \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.index>`_
 
@@ -1201,17 +1208,21 @@ class Course(CanvasObject):
         """
         from canvasapi.assignment import Assignment
 
+        warnings.warn(
+            'Course.list_submissions() is deprecated and will be removed in '
+            'the future. Use Assignment.get_submissions() instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
 
-        return PaginatedList(
-            Submission,
+        assignment = Assignment(
             self._requester,
-            'GET',
-            'courses/{}/assignments/{}/submissions'.format(self.id, assignment_id),
-            {'course_id': self.id},
-            _kwargs=combine_kwargs(**kwargs)
+            {'course_id': self.id, 'id': assignment_id}
         )
 
+        return assignment.get_submissions(**kwargs)
+
     def list_multiple_submissions(self, **kwargs):
         """
         List submissions for multiple assignments.
@@ -1240,6 +1251,10 @@ class Course(CanvasObject):
         """
         Get a single submission, based on user id.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.get_submission` instead.
+
         :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.show>`_
 
@@ -1251,25 +1266,30 @@ class Course(CanvasObject):
         :rtype: :class:`canvasapi.submission.Submission`
         """
         from canvasapi.assignment import Assignment
-        from canvasapi.user import User
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
-        user_id = obj_or_id(user, "user", (User,))
+        warnings.warn(
+            '`Course.get_submission()` is deprecated and will be removed in a '
+            'future version. Use `Assignment.get_submission()` instead',
+            DeprecationWarning
+        )
 
-        response = self._requester.request(
-            'GET',
-            'courses/{}/assignments/{}/submissions/{}'.format(self.id, assignment_id, user_id),
-            _kwargs=combine_kwargs(**kwargs)
+        assignment_id = obj_or_id(assignment, 'assignment', (Assignment,))
+
+        assignment = Assignment(
+            self._requester,
+            {'course_id': self.id, 'id': assignment_id}
         )
-        response_json = response.json()
-        response_json.update(course_id=self.id)
 
-        return Submission(self._requester, response_json)
+        return assignment.get_submission(user, **kwargs)
 
     def update_submission(self, assignment, user, **kwargs):
         """
         Comment on and/or update the grading for a student's assignment submission.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.edit` instead.
+
         :calls: `PUT /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.update>`_
 
@@ -1283,29 +1303,31 @@ class Course(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
-        user_id = obj_or_id(user, "user", (User,))
-
-        response = self._requester.request(
-            'PUT',
-            'courses/{}/assignments/{}/submissions/{}'.format(self.id, assignment_id, user_id),
-            _kwargs=combine_kwargs(**kwargs)
+        warnings.warn(
+            '`Course.update_submission()` is deprecated and will be removed in a '
+            'future version. Use `Submission.edit()` instead',
+            DeprecationWarning
         )
 
-        response_json = response.json()
-        response_json.update(course_id=self.id)
-
-        submission = self.get_submission(assignment_id, user_id)
+        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        user_id = obj_or_id(user, "user", (User,))
 
-        if 'submission_type' in response_json:
-            super(Submission, submission).set_attributes(response_json)
+        submission = Submission(self._requester, {
+            'course_id': self.id,
+            'assignment_id': assignment_id,
+            'user_id': user_id,
+        })
 
-        return Submission(self._requester, response_json)
+        return submission.edit(**kwargs)
 
-    def list_gradeable_students(self, assignment):
+    def list_gradeable_students(self, assignment, **kwargs):
         """
         List students eligible to submit the assignment.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.get_gradeable_students` instead.
+
         :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/gradeable_students  \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.gradeable_students>`_
 
@@ -1313,23 +1335,33 @@ class Course(CanvasObject):
         :type assignment: :class:`canvasapi.assignment.Assignment` or int
 
         :rtype: :class:`canvasapi.paginated_list.PaginatedList` of
-            :class:`canvasapi.user.User`
+            :class:`canvasapi.user.UserDisplay`
         """
         from canvasapi.assignment import Assignment
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        warnings.warn(
+            '`Course.list_gradeable_students()` is deprecated and will be '
+            'removed in a future version. Use '
+            '`Assignment.get_gradeable_students()` instead.',
+            DeprecationWarning
+        )
 
-        return PaginatedList(
-            UserDisplay,
+        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        assignment = Assignment(
             self._requester,
-            'GET',
-            'courses/{}/assignments/{}/gradeable_students'.format(self.id, assignment_id)
+            {'id': assignment_id, 'course_id': self.id}
         )
 
-    def mark_submission_as_read(self, assignment, user):
+        return assignment.get_gradeable_students(**kwargs)
+
+    def mark_submission_as_read(self, assignment, user, **kwargs):
         """
         Mark submission as read. No request fields are necessary.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.mark_read` instead.
+
         :calls: `PUT
             /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id/read \
             <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_read>`_
@@ -1344,23 +1376,30 @@ class Course(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
+        warnings.warn(
+            '`Course.mark_submission_as_read()` is deprecated and will be '
+            'removed in a future version. Use `Submission.mark_read()` instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
         user_id = obj_or_id(user, "user", (User,))
 
-        response = self._requester.request(
-            'PUT',
-            'courses/{}/assignments/{}/submissions/{}/read'.format(
-                self.id,
-                assignment_id,
-                user_id,
-            )
-        )
-        return response.status_code == 204
+        submission = Submission(self._requester, {
+            'course_id': self.id,
+            'assignment_id': assignment_id,
+            'user_id': user_id
+        })
+        return submission.mark_read(**kwargs)
 
-    def mark_submission_as_unread(self, assignment, user):
+    def mark_submission_as_unread(self, assignment, user, **kwargs):
         """
         Mark submission as unread. No request fields are necessary.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.mark_unread` instead.
+
         :calls: `DELETE
             /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id/read \
             <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_unread>`_
@@ -1375,18 +1414,21 @@ class Course(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
+        warnings.warn(
+            '`Course.mark_submission_as_unread()` is deprecated and will be '
+            'removed in a future version. Use `Submission.mark_unread()` instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
         user_id = obj_or_id(user, "user", (User,))
 
-        response = self._requester.request(
-            'DELETE',
-            'courses/{}/assignments/{}/submissions/{}/read'.format(
-                self.id,
-                assignment_id,
-                user_id,
-            ),
-        )
-        return response.status_code == 204
+        submission = Submission(self._requester, {
+            'course_id': self.id,
+            'assignment_id': assignment_id,
+            'user_id': user_id
+        })
+        return submission.mark_unread(**kwargs)
 
     def list_external_feeds(self):
         """
@@ -1781,7 +1823,7 @@ class Course(CanvasObject):
         """
 
         response = self._requester.request(
-            "GET",
+            'GET',
             'courses/%s/grading_standards/%d' % (self.id, grading_standard_id),
             _kwargs=combine_kwargs(**kwargs)
         )
diff --git a/canvasapi/section.py b/canvasapi/section.py
index f946006..707bb60 100644
--- a/canvasapi/section.py
+++ b/canvasapi/section.py
@@ -1,14 +1,15 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
-from warnings import warn
+import warnings
 
 from six import python_2_unicode_compatible
 
 from canvasapi.canvas_object import CanvasObject
-from canvasapi.exceptions import RequiredFieldMissing
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.submission import Submission
 from canvasapi.util import combine_kwargs, obj_or_id
 
+warnings.simplefilter('always', DeprecationWarning)
+
 
 @python_2_unicode_compatible
 class Section(CanvasObject):
@@ -111,6 +112,10 @@ class Section(CanvasObject):
         """
         Makes a submission for an assignment.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.submit` instead.
+
         :calls: `POST /api/v1/sections/:section_id/assignments/:assignment_id/submissions \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions.create>`_
 
@@ -123,29 +128,28 @@ class Section(CanvasObject):
         """
         from canvasapi.assignment import Assignment
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
-
-        if isinstance(submission, dict) and 'submission_type' in submission:
-            kwargs['submision'] = submission
-        else:
-            raise RequiredFieldMissing(
-                "Dictionary with key 'submission_type' is required."
-            )
-
-        response = self._requester.request(
-            'POST',
-            'sections/{}/assignments/{}/submissions'.format(self.id, assignment_id),
-            _kwargs=combine_kwargs(**kwargs)
+        warnings.warn(
+            'Section.submit_assignment() is deprecated and will be removed '
+            'in the future. Use Assignment.submit() instead.',
+            DeprecationWarning
         )
-        response_json = response.json()
-        response_json.update(section_id=self.id)
 
-        return Submission(self._requester, response_json)
+        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        assignment = Assignment(self._requester, {
+            'course_id': self.course_id,
+            'section_id': self.id,
+            'id': assignment_id
+        })
+        return assignment.submit(submission, **kwargs)
 
     def list_submissions(self, assignment, **kwargs):
         """
         Get all existing submissions for an assignment.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.get_submissions` instead.
+
         :calls: `GET /api/v1/sections/:section_id/assignments/:assignment_id/submissions  \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.index>`_
 
@@ -157,16 +161,20 @@ class Section(CanvasObject):
         """
         from canvasapi.assignment import Assignment
 
+        warnings.warn(
+            'Section.list_submissions() is deprecated and will be removed '
+            'in the future. Use Assignment.get_submissions() instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        assignment = Assignment(self._requester, {
+            'course_id': self.course_id,
+            'section_id': self.id,
+            'id': assignment_id
+        })
 
-        return PaginatedList(
-            Submission,
-            self._requester,
-            'GET',
-            'sections/{}/assignments/{}/submissions'.format(self.id, assignment_id),
-            {'section_id': self.id},
-            _kwargs=combine_kwargs(**kwargs)
-        )
+        return assignment.get_submissions(**kwargs)
 
     def list_multiple_submissions(self, **kwargs):
         """
@@ -180,7 +188,7 @@ class Section(CanvasObject):
             :class:`canvasapi.submission.Submission`
         """
         if 'grouped' in kwargs:
-            warn('The `grouped` parameter must be empty. Removing kwarg `grouped`.')
+            warnings.warn('The `grouped` parameter must be empty. Removing kwarg `grouped`.')
             del kwargs['grouped']
 
         return PaginatedList(
@@ -196,6 +204,10 @@ class Section(CanvasObject):
         """
         Get a single submission, based on user id.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.assignment.Assignment.get_submission` instead.
+
         :calls: `GET /api/v1/sections/:section_id/assignments/:assignment_id/submissions/:user_id \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.show>`_
 
@@ -207,25 +219,31 @@ class Section(CanvasObject):
         :rtype: :class:`canvasapi.submission.Submission`
         """
         from canvasapi.assignment import Assignment
-        from canvasapi.user import User
+
+        warnings.warn(
+            'Section.get_submission() is deprecated and will be removed '
+            'in the future. Use Assignment.get_submission() instead.',
+            DeprecationWarning
+        )
 
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
-        user_id = obj_or_id(user, "user", (User,))
 
-        response = self._requester.request(
-            'GET',
-            'sections/{}/assignments/{}/submissions/{}'.format(self.id, assignment_id, user_id),
-            _kwargs=combine_kwargs(**kwargs)
-        )
-        response_json = response.json()
-        response_json.update(section_id=self.id)
+        assignment = Assignment(self._requester, {
+            'course_id': self.course_id,
+            'section_id': self.id,
+            'id': assignment_id
+        })
 
-        return Submission(self._requester, response_json)
+        return assignment.get_submission(user, **kwargs)
 
     def update_submission(self, assignment, user, **kwargs):
         """
         Comment on and/or update the grading for a student's assignment submission.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.edit` instead.
+
         :calls: `PUT /api/v1/sections/:section_id/assignments/:assignment_id/submissions/:user_id \
         <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.update>`_
 
@@ -239,29 +257,31 @@ class Section(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
-        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
-        user_id = obj_or_id(user, "user", (User,))
-
-        response = self._requester.request(
-            'PUT',
-            'sections/{}/assignments/{}/submissions/{}'.format(self.id, assignment_id, user_id),
-            _kwargs=combine_kwargs(**kwargs)
+        warnings.warn(
+            'Section.update_submission() is deprecated and will be removed '
+            'in the future. Use Submission.edit() instead.',
+            DeprecationWarning
         )
 
-        submission = self.get_submission(assignment_id, user_id)
-
-        response_json = response.json()
-        response_json.update(section_id=self.id)
+        assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
+        user_id = obj_or_id(user, "user", (User,))
 
-        if 'submission_type' in response_json:
-            super(Submission, submission).set_attributes(response_json)
+        submission = Submission(self._requester, {
+            'course_id': self.course_id,
+            'assignment_id': assignment_id,
+            'user_id': user_id
+        })
 
-        return Submission(self._requester, response_json)
+        return submission.edit(**kwargs)
 
-    def mark_submission_as_read(self, assignment, user):
+    def mark_submission_as_read(self, assignment, user, **kwargs):
         """
         Mark submission as read. No request fields are necessary.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.mark_read` instead.
+
         :calls: `PUT
             /api/v1/sections/:section_id/assignments/:assignment_id/submissions/:user_id/read \
             <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_read>`_
@@ -276,23 +296,30 @@ class Section(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
+        warnings.warn(
+            'Section.mark_submission_as_read() is deprecated and will be '
+            'removed in the future. Use Submission.mark_read() instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
         user_id = obj_or_id(user, "user", (User,))
 
-        response = self._requester.request(
-            'PUT',
-            'sections/{}/assignments/{}/submissions/{}/read'.format(
-                self.id,
-                assignment_id,
-                user_id,
-            )
-        )
-        return response.status_code == 204
+        submission = Submission(self._requester, {
+            'course_id': self.course_id,
+            'assignment_id': assignment_id,
+            'user_id': user_id
+        })
+        return submission.mark_read(**kwargs)
 
-    def mark_submission_as_unread(self, assignment, user):
+    def mark_submission_as_unread(self, assignment, user, **kwargs):
         """
         Mark submission as unread. No request fields are necessary.
 
+        .. warning::
+            .. deprecated:: 0.9.0
+                Use :func:`canvasapi.submission.Submission.mark_unread` instead.
+
         :calls: `DELETE
             /api/v1/sections/:section_id/assignments/:assignment_id/submissions/:user_id/read \
             <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_unread>`_
@@ -307,15 +334,18 @@ class Section(CanvasObject):
         from canvasapi.assignment import Assignment
         from canvasapi.user import User
 
+        warnings.warn(
+            'Section.mark_submission_as_unread() is deprecated and will be '
+            'removed in the future. Use Submission.mark_unread() instead.',
+            DeprecationWarning
+        )
+
         assignment_id = obj_or_id(assignment, "assignment", (Assignment,))
         user_id = obj_or_id(user, "user", (User,))
 
-        response = self._requester.request(
-            'DELETE',
-            'sections/{}/assignments/{}/submissions/{}/read'.format(
-                self.id,
-                assignment_id,
-                user_id,
-            ),
-        )
-        return response.status_code == 204
+        submission = Submission(self._requester, {
+            'course_id': self.course_id,
+            'assignment_id': assignment_id,
+            'user_id': user_id
+        })
+        return submission.mark_unread(**kwargs)
diff --git a/canvasapi/submission.py b/canvasapi/submission.py
index eccc86c..eb15e84 100644
--- a/canvasapi/submission.py
+++ b/canvasapi/submission.py
@@ -4,13 +4,80 @@ from six import python_2_unicode_compatible
 
 from canvasapi.canvas_object import CanvasObject
 from canvasapi.upload import Uploader
+from canvasapi.util import combine_kwargs
 
 
 @python_2_unicode_compatible
 class Submission(CanvasObject):
 
     def __str__(self):
-        return "{}".format(self.id)
+        return '{}-{}'.format(self.assignment_id, self.user_id)
+
+    def edit(self, **kwargs):
+        """
+        Comment on and/or update the grading for a student's assignment submission.
+
+        :calls: `PUT /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id \
+        <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.update>`_
+
+        :rtype: :class:`canvasapi.submission.Submission`
+        """
+        response = self._requester.request(
+            'PUT',
+            'courses/{}/assignments/{}/submissions/{}'.format(
+                self.course_id,
+                self.assignment_id,
+                self.user_id
+            ),
+            _kwargs=combine_kwargs(**kwargs)
+        )
+        response_json = response.json()
+        response_json.update(course_id=self.course_id)
+
+        super(Submission, self).set_attributes(response_json)
+        return self
+
+    def mark_read(self, **kwargs):
+        """
+        Mark submission as read. No request fields are necessary.
+
+        :calls: `PUT
+            /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id/read \
+            <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_read>`_
+
+        :returns: True if successfully marked as read.
+        :rtype: bool
+        """
+        response = self._requester.request(
+            'PUT',
+            'courses/{}/assignments/{}/submissions/{}/read'.format(
+                self.course_id,
+                self.assignment_id,
+                self.user_id
+            )
+        )
+        return response.status_code == 204
+
+    def mark_unread(self, **kwargs):
+        """
+        Mark submission as unread. No request fields are necessary.
+
+        :calls: `DELETE
+            /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id/read \
+            <https://canvas.instructure.com/doc/api/submissions.html#method.submissions_api.mark_submission_unread>`_
+
+        :returns: True if successfully marked as unread.
+        :rtype: bool
+        """
+        response = self._requester.request(
+            'DELETE',
+            'courses/{}/assignments/{}/submissions/{}/read'.format(
+                self.course_id,
+                self.assignment_id,
+                self.user_id
+            )
+        )
+        return response.status_code == 204
 
     def upload_comment(self, file, **kwargs):
         """
@@ -26,11 +93,6 @@ class Submission(CanvasObject):
             and the JSON response from the API.
         :rtype: tuple
         """
-        from canvasapi.course import Course
-
-        if not hasattr(self, 'course_id'):
-            raise ValueError('Must use a course to upload file comments.')
-
         response = Uploader(
             self._requester,
             'courses/{}/assignments/{}/submissions/{}/comments/files'.format(
@@ -43,12 +105,7 @@ class Submission(CanvasObject):
         ).start()
 
         if response[0]:
-            # 1337 h@x: remove once lazy loading is completed
-            course = Course(self._requester, {'id': self.course_id})
-
-            course.update_submission(
-                self.assignment_id,
-                self.user_id,
+            self.edit(
                 comment={
                     'file_ids': [response[1]['id']]
                 }
diff --git a/setup.py b/setup.py
index ab95a2a..b25f5cb 100644
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,6 @@ setup(
         'Operating System :: OS Independent',
         'Programming Language :: Python',
         'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 3.3',
         'Programming Language :: Python :: 3.4',
         'Programming Language :: Python :: 3.5',
         'Programming Language :: Python :: 3.6',


Test patch:
diff --git a/tests/fixtures/assignment.json b/tests/fixtures/assignment.json
index 564854e..47f38b6 100644
--- a/tests/fixtures/assignment.json
+++ b/tests/fixtures/assignment.json
@@ -11,9 +11,9 @@
 	},
 	"delete_assignment": {
 		"method": "DELETE",
-		"endpoint": "courses/1/assignments/5",
+		"endpoint": "courses/1/assignments/1",
 		"data": {
-			"id": 5,
+			"id": 1,
 			"course_id": 1,
 			"name": "Assignment 1",
 			"description": "Delete this assignment"
@@ -32,9 +32,9 @@
 	},
 	"edit_assignment": {
 		"method": "PUT",
-		"endpoint": "courses/1/assignments/5",
+		"endpoint": "courses/1/assignments/1",
 		"data": {
-			"id": 5,
+			"id": 1,
 			"course_id": 1,
 			"name": "New Name",
 			"description": "Edit this assignment"
@@ -72,5 +72,17 @@
 			}
 		],
 		"status_code": 200
+	},
+	"submit": {
+		"method": "POST",
+		"endpoint": "courses/1/assignments/1/submissions",
+		"data": {
+			"id": 1,
+			"assignment_id": 1,
+			"user_id": 1,
+			"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
+			"submission_type": "online_upload"
+		},
+		"status_code": 200
 	}
 }
diff --git a/tests/fixtures/course.json b/tests/fixtures/course.json
index fa235c6..59b2fe9 100644
--- a/tests/fixtures/course.json
+++ b/tests/fixtures/course.json
@@ -41,7 +41,7 @@
 		"method": "POST",
 		"endpoint": "courses/1/assignments",
 		"data": {
-			"id": 5,
+			"id": 1,
 			"course_id": 1,
 			"name": "Newly Created Assignment",
 			"description": "Do this assignment"
@@ -119,17 +119,6 @@
 		"status_code": 200
 	},
 	"get_assignment_by_id": {
-		"method": "GET",
-		"endpoint": "courses/1/assignments/5",
-		"data": {
-			"id": 5,
-			"course_id": 1,
-			"name": "Assignment 1",
-			"description": "Do this assignment"
-		},
-		"status_code": 200
-	},
-	"get_assignment_by_id_2": {
 		"method": "GET",
 		"endpoint": "courses/1/assignments/1",
 		"data": {
@@ -1262,72 +1251,6 @@
 		],
 		"status_code": 200
 	},
-	"submit_assignment": {
-		"method": "POST",
-		"endpoint": "courses/1/assignments/1/submissions",
-		"data": {
-			"id": 1,
-			"assignment_id": 1,
-			"user_id": 1,
-			"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
-			"submission_type": "online_upload"
-		},
-		"status_code": 200
-	},
-	"submit_assignment_2": {
-		"method": "POST",
-		"endpoint": "courses/1/assignments/5/submissions",
-		"data": {
-			"id": 1,
-			"assignment_id": 1,
-			"user_id": 1,
-			"html_url": "http://example.com/courses/1/assignments/5/submissions/1",
-			"submission_type": "online_upload"
-		},
-		"status_code": 200
-	},
-	"list_submissions": {
-		"method": "GET",
-		"endpoint": "courses/1/assignments/1/submissions",
-		"data": [
-			{
-				"id": 1,
-				"assignments_id": 1,
-				"user_id": 1,
-				"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
-				"submission_type": "online_upload"
-			},
-			{
-				"id": 2,
-				"assignments_id": 1,
-				"user_id": 2,
-				"html_url": "http://example.com/courses/1/assignments/1/submissions/2",
-				"submission_type": "online_upload"
-			}
-		],
-		"status_code": 200
-	},
-	"list_submissions_2": {
-		"method": "GET",
-		"endpoint": "courses/1/assignments/5/submissions",
-		"data": [
-			{
-				"id": 1,
-				"assignments_id": 1,
-				"user_id": 1,
-				"html_url": "http://example.com/courses/1/assignments/5/submissions/1",
-				"submission_type": "online_upload"
-			},
-			{
-				"id": 2,
-				"assignments_id": 1,
-				"user_id": 2,
-				"html_url": "http://example.com/courses/1/assignments/5/submissions/2",
-				"submission_type": "online_upload"
-			}
-		],
-		"status_code": 200
-	},
 	"list_multiple_submissions": {
 		"method": "GET",
 		"endpoint": "courses/1/students/submissions",
@@ -1349,30 +1272,6 @@
 		],
 		"status_code": 200
 	},
-	"get_submission": {
-		"method": "GET",
-		"endpoint": "courses/1/assignments/1/submissions/1",
-		"data": {
-			"id": 1,
-			"assignments_id": 1,
-			"user_id": 1,
-			"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
-			"submission_type": "online_upload"
-		},
-		"status_code": 200
-	},
-	"get_submission_2": {
-		"method": "GET",
-		"endpoint": "courses/1/assignments/5/submissions/1",
-		"data": {
-				"id": 1,
-				"assignments_id": 1,
-				"user_id": 1,
-				"html_url": "http://example.com/courses/1/assignments/5/submissions/1",
-				"submission_type": "online_upload"
-		},
-		"status_code": 200
-	},
 	"list_gradeable_students": {
 		"method": "GET",
 		"endpoint": "courses/1/assignments/1/gradeable_students",
diff --git a/tests/fixtures/submission.json b/tests/fixtures/submission.json
index bee0e6f..1d18224 100644
--- a/tests/fixtures/submission.json
+++ b/tests/fixtures/submission.json
@@ -23,6 +23,27 @@
 		},
 		"status_code": 200
 	},
+	"list_submissions": {
+		"method": "GET",
+		"endpoint": "courses/1/assignments/1/submissions",
+		"data": [
+			{
+				"id": 1,
+				"assignments_id": 1,
+				"user_id": 1,
+				"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
+				"submission_type": "online_upload"
+			},
+			{
+				"id": 2,
+				"assignments_id": 1,
+				"user_id": 2,
+				"html_url": "http://example.com/courses/1/assignments/1/submissions/2",
+				"submission_type": "online_upload"
+			}
+		],
+		"status_code": 200
+	},
 	"upload_comment": {
 		"method": "POST",
 		"endpoint": "courses/1/assignments/1/submissions/1/comments/files",
@@ -42,11 +63,16 @@
 			"id": 1
 		}
 	},
-	"update_submission": {
-		"method": "ANY",
+	"edit": {
+		"method": "PUT",
 		"endpoint": "courses/1/assignments/1/submissions/1",
 		"data": {
-			"id": 1
+				"id": 1,
+				"assignments_id": 1,
+				"user_id": 1,
+				"html_url": "http://example.com/courses/1/assignments/1/submissions/1",
+				"submission_type": "online_upload",
+				"excused": true
 		},
 		"status_code": 200
 	}
diff --git a/tests/test_assignment.py b/tests/test_assignment.py
index b69cf86..e0adc34 100644
--- a/tests/test_assignment.py
+++ b/tests/test_assignment.py
@@ -5,6 +5,9 @@ import requests_mock
 
 from canvasapi import Canvas
 from canvasapi.assignment import Assignment, AssignmentGroup
+from canvasapi.exceptions import RequiredFieldMissing
+from canvasapi.submission import Submission
+from canvasapi.user import UserDisplay
 from tests import settings
 from tests.util import register_uris
 
@@ -19,7 +22,15 @@ class TestAssignment(unittest.TestCase):
             register_uris({'course': ['get_by_id', 'get_assignment_by_id']}, m)
 
             self.course = self.canvas.get_course(1)
-            self.assignment = self.course.get_assignment(5)
+            self.assignment = self.course.get_assignment(1)
+
+    # delete()
+    def test_delete_assignments(self, m):
+        register_uris({'assignment': ['delete_assignment']}, m)
+
+        deleted_assignment = self.assignment.delete()
+
+        self.assertIsInstance(deleted_assignment, Assignment)
 
     # edit()
     def test_edit_assignment(self, m):
@@ -32,13 +43,58 @@ class TestAssignment(unittest.TestCase):
         self.assertTrue(hasattr(edited_assignment, 'name'))
         self.assertEqual(edited_assignment.name, name)
 
-    # delete()
-    def test_delete_assignments(self, m):
-        register_uris({'assignment': ['delete_assignment']}, m)
+    # get_gradeable_students()
+    def test_get_gradeable_students(self, m):
+        register_uris({'course': ['list_gradeable_students']}, m)
 
-        deleted_assignment = self.assignment.delete()
+        students = self.assignment.get_gradeable_students()
+        student_list = [student for student in students]
 
-        self.assertIsInstance(deleted_assignment, Assignment)
+        self.assertEqual(len(student_list), 2)
+        self.assertIsInstance(student_list[0], UserDisplay)
+
+    # get_submission()
+    def test_get_submission(self, m):
+        register_uris({
+            'submission': ['get_by_id_course'],
+            'user': ['get_by_id']
+        }, m)
+
+        user_id = 1
+        submission_by_id = self.assignment.get_submission(user_id)
+        self.assertIsInstance(submission_by_id, Submission)
+        self.assertTrue(hasattr(submission_by_id, 'submission_type'))
+
+        user = self.canvas.get_user(user_id)
+        submission_by_obj = self.assignment.get_submission(user)
+        self.assertIsInstance(submission_by_obj, Submission)
+        self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+
+    # get_submissions()
+    def test_get_submissions(self, m):
+        register_uris({'submission': ['list_submissions']}, m)
+
+        submissions = self.assignment.get_submissions()
+        submission_list_by_id = [submission for submission in submissions]
+
+        self.assertEqual(len(submission_list_by_id), 2)
+        self.assertIsInstance(submission_list_by_id[0], Submission)
+
+    # submit()
+    def test_submit(self, m):
+        register_uris({'assignment': ['submit']}, m)
+
+        sub_type = "online_upload"
+        sub_dict = {'submission_type': sub_type}
+        submission = self.assignment.submit(sub_dict)
+
+        self.assertIsInstance(submission, Submission)
+        self.assertTrue(hasattr(submission, 'submission_type'))
+        self.assertEqual(submission.submission_type, sub_type)
+
+    def test_submit_fail(self, m):
+        with self.assertRaises(RequiredFieldMissing):
+            self.assignment.submit({})
 
     # __str__()
     def test__str__(self, m):
diff --git a/tests/test_course.py b/tests/test_course.py
index 04aa27b..b9fda7f 100644
--- a/tests/test_course.py
+++ b/tests/test_course.py
@@ -51,7 +51,7 @@ class TestCourse(unittest.TestCase):
             self.page = self.course.get_page('my-url')
             self.quiz = self.course.get_quiz(1)
             self.user = self.canvas.get_user(1)
-            self.assignment = self.course.get_assignment('5')
+            self.assignment = self.course.get_assignment(1)
 
     # __str__()
     def test__str__(self, m):
@@ -333,7 +333,7 @@ class TestCourse(unittest.TestCase):
         self.assertIsInstance(assignment, Assignment)
         self.assertTrue(hasattr(assignment, 'name'))
         self.assertEqual(assignment.name, name)
-        self.assertEqual(assignment.id, 5)
+        self.assertEqual(assignment.id, 1)
 
     def test_create_assignment_fail(self, m):
         with self.assertRaises(RequiredFieldMissing):
@@ -343,7 +343,7 @@ class TestCourse(unittest.TestCase):
     def test_get_assignment(self, m):
         register_uris({'course': ['get_assignment_by_id']}, m)
 
-        assignment_by_id = self.course.get_assignment('5')
+        assignment_by_id = self.course.get_assignment(1)
         self.assertIsInstance(assignment_by_id, Assignment)
         self.assertTrue(hasattr(assignment_by_id, 'name'))
 
@@ -446,13 +446,15 @@ class TestCourse(unittest.TestCase):
     def test_list_sections(self, m):
         register_uris({'course': ['get_sections', 'get_sections_p2']}, m)
 
-        with warnings.catch_warnings(record=True) as w:
+        with warnings.catch_warnings(record=True) as warning_list:
             sections = self.course.list_sections()
             section_list = [sect for sect in sections]
 
             self.assertEqual(len(section_list), 4)
             self.assertIsInstance(section_list[0], Section)
-            self.assertTrue(issubclass(w[0].category, DeprecationWarning))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     def test_create_course_section(self, m):
         register_uris({'course': ['create_section']}, m)
@@ -714,43 +716,63 @@ class TestCourse(unittest.TestCase):
 
     # submit_assignment()
     def test_submit_assignment(self, m):
-        register_uris({'course': ['submit_assignment', 'submit_assignment_2']}, m)
+        register_uris({'assignment': ['submit']}, m)
 
-        assignment_id = 1
-        sub_type = "online_upload"
-        sub_dict = {'submission_type': sub_type}
-        submission_by_id = self.course.submit_assignment(assignment_id, sub_dict)
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignment_id = 1
+            sub_type = "online_upload"
+            sub_dict = {'submission_type': sub_type}
+            submission_by_id = self.course.submit_assignment(assignment_id, sub_dict)
 
-        self.assertIsInstance(submission_by_id, Submission)
-        self.assertTrue(hasattr(submission_by_id, 'submission_type'))
-        self.assertEqual(submission_by_id.submission_type, sub_type)
+            self.assertIsInstance(submission_by_id, Submission)
+            self.assertTrue(hasattr(submission_by_id, 'submission_type'))
+            self.assertEqual(submission_by_id.submission_type, sub_type)
 
-        submission_by_obj = self.course.submit_assignment(self.assignment, sub_dict)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
-        self.assertIsInstance(submission_by_obj, Submission)
-        self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
-        self.assertEqual(submission_by_obj.submission_type, sub_type)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.course.submit_assignment(self.assignment, sub_dict)
 
-    def test_subit_assignment_fail(self, m):
-        with self.assertRaises(RequiredFieldMissing):
-            self.course.submit_assignment(1, {})
+            self.assertIsInstance(submission_by_obj, Submission)
+            self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+            self.assertEqual(submission_by_obj.submission_type, sub_type)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
+
+    def test_submit_assignment_fail(self, m):
+        with warnings.catch_warnings(record=True) as warning_list:
+            with self.assertRaises(RequiredFieldMissing):
+                self.course.submit_assignment(1, {})
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_submissions()
     def test_list_submissions(self, m):
-        register_uris({'course': ['list_submissions', 'list_submissions_2']}, m)
+        register_uris({'submission': ['list_submissions']}, m)
 
-        assignment_id = 1
-        submissions_by_id = self.course.list_submissions(assignment_id)
-        submission_list_by_id = [submission for submission in submissions_by_id]
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignment_id = 1
+            submissions_by_id = self.course.list_submissions(assignment_id)
+            submission_list_by_id = [submission for submission in submissions_by_id]
+
+            self.assertEqual(len(submission_list_by_id), 2)
+            self.assertIsInstance(submission_list_by_id[0], Submission)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
-        self.assertEqual(len(submission_list_by_id), 2)
-        self.assertIsInstance(submission_list_by_id[0], Submission)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submissions_by_obj = self.course.list_submissions(self.assignment)
+            submission_list_by_obj = [submission for submission in submissions_by_obj]
 
-        submissions_by_obj = self.course.list_submissions(self.assignment)
-        submission_list_by_obj = [submission for submission in submissions_by_obj]
+            self.assertEqual(len(submission_list_by_obj), 2)
+            self.assertIsInstance(submission_list_by_obj[0], Submission)
 
-        self.assertEqual(len(submission_list_by_obj), 2)
-        self.assertIsInstance(submission_list_by_obj[0], Submission)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_multiple_submission()
     def test_list_multiple_submissions(self, m):
@@ -783,92 +805,139 @@ class TestCourse(unittest.TestCase):
 
     # get_submission()
     def test_get_submission(self, m):
-        register_uris({'course': ['get_assignment_by_id_2', 'get_submission']}, m)
+        register_uris({
+            'course': ['get_assignment_by_id'],
+            'submission': ['get_by_id_course']
+        }, m)
 
         assignment_for_id = 1
         user_id = 1
-        submission_by_id = self.course.get_submission(assignment_for_id, user_id)
-        self.assertIsInstance(submission_by_id, Submission)
-        self.assertTrue(hasattr(submission_by_id, 'submission_type'))
 
-        assignment_for_obj = self.course.get_assignment(1)
-        submission_by_obj = self.course.get_submission(assignment_for_obj, self.user)
-        self.assertIsInstance(submission_by_obj, Submission)
-        self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.course.get_submission(assignment_for_id, user_id)
+            self.assertIsInstance(submission_by_id, Submission)
+            self.assertTrue(hasattr(submission_by_id, 'submission_type'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
+
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignment_for_obj = self.course.get_assignment(1)
+            submission_by_obj = self.course.get_submission(assignment_for_obj, self.user)
+            self.assertIsInstance(submission_by_obj, Submission)
+            self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # update_submission()
     def test_update_submission(self, m):
-        register_uris(
-            {
-                'course': [
-                    'get_assignment_by_id_2',
-                    'update_submission',
-                    'get_submission'
-                ]
-            }, m)
+        register_uris({
+            'course': ['get_assignment_by_id'],
+            'submission': ['edit', 'get_by_id_course']
+        }, m)
 
         assignment_for_id = 1
         user_id = 1
-        submission = self.course.update_submission(
-            assignment_for_id,
-            user_id,
-            submission={'excuse': True}
-        )
-        self.assertIsInstance(submission, Submission)
-        self.assertTrue(hasattr(submission, 'excused'))
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission = self.course.update_submission(
+                assignment_for_id,
+                user_id,
+                submission={'excuse': True}
+            )
+            self.assertIsInstance(submission, Submission)
+            self.assertTrue(hasattr(submission, 'excused'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         assignment_for_obj = self.course.get_assignment(1)
-        submission = self.course.update_submission(
-            assignment_for_obj,
-            self.user,
-            submission={'excuse': True}
-        )
-        self.assertIsInstance(submission, Submission)
-        self.assertTrue(hasattr(submission, 'excused'))
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission = self.course.update_submission(
+                assignment_for_obj,
+                self.user,
+                submission={'excuse': True}
+            )
+            self.assertIsInstance(submission, Submission)
+            self.assertTrue(hasattr(submission, 'excused'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_gradeable_students()
     def test_list_gradeable_students(self, m):
-        register_uris({'course': ['get_assignment_by_id_2', 'list_gradeable_students']}, m)
+        register_uris({'course': ['get_assignment_by_id', 'list_gradeable_students']}, m)
 
         assignment_for_id = 1
-        students_by_id = self.course.list_gradeable_students(assignment_for_id)
-        student_list_by_id = [student for student in students_by_id]
+        with warnings.catch_warnings(record=True) as warning_list:
+            students_by_id = self.course.list_gradeable_students(assignment_for_id)
+            student_list_by_id = [student for student in students_by_id]
 
-        self.assertEqual(len(student_list_by_id), 2)
-        self.assertIsInstance(student_list_by_id[0], UserDisplay)
+            self.assertEqual(len(student_list_by_id), 2)
+            self.assertIsInstance(student_list_by_id[0], UserDisplay)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         assignment_for_obj = self.course.get_assignment(1)
-        students_by_id = self.course.list_gradeable_students(assignment_for_obj)
-        student_list_by_id = [student for student in students_by_id]
+        with warnings.catch_warnings(record=True) as warning_list:
+            students_by_id = self.course.list_gradeable_students(assignment_for_obj)
+            student_list_by_id = [student for student in students_by_id]
 
-        self.assertEqual(len(student_list_by_id), 2)
-        self.assertIsInstance(student_list_by_id[0], UserDisplay)
+            self.assertEqual(len(student_list_by_id), 2)
+            self.assertIsInstance(student_list_by_id[0], UserDisplay)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # mark_submission_as_read
     def test_mark_submission_as_read(self, m):
-        register_uris({'course': ['get_assignment_by_id_2', 'mark_submission_as_read']}, m)
+        register_uris({'course': ['get_assignment_by_id', 'mark_submission_as_read']}, m)
 
         assignment_for_id = 1
         user_for_id = 1
-        submission_by_id = self.course.mark_submission_as_read(assignment_for_id, user_for_id)
-        self.assertTrue(submission_by_id)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.course.mark_submission_as_read(assignment_for_id, user_for_id)
+            self.assertTrue(submission_by_id)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         assignment_for_obj = self.course.get_assignment(1)
-        submission_by_obj = self.course.mark_submission_as_read(assignment_for_obj, self.user)
-        self.assertTrue(submission_by_obj)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.course.mark_submission_as_read(assignment_for_obj, self.user)
+            self.assertTrue(submission_by_obj)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # mark_submission_as_unread
     def test_mark_submission_as_unread(self, m):
-        register_uris({'course': ['get_assignment_by_id_2', 'mark_submission_as_unread']}, m)
+        register_uris({'course': ['get_assignment_by_id', 'mark_submission_as_unread']}, m)
 
         assignment_for_id = 1
         user_for_id = 1
-        submission_by_id = self.course.mark_submission_as_unread(assignment_for_id, user_for_id)
-        self.assertTrue(submission_by_id)
+
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.course.mark_submission_as_unread(
+                assignment_for_id,
+                user_for_id
+            )
+            self.assertTrue(submission_by_id)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         assignment_for_obj = self.course.get_assignment(1)
-        submission_by_obj = self.course.mark_submission_as_unread(assignment_for_obj, self.user)
-        self.assertTrue(submission_by_obj)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.course.mark_submission_as_unread(
+                assignment_for_obj,
+                self.user
+            )
+            self.assertTrue(submission_by_obj)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_external_feeds()
     def test_list_external_feeds(self, m):
diff --git a/tests/test_section.py b/tests/test_section.py
index b5c24bf..0711226 100644
--- a/tests/test_section.py
+++ b/tests/test_section.py
@@ -79,7 +79,7 @@ class TestSection(unittest.TestCase):
     def test_submit_assignment(self, m):
         register_uris(
             {
-                'section': ['submit_assignment'],
+                'assignment': ['submit'],
                 'submission': ['get_by_id_section'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
@@ -87,49 +87,67 @@ class TestSection(unittest.TestCase):
         assignment_id = 1
         sub_type = "online_upload"
         sub_dict = {'submission_type': sub_type}
-        assignment_by_id = self.section.submit_assignment(assignment_id, sub_dict)
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignment_by_id = self.section.submit_assignment(assignment_id, sub_dict)
+
+            self.assertIsInstance(assignment_by_id, Submission)
+            self.assertTrue(hasattr(assignment_by_id, 'submission_type'))
+            self.assertEqual(assignment_by_id.submission_type, sub_type)
 
-        self.assertIsInstance(assignment_by_id, Submission)
-        self.assertTrue(hasattr(assignment_by_id, 'submission_type'))
-        self.assertEqual(assignment_by_id.submission_type, sub_type)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
         assignments_obj = user_obj.get_assignments(1)
         sub_type = "online_upload"
         sub_dict = {'submission_type': sub_type}
-        assignment_by_obj = self.section.submit_assignment(assignments_obj[0], sub_dict)
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignment_by_obj = self.section.submit_assignment(assignments_obj[0], sub_dict)
+
+            self.assertIsInstance(assignment_by_obj, Submission)
+            self.assertTrue(hasattr(assignment_by_obj, 'submission_type'))
+            self.assertEqual(assignment_by_obj.submission_type, sub_type)
 
-        self.assertIsInstance(assignment_by_obj, Submission)
-        self.assertTrue(hasattr(assignment_by_obj, 'submission_type'))
-        self.assertEqual(assignment_by_obj.submission_type, sub_type)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     def test_subit_assignment_fail(self, m):
-        with self.assertRaises(RequiredFieldMissing):
-            self.section.submit_assignment(1, {})
+        with warnings.catch_warnings(record=True) as warning_list:
+            with self.assertRaises(RequiredFieldMissing):
+                self.section.submit_assignment(1, {})
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_submissions()
     def test_list_submissions(self, m):
         register_uris(
             {
-                'section': ['list_submissions'],
-                'submission': ['get_by_id_section'],
+                'submission': ['list_submissions'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
 
         assignment_id = 1
-        submissions_by_id = self.section.list_submissions(assignment_id)
-        submission_list_by_id = [submission for submission in submissions_by_id]
+        with warnings.catch_warnings(record=True) as warning_list:
+            submissions_by_id = self.section.list_submissions(assignment_id)
+            submission_list_by_id = [submission for submission in submissions_by_id]
+
+            self.assertEqual(len(submission_list_by_id), 2)
+            self.assertIsInstance(submission_list_by_id[0], Submission)
 
-        self.assertEqual(len(submission_list_by_id), 2)
-        self.assertIsInstance(submission_list_by_id[0], Submission)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
         assignments_obj = user_obj.get_assignments(1)
-        submissions_by_obj = self.section.list_submissions(assignments_obj[0])
-        submission_list_by_obj = [submission for submission in submissions_by_obj]
+        with warnings.catch_warnings(record=True) as warning_list:
+            submissions_by_obj = self.section.list_submissions(assignments_obj[0])
+            submission_list_by_obj = [submission for submission in submissions_by_obj]
+
+            self.assertEqual(len(submission_list_by_obj), 2)
+            self.assertIsInstance(submission_list_by_obj[0], Submission)
 
-        self.assertEqual(len(submission_list_by_obj), 2)
-        self.assertIsInstance(submission_list_by_obj[0], Submission)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # list_multiple_submission()
     def test_list_multiple_submissions(self, m):
@@ -164,93 +182,126 @@ class TestSection(unittest.TestCase):
     def test_get_submission(self, m):
         register_uris(
             {
-                'section': ['get_submission'],
-                'submission': ['get_by_id_section'],
+                'submission': ['get_by_id_course'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
 
         assignment_id = 1
         user_id = 1
-        submission_by_id = self.section.get_submission(assignment_id, user_id)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.section.get_submission(assignment_id, user_id)
 
-        self.assertIsInstance(submission_by_id, Submission)
-        self.assertTrue(hasattr(submission_by_id, 'submission_type'))
+            self.assertIsInstance(submission_by_id, Submission)
+            self.assertTrue(hasattr(submission_by_id, 'submission_type'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
         assignments_obj = user_obj.get_assignments(1)
-        submission_by_obj = self.section.get_submission(assignments_obj[0], user_obj)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.section.get_submission(assignments_obj[0], user_obj)
 
-        self.assertIsInstance(submission_by_obj, Submission)
-        self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+            self.assertIsInstance(submission_by_obj, Submission)
+            self.assertTrue(hasattr(submission_by_obj, 'submission_type'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # update_submission()
     def test_update_submission(self, m):
         register_uris(
             {
-                'section': ['update_submission', 'get_submission'],
-                'submission': ['get_by_id_section'],
+                'submission': ['get_by_id_section', 'edit'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
 
         assignment_id = 1
         user_id = 1
-        submission_by_id = self.section.update_submission(
-            assignment_id,
-            user_id,
-            submission={'excuse': True}
-        )
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.section.update_submission(
+                assignment_id,
+                user_id,
+                submission={'excuse': True}
+            )
+
+            self.assertIsInstance(submission_by_id, Submission)
+            self.assertTrue(hasattr(submission_by_id, 'excused'))
 
-        self.assertIsInstance(submission_by_id, Submission)
-        self.assertTrue(hasattr(submission_by_id, 'excused'))
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
         assignments_obj = user_obj.get_assignments(1)
 
-        submission_by_obj = self.section.update_submission(
-            assignments_obj[0],
-            user_obj,
-            submission={'excuse': True}
-        )
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.section.update_submission(
+                assignments_obj[0],
+                user_obj,
+                submission={'excuse': True}
+            )
 
-        self.assertIsInstance(submission_by_obj, Submission)
-        self.assertTrue(hasattr(submission_by_obj, 'excused'))
+            self.assertIsInstance(submission_by_obj, Submission)
+            self.assertTrue(hasattr(submission_by_obj, 'excused'))
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # mark_submission_as_read
     def test_mark_submission_as_read(self, m):
         register_uris(
             {
-                'section': ['mark_submission_as_read'],
+                'course': ['mark_submission_as_read'],
                 'submission': ['get_by_id_section'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
 
         submission_id = 1
         user_id = 1
-        submission_by_id = self.section.mark_submission_as_read(submission_id, user_id)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.section.mark_submission_as_read(submission_id, user_id)
 
-        self.assertTrue(submission_by_id)
+            self.assertTrue(submission_by_id)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
-        assignments_obj = user_obj.get_assignments(1)
-        submission_by_obj = self.section.mark_submission_as_read(assignments_obj[0], user_obj)
+        with warnings.catch_warnings(record=True) as warning_list:
+            assignments_obj = user_obj.get_assignments(1)
+            submission_by_obj = self.section.mark_submission_as_read(assignments_obj[0], user_obj)
+
+            self.assertTrue(submission_by_obj)
 
-        self.assertTrue(submission_by_obj)
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
     # mark_submission_as_unread
     def test_mark_submission_as_unread(self, m):
         register_uris(
             {
-                'section': ['mark_submission_as_unread'],
+                'course': ['mark_submission_as_unread'],
                 'submission': ['get_by_id_section'],
                 'user': ['get_by_id', 'get_user_assignments']
             }, m)
 
         user_id = 1
         assignment_id = 1
-        submission_by_id = self.section.mark_submission_as_unread(assignment_id, user_id)
-        self.assertTrue(submission_by_id)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_id = self.section.mark_submission_as_unread(assignment_id, user_id)
+            self.assertTrue(submission_by_id)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
 
         user_obj = self.canvas.get_user(1)
         assignments_obj = user_obj.get_assignments(1)
-        submission_by_obj = self.section.mark_submission_as_unread(assignments_obj[0], user_obj)
-        self.assertTrue(submission_by_obj)
+        with warnings.catch_warnings(record=True) as warning_list:
+            submission_by_obj = self.section.mark_submission_as_unread(
+                assignments_obj[0],
+                user_obj
+            )
+            self.assertTrue(submission_by_obj)
+
+            self.assertEqual(len(warning_list), 1)
+            self.assertEqual(warning_list[-1].category, DeprecationWarning)
diff --git a/tests/test_submission.py b/tests/test_submission.py
index ed1c693..354e04a 100644
--- a/tests/test_submission.py
+++ b/tests/test_submission.py
@@ -1,10 +1,12 @@
 from __future__ import absolute_import, division, print_function, unicode_literals
 import unittest
 import uuid
+import warnings
 
 import requests_mock
 
 from canvasapi import Canvas
+from canvasapi.submission import Submission
 from tests import settings
 from tests.util import cleanup_file, register_uris
 
@@ -17,27 +19,47 @@ class TestSubmission(unittest.TestCase):
 
         with requests_mock.Mocker() as m:
             register_uris({
-                'course': ['get_by_id'],
+                'course': ['get_by_id', 'get_assignment_by_id'],
                 'section': ['get_by_id'],
                 'submission': ['get_by_id_course', 'get_by_id_section']
             }, m)
 
-            self.course = self.canvas.get_course(1)
-            self.submission_course = self.course.get_submission(1, 1)
-            self.section = self.canvas.get_section(1)
-            self.submission_section = self.section.get_submission(1, 1)
+            with warnings.catch_warnings(record=True) as warning_list:
+                self.course = self.canvas.get_course(1)
+                self.submission_course = self.course.get_submission(1, 1)
+
+                self.section = self.canvas.get_section(1)
+                self.submission_section = self.section.get_submission(1, 1)
+
+                self.assertEqual(len(warning_list), 2)
+                self.assertEqual(warning_list[0].category, DeprecationWarning)
+                self.assertEqual(warning_list[1].category, DeprecationWarning)
 
     # __str__()
     def test__str__(self, m):
         string = str(self.submission_course)
         self.assertIsInstance(string, str)
 
+    # edit()
+    def test_edit(self, m):
+        register_uris({
+            'submission': ['edit']
+        }, m)
+
+        self.assertFalse(hasattr(self.submission_course, 'excused'))
+
+        self.submission_course.edit(submission={'excuse': True})
+
+        self.assertIsInstance(self.submission_course, Submission)
+        self.assertTrue(hasattr(self.submission_course, 'excused'))
+        self.assertTrue(self.submission_course.excused)
+
     # upload_comment()
     def test_upload_comment(self, m):
         register_uris({'submission': [
             'upload_comment',
             'upload_comment_final',
-            'update_submission',
+            'edit',
         ]}, m)
 
         filename = 'testfile_submission_{}'.format(uuid.uuid4().hex)
@@ -53,6 +75,20 @@ class TestSubmission(unittest.TestCase):
             cleanup_file(filename)
 
     def test_upload_comment_section(self, m):
-        # Sections do not support uploading file comments
-        with self.assertRaises(ValueError):
-            self.submission_section.upload_comment('fakefilename.txt')
+        register_uris({'submission': [
+            'upload_comment',
+            'upload_comment_final',
+            'edit',
+        ]}, m)
+
+        filename = 'testfile_submission_{}'.format(uuid.uuid4().hex)
+
+        try:
+            with open(filename, 'w+') as file:
+                response = self.submission_section.upload_comment(file)
+
+            self.assertTrue(response[0])
+            self.assertIsInstance(response[1], dict)
+            self.assertIn('url', response[1])
+        finally:
+            cleanup_file(filename)
diff --git a/tests/test_user.py b/tests/test_user.py
index c43b98f..eb5fbd8 100644
--- a/tests/test_user.py
+++ b/tests/test_user.py
@@ -345,11 +345,12 @@ class TestUserDisplay(unittest.TestCase):
 
         with requests_mock.Mocker() as m:
             register_uris({
-                'course': ['get_by_id', 'list_gradeable_students']
+                'course': ['get_by_id', 'get_assignment_by_id', 'list_gradeable_students']
             }, m)
 
             self.course = self.canvas.get_course(1)
-            self.userDisplays = self.course.list_gradeable_students(1)
+            self.assignment = self.course.get_assignment(1)
+            self.userDisplays = self.assignment.get_gradeable_students()
             self.userDisplayList = [ud for ud in self.userDisplays]
             self.userDisplay = self.userDisplayList[0]