Repository: ucfopen/canvasapi

Issue:
Provisional Grades
**What resource needs additional coverage?**
Provisional Grades

**What endpoints need to be covered?**

- [ ] [Bulk select provisional grades](https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.bulk_select)
  `PUT /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/bulk_select`
- [ ] [Show provisional grade status for a student](https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.status)
  `GET /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/status`
- [ ] [Select provisional grade](https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.select)
  `PUT /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/:provisional_grade_id/select`
- [ ] [Publish provisional grades for an assignment](https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.publish)
  `POST /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/publish`
- [ ] [Show provisional grade status for a student](https://canvas.instructure.com/doc/api/all_resources.html#method.anonymous_provisional_grades.status)
  `GET /api/v1/courses/:course_id/assignments/:assignment_id/anonymous_provisional_grades/status`


Discussion:
zenith110: Removed bulk_select_provisional_grades
coveralls: 
[![Coverage Status](https://coveralls.io/builds/38152948/badge)](https://coveralls.io/builds/38152948)

Coverage remained the same at 100.0% when pulling **d4ff58a6e5ab79990c8ecc53a3efd20d6c02fcf7 on zenith110:issue/465-provisional-grades** into **9640fa88c6ae1369d9c22d03b967c630dfac1c29 on ucfopen:develop**.

Thetwam: Great work, @zenith110! Thanks for the PR 👍 

Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index d7583cf..02ef201 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -10,6 +10,7 @@ Development Lead
 Patches and Suggestions
 -----------------------
 
+- Abrahan Nevarez [@zenith110](https://github.com/zenith110)
 - Adrian Goetz [@a-goetz](https://github.com/a-goetz)
 - Aileen Pongnon [@aileenpongnon](https://github.com/aileenpongnon)
 - Alyssa Davis [@allygator](https://github.com/allygator)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52fc03f..d1e561d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### New Endpoint Coverage
 
 - Enroll a user in a section (Thanks, [@damianfs](https://github.com/damianfs))
+- Provisional Grades (Thanks, [@zenith110](https://github.com/zenith110))
 
 ### General
 
diff --git a/canvasapi/assignment.py b/canvasapi/assignment.py
index 52253b6..4b01566 100644
--- a/canvasapi/assignment.py
+++ b/canvasapi/assignment.py
@@ -160,6 +160,32 @@ class Assignment(CanvasObject):
             _kwargs=combine_kwargs(**kwargs),
         )
 
+    def get_provisional_grades_status(self, student_id, **kwargs):
+        """
+        Tell whether the student's submission needs one or more provisional grades.
+
+        :calls: `GET /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/
+            status \
+        <https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.status>`_
+
+        :param student_id: The object or ID of the related student
+        :type student_id: :class:`canvasapi.user.User` or int
+
+        :rtype: bool
+        """
+        kwargs["student_id"] = obj_or_id(student_id, "student_id", (User,))
+        request = self._requester.request(
+            "GET",
+            "courses/{}/assignments/{}/provisional_grades/status".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        request_json = request.json()
+
+        return request_json.get("needs_provisional_grade")
+
     def get_submission(self, user, **kwargs):
         """
         Get a single submission, based on user id.
@@ -205,6 +231,55 @@ class Assignment(CanvasObject):
             _kwargs=combine_kwargs(**kwargs),
         )
 
+    def publish_provisional_grades(self, **kwargs):
+        """
+        Publish the selected provisional grade for all submissions to an assignment.
+        Use the “Select provisional grade” endpoint to choose which provisional grade to publish
+        for a particular submission.
+
+        Students not in the moderation set will have their one
+        and only provisional grade published.
+
+        WARNING: This is irreversible. This will overwrite existing grades in the gradebook.
+
+        :calls: `POST /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades
+            /publish \
+        <https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.publish>`_
+        :rtype: dict
+        """
+        response = self._requester.request(
+            "POST",
+            "courses/{}/assignments/{}/provisional_grades/publish".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+        return response.json()
+
+    def selected_provisional_grade(self, provisional_grade_id, **kwargs):
+        """
+        Choose which provisional grade the student should receive for a submission.
+        The caller must be the final grader for the assignment
+        or an admin with :select_final_grade rights.
+
+        :calls: `PUT /api/v1/courses/:course_id/assignments/:assignment_id/provisional_grades/
+            :provisonal_grade_id/select \
+        <https://canvas.instructure.com/doc/api/all_resources.html#method.provisional_grades.select>`_
+
+        :param provisional_grade_id: ID of the provisional grade
+        :type provisional_grade_id: int
+        :rtype: dict
+        """
+        response = self._requester.request(
+            "PUT",
+            "courses/{}/assignments/{}/provisional_grades/{}/select".format(
+                self.course_id, self.id, provisional_grade_id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return response.json()
+
     def set_extensions(self, assignment_extensions, **kwargs):
         """
         Set extensions for student assignment submissions
@@ -254,6 +329,30 @@ class Assignment(CanvasObject):
             for extension in extension_list
         ]
 
+    def show_provisonal_grades_for_student(self, anonymous_id, **kwargs):
+        """
+        :call: `GET /api/v1/courses/:course_id/assignments/:assignment_id/
+            anonymous_provisional_grades/status \
+        <https://canvas.instructure.com/doc/api/all_resources.html#method.anonymous_provisional_grades.status>`_
+
+        :param anonymous_id: The ID of the student to show the status for
+        :type anonymous_id: :class:`canvasapi.user.User` or int
+
+        :rtype: dict
+        """
+
+        kwargs["anonymous_id"] = obj_or_id(anonymous_id, "anonymous_id", (User,))
+
+        request = self._requester.request(
+            "GET",
+            "courses/{}/assignments/{}/anonymous_provisional_grades/status".format(
+                self.course_id, self.id
+            ),
+            _kwargs=combine_kwargs(**kwargs),
+        )
+
+        return request.json().get("needs_provisional_grade")
+
     def submissions_bulk_update(self, **kwargs):
         """
         Update the grading and comments on multiple student's assignment
diff --git a/canvasapi/submission.py b/canvasapi/submission.py
index 45a5348..6e1250e 100644
--- a/canvasapi/submission.py
+++ b/canvasapi/submission.py
@@ -79,6 +79,7 @@ class Submission(CanvasObject):
             _kwargs=combine_kwargs(**kwargs),
         )
         response_json = response.json()
+
         response_json.update(course_id=self.course_id)
 
         super(Submission, self).set_attributes(response_json)


Test patch:
diff --git a/tests/fixtures/assignment.json b/tests/fixtures/assignment.json
index e457e63..c6f6285 100644
--- a/tests/fixtures/assignment.json
+++ b/tests/fixtures/assignment.json
@@ -379,5 +379,35 @@
 		"data": {
 			"no_url": "bad_url_failure"
 		}
+	},
+	"get_provisional_grades_status": {
+		"method": "GET",
+		"endpoint": "courses/1/assignments/1/provisional_grades/status",
+		"data": {
+			"needs_provisional_grade": false
+		}
+	},
+	"show_provisonal_grades_for_student": {
+		"method": "GET",
+		"endpoint": "courses/1/assignments/1/anonymous_provisional_grades/status",
+		"data": {
+			"needs_provisional_grade": false
+		}
+	},
+	"selected_provisional_grade": {
+		"method": "PUT",
+		"endpoint": "courses/1/assignments/1/provisional_grades/1/select",
+		"data": {
+			"assignment_id": 1,
+			"student_id": 2,
+			"provisional_grade_id": 3
+		}
+	},
+	"publish_provisional_grades": {
+		"method": "POST",
+		"endpoint": "courses/1/assignments/1/provisional_grades/publish",
+		"data": {
+			"message": "OK"
+		}
 	}
 }
diff --git a/tests/test_assignment.py b/tests/test_assignment.py
index 165970c..b86314b 100644
--- a/tests/test_assignment.py
+++ b/tests/test_assignment.py
@@ -306,6 +306,50 @@ class TestAssignment(unittest.TestCase):
         finally:
             cleanup_file(filename)
 
+    # get_provisional_grades_status
+    def test_get_provisional_grades_status(self, m):
+        register_uris(
+            {"assignment": ["get_provisional_grades_status"], "user": ["get_by_id"]}, m
+        )
+        student_id = 1
+        user = self.canvas.get_user(student_id)
+        status = self.assignment.get_provisional_grades_status(user)
+        self.assertIsInstance(status, bool)
+        self.assertFalse(status)
+
+    # selected_provisional_grade
+    def test_selected_provisional_grade(self, m):
+        register_uris({"assignment": ["selected_provisional_grade"]}, m)
+        provisional_grade_id = 1
+        selected_provisional_grade = self.assignment.selected_provisional_grade(
+            provisional_grade_id
+        )
+        self.assertIsInstance(selected_provisional_grade, dict)
+        self.assertIn("assignment_id", selected_provisional_grade)
+
+    # publish_provisional_grades
+    def test_publish_provisional_grades(self, m):
+        register_uris({"assignment": ["publish_provisional_grades"]}, m)
+        publish = self.assignment.publish_provisional_grades()
+        self.assertIsInstance(publish, dict)
+        self.assertIn("message", publish)
+
+    # show_provisional_grades_for_student
+    def test_show_provisonal_grades_for_student(self, m):
+        register_uris(
+            {
+                "assignment": ["show_provisonal_grades_for_student"],
+                "user": ["get_by_id"],
+            },
+            m,
+        )
+        anonymous_id = 1
+        user = self.canvas.get_user(anonymous_id)
+        show_status = self.assignment.show_provisonal_grades_for_student(user)
+
+        self.assertIsInstance(show_status, bool)
+        self.assertFalse(show_status)
+
 
 @requests_mock.Mocker()
 class TestAssignmentExtension(unittest.TestCase):