Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions src/gradescopeapi/classes/_helpers/_assignment_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,28 @@ def get_submission_files(
f"{gradescope_base_url}/courses/{course_id}/assignments/{assignment_id}"
)

file_info_link = f"{ASSIGNMENT_ENDPOINT}/submissions/{submission_id}.json?content=react&only_keys[]=text_files&only_keys[]=file_comments"
file_info_link = (
f"{ASSIGNMENT_ENDPOINT}/submissions/{submission_id}.json?content=react"
"&only_keys[]=text_files&only_keys[]=file_comments"
"&only_keys[]=pdf_attachment&only_keys[]=image_attachments"
)
file_info_resp = session.get(file_info_link)
aws_links = []
if file_info_resp.status_code == requests.codes.ok:
file_info_json = json.loads(file_info_resp.text)
if file_info_json.get("text_files"):
aws_links = []
# autograder / code submissions
for file_data in file_info_json["text_files"]:
aws_links.append(file_data["file"]["url"])
elif file_info_json.get("pdf_attachment", {}).get("url"):
# PDF / image submissions: Gradescope stores the student's original
# upload as a single combined pdf_attachment (the un-annotated file).
aws_links.append(file_info_json["pdf_attachment"]["url"])
elif file_info_json.get("image_attachments"):
# fallback: individual image files when there is no combined PDF
for image in file_info_json["image_attachments"]:
if image.get("url"):
aws_links.append(image["url"])
else:
raise NotImplementedError("Image only submissions not yet supported")
# TODO add support for image questions
raise NotImplementedError("Submission has no downloadable files")
return aws_links
62 changes: 62 additions & 0 deletions tests/test_submission_files_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""Unit tests for get_submission_files — no network/credentials required.

A fake session returns a canned submission JSON so we can exercise each
submission shape (autograder text_files, PDF/image submissions via
pdf_attachment, and image_attachments).
"""
import json

import pytest

from gradescopeapi.classes._helpers._assignment_helpers import get_submission_files


class _FakeResp:
status_code = 200

def __init__(self, payload):
self.text = json.dumps(payload)


class _FakeSession:
def __init__(self, payload):
self._payload = payload

def get(self, url):
return _FakeResp(self._payload)


def _call(payload):
return get_submission_files(_FakeSession(payload), "1", "2", "3")


def test_text_files_submission():
links = _call({"text_files": [{"file": {"url": "https://aws/a.py"}}]})
assert links == ["https://aws/a.py"]


def test_pdf_attachment_submission():
# PDF/image submission: no autograder text_files, just a combined PDF
links = _call(
{"text_files": [], "pdf_attachment": {"url": "https://aws/orig.pdf"}}
)
assert links == ["https://aws/orig.pdf"]


def test_image_attachments_submission():
links = _call(
{
"text_files": [],
"pdf_attachment": {},
"image_attachments": [
{"url": "https://aws/1.jpg"},
{"url": "https://aws/2.jpg"},
],
}
)
assert links == ["https://aws/1.jpg", "https://aws/2.jpg"]


def test_no_files_raises():
with pytest.raises(NotImplementedError):
_call({"text_files": [], "pdf_attachment": {}, "image_attachments": []})