Skip to content

Commit

Permalink
course/assignment names and getting multiple courses/assignments
Browse files Browse the repository at this point in the history
  • Loading branch information
reidoko committed Nov 19, 2024
1 parent cf6313b commit 9af041f
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 18 deletions.
24 changes: 13 additions & 11 deletions src/gradescope_api/assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,26 @@


class GradescopeAssignment:
def __init__(self, _client: GradescopeClient, _course: GradescopeCourse, assignment_id: str) -> None:
def __init__(self, _client: GradescopeClient, _course: GradescopeCourse, assignment_id: str, assignment_name: Optional[str] = None) -> None:
self._client = _client
self._course = _course
self.assignment_id = assignment_id
self.assignment_name = assignment_name

def get_url(self) -> str:
return self._course.get_url() + f"/assignments/{self.assignment_id}"

def get_title(self) -> str:
course_id = self._course.course_id
assignment_id = self.assignment_id
response = self._client.session.get(
f"https://www.gradescope.com/courses/{course_id}/assignments/{assignment_id}"
)
check_response(response, "could not load assignment")
soup = BeautifulSoup(response.content, "html.parser")
title = soup.find("h2", {"class" : "sidebar--title"})["title"]
return title
def get_name(self) -> str:
if self.assignment_name is None:
course_id = self._course.course_id
assignment_id = self.assignment_id
response = self._client.session.get(
f"https://www.gradescope.com/courses/{course_id}/assignments/{assignment_id}"
)
check_response(response, "could not load assignment")
soup = BeautifulSoup(response.content, "html.parser")
self.assignment_name = soup.find("h2", {"class" : "sidebar--title"})["title"]
return self.assignment_name


def apply_extension(self, email: str, num_days: int):
Expand Down
24 changes: 22 additions & 2 deletions src/gradescope_api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,26 @@ def _log_in(self, email: str, password: str):
response = self.submit_form(url=url, data=payload)
check_response(response, error="failed to log in")

def get_course(self, course_url: Optional[str] = None, course_id: Optional[str] = None):
def get_course(self, course_url: Optional[str] = None, course_id: Optional[str] = None, course_name: Optional[str] = None, course_term : Optional[str] = None):
course_id = course_id or get_url_id(course_url, "courses")
return GradescopeCourse(_client=self, course_id=course_id)
return GradescopeCourse(_client=self, course_id=course_id, course_name=course_name, course_term=course_term)

def get_courses(self):
response = self.session.get(BASE_URL)
content = response.content
soup = BeautifulSoup(content, "html.parser")
terms_and_courses = soup.find_all("div", attrs={"class":["courseList--term","courseList--coursesForTerm"]})
current_term = None

courses = []
for term_or_courses in terms_and_courses:
if term_or_courses["class"][0] == "courseList--term":
current_term = next(term_or_courses.children)
else:
for course_info in term_or_courses.find_all("a"):
courses.append(self.get_course(
course_id=course_info["href"].split('/')[-1],
course_name=next(course_info.find("h3").children),
course_term=current_term
))
return courses
34 changes: 29 additions & 5 deletions src/gradescope_api/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
from typing import TYPE_CHECKING, Dict, List, Optional
from collections.abc import Callable

from bs4 import BeautifulSoup
from gradescope_api.errors import check_response
Expand All @@ -14,14 +15,33 @@


class GradescopeCourse:
def __init__(self, _client: GradescopeClient, course_id: str) -> None:
def __init__(self, _client: GradescopeClient, course_id: str, course_name: Optional[str] = None, course_term: Optional[str] = None) -> None:
self._client = _client
self.course_id = course_id
self.course_name = course_name
self.course_term = course_term
self.roster: List[GradescopeStudent] = []

def _initialize(self):
response = self._client.session.get(url=self.get_url(), timeout=20)
check_response(response, "failed to get course page")
soup = BeautifulSoup(response.content, "html.parser")
self.course_name = next(soup.find("h1").children)
self.course_term = next(soup.find("h2", attrs={"class" : "courseHeader--term"}).children)

def get_url(self) -> str:
return self._client.get_base_url() + f"/courses/{self.course_id}"

def get_name(self) -> str:
if self.course_name is None:
self._initialize()
return self.course_name

def get_term(self) -> str:
if self.course_term is None:
self._initialize()
return self.course_term

def get_roster(self) -> List[GradescopeStudent]:
if self.roster:
return self.roster
Expand Down Expand Up @@ -65,14 +85,16 @@ def get_student(self, sid: Optional[str] = None, email: Optional[str] = None) ->
return None

def get_assignment(
self, assignment_id: Optional[str] = None, assignment_url: Optional[str] = None
self, assignment_id: Optional[str] = None, assignment_url: Optional[str] = None, assignment_name: Optional[str] = None
) -> Optional[GradescopeAssignment]:
assert assignment_id or assignment_url
assignment_id = assignment_id or get_url_id(url=assignment_url, kind="assignments")
return GradescopeAssignment(_client=self._client, _course=self, assignment_id=assignment_id)
return GradescopeAssignment(_client=self._client, _course=self, assignment_id=assignment_id, assignment_name=assignment_name)

def get_assignments(self, where=lambda x: True) -> list[GradescopeAssignment]:
def get_assignments(self, where: Optional[Union[str, Callable[[Dict], bool]]]=lambda x: True) -> list[GradescopeAssignment]:
if (callable(where)):
# You can filter on whatever attributes are available in the table data
# e.g. x["submission_window"]["due_date"]
filter_fn = lambda x: x["type"] == "assignment" and where(x)
else:
key = where.lower()
Expand All @@ -86,6 +108,8 @@ def get_assignments(self, where=lambda x: True) -> list[GradescopeAssignment]:
assignment_data = json.loads(props)["table_data"]

return [
self.get_assignment(assignment_url=f"https://www.gradescope.com/courses/{data['url']}")
self.get_assignment(
assignment_url=f"https://www.gradescope.com/courses/{data['url']}",
assignment_name=data["title"])
for data in filter(filter_fn, assignment_data)
]

0 comments on commit 9af041f

Please sign in to comment.