Repository: ucfopen/canvasapi

Issue:
Improving `canvasapi.submission.Submission`
# Improving `canvasapi.submission.Submission`

Actually, the [`Submission`](https://canvas.instructure.com/doc/api/submissions.html#Submission) object returned by the Canvas API will potentially [contain an `attachment` attribute](https://community.canvaslms.com/t5/Canvas-Developers-Group/Downloading-Submission-File-Attachment/td-p/117413), which is, in fact, a list of [`File`](https://canvas.instructure.com/doc/api/files.html#File) objects.

Currently, the created Submission object will store it as a list of plain Python `dict` parsed from the returned JSON.

# Expected behavior

The constructor should further parse the `attachments` attribute and store it as a list of `canvasapi.file.File`s.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/ucfopen/canvasapi/pull/512?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) Report
> Merging [#512](https://codecov.io/gh/ucfopen/canvasapi/pull/512?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) (c1907e0) into [develop](https://codecov.io/gh/ucfopen/canvasapi/commit/fef329b92c06d9ed58e4744f65b8acf605f31207?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) (fef329b) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##           develop      #512   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           69        69           
  Lines         3656      3660    +4     
=========================================
+ Hits          3656      3660    +4     
```


| [Impacted Files](https://codecov.io/gh/ucfopen/canvasapi/pull/512?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen) | Coverage Δ | |
|---|---|---|
| [canvasapi/submission.py](https://codecov.io/gh/ucfopen/canvasapi/pull/512/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen#diff-Y2FudmFzYXBpL3N1Ym1pc3Npb24ucHk=) | `100.00% <100.00%> (ø)` | |

:mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=ucfopen)


Patch:
diff --git a/AUTHORS.md b/AUTHORS.md
index 0790d78..f82fa2e 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -79,6 +79,7 @@ Patches and Suggestions
 - Stephen Woosley [@stephenwoosley](https://github.com/stephenwoosley)
 - Steven Bell [@stevenbell](https://github.com/stevenbell)
 - Steven Williams [@onomou](https://github.com/onomou)
+- Tinson Lai [@laitingsheng](https://github.com/laitingsheng)
 - Toan Vu [@vutoan1245](https://github.com/vutoan1245)
 - [@Tobiaqs](https://github.com/Tobiaqs)
 - Tuan Pham [@tuanvpham](https://github.com/tuanvpham)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 374a1a6..45b7c07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
 - Update `Course.get_course_level_student_summary_data` to return a `PaginatedList` of `CourseStudentSummary` items instead of a dictionary. (Thanks, [@craigdsthompson](https://github.com/craigdsthompson))
 - Update `Course.get_outcome_results` to return a `PaginatedList` of `OutcomeResult` items instead of a dictionary. (Thanks, [@bennettscience](https://github.com/bennettscience))
 - Remove unnecessary `id` parameter from `delete`, `reorder_question_group`, and `update` methods in `QuizGroup` class. (Thanks, [@kailukaitisBrendan](https://github.com/kailukaitisBrendan))
+- Update `Submission` to return attachments as `File` objects instead of dictionaries. (Thanks, [@laitingsheng](https://github.com/laitingsheng))
 
 ## [2.2.0] - 2021-03-25
 
diff --git a/canvasapi/submission.py b/canvasapi/submission.py
index 6e1250e..f2d738c 100644
--- a/canvasapi/submission.py
+++ b/canvasapi/submission.py
@@ -1,4 +1,5 @@
 from canvasapi.canvas_object import CanvasObject
+from canvasapi.file import File
 from canvasapi.paginated_list import PaginatedList
 from canvasapi.peer_review import PeerReview
 from canvasapi.upload import FileOrPathLike, Uploader
@@ -6,6 +7,14 @@ from canvasapi.util import combine_kwargs, obj_or_id
 
 
 class Submission(CanvasObject):
+    def __init__(self, requester, attributes):
+        super(Submission, self).__init__(requester, attributes)
+
+        self.attachments = [
+            File(requester, attachment)
+            for attachment in attributes.get("attachments", [])
+        ]
+
     def __str__(self):
         return "{}-{}".format(self.assignment_id, self.user_id)
 


Test patch:
diff --git a/tests/fixtures/submission.json b/tests/fixtures/submission.json
index 1bba6eb..4f290ec 100644
--- a/tests/fixtures/submission.json
+++ b/tests/fixtures/submission.json
@@ -51,12 +51,33 @@
 		},
 		"status_code": 200
 	},
+	"get_by_id_with_attachments": {
+		"method": "GET",
+		"endpoint": "courses/1/assignments/1/submissions/1",
+		"data": {
+			"id": 1,
+			"assignment_id": 1,
+			"user_id": 1,
+			"html_url": "https://example.com/courses/1/assignments/1/submissions/1",
+			"submission_type": "online_upload",
+			"attachments": [
+				{
+					"id": 123,
+					"display_name": "attachment.jpeg",
+					"content-type": "image/jpeg",
+					"url": "attachment_file",
+					"size": 3543
+				}
+			]
+		},
+		"status_code": 200
+	},
 	"get_times": {
 		"method": "GET",
 		"endpoint": "courses/1/quizzes/1/submissions/1/time",
 		"data": {
-		    "end_at": "9999-99-99T59:59:59Z",
-		    "time_left": 0
+			"end_at": "9999-99-99T59:59:59Z",
+			"time_left": 0
 		},
 		"status_code": 200
 	},
diff --git a/tests/test_submission.py b/tests/test_submission.py
index 6495065..cdcb595 100644
--- a/tests/test_submission.py
+++ b/tests/test_submission.py
@@ -4,6 +4,7 @@ import uuid
 import requests_mock
 
 from canvasapi import Canvas
+from canvasapi.file import File
 from canvasapi.peer_review import PeerReview
 from canvasapi.submission import GroupedSubmission, Submission
 from tests import settings
@@ -30,6 +31,19 @@ class TestSubmission(unittest.TestCase):
             self.assignment = self.course.get_assignment(1)
             self.submission = self.assignment.get_submission(1)
 
+    # __init__()
+    def test__init__attachments(self, m):
+        register_uris({"submission": ["get_by_id_with_attachments"]}, m)
+
+        submission = self.assignment.get_submission(1)
+
+        self.assertTrue(hasattr(submission, "attachments"))
+        self.assertIsInstance(submission.attachments, list)
+        self.assertEqual(len(submission.attachments), 1)
+        self.assertIsInstance(submission.attachments[0], File)
+        self.assertTrue(hasattr(submission.attachments[0], "id"))
+        self.assertEqual(submission.attachments[0].id, 123)
+
     # __str__()
     def test__str__(self, m):
         string = str(self.submission)