From acd332bd75f8725d58ce6706c4d707c05c274101 Mon Sep 17 00:00:00 2001 From: Antoni Czaplicki Date: Tue, 26 Nov 2024 00:20:56 +0100 Subject: [PATCH] Add get_user_course_editions method --- pyproject.toml | 10 +++++----- usos_api/helper.py | 13 ++----------- usos_api/models/course.py | 18 ++++++++++++++---- usos_api/models/term.py | 2 +- usos_api/services/courses.py | 34 +++++++++++++++++++++++++++++++++- usos_api/services/grades.py | 6 +++++- usos_api/services/groups.py | 2 +- 7 files changed, 61 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8378c40..61d4de8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "usos-api" -version = "0.3.0" +version = "0.3.1" description = "Asynchronous USOS API for Python" authors = ["Antoni-Czaplicki <56671347+Antoni-Czaplicki@users.noreply.github.com>"] readme = "README.md" @@ -10,10 +10,10 @@ documentation = "https://usos-api.readthedocs.io" [tool.poetry.dependencies] python = "^3.10" -aiohttp = "^3.9.5" -pydantic = "^2.8.2" -oauthlib = "^3.2.2" -python-dotenv = "^1.0.1" +aiohttp = "^3.11" +pydantic = "^2.10" +oauthlib = "^3.2" +python-dotenv = "^1.0" [tool.poetry.group.dev.dependencies] black = "*" diff --git a/usos_api/helper.py b/usos_api/helper.py index 0d47839..02d44b5 100644 --- a/usos_api/helper.py +++ b/usos_api/helper.py @@ -22,18 +22,9 @@ async def get_user_end_grades_with_weights( ects_by_term = await self.client.course_service.get_user_courses_ects() terms = await self.client.term_service.get_terms(list(ects_by_term.keys())) term_ids = [ - term.id for term in terms if not current_term_only or term.is_current - ] - course_ids = [ - course_id - for term_id in term_ids - for course_id in ects_by_term[term_id].keys() + term.id for term in terms if not current_term_only or term.is_ongoing ] - courses = { - course.id: course - for course in await self.client.course_service.get_courses(course_ids) - } grades_by_term = await self.client.grade_service.get_grades_by_terms(term_ids) user_grades = [] @@ -47,7 +38,7 @@ async def get_user_end_grades_with_weights( for grade in grades: grade.weight = ects grade.course_edition = CourseEdition( - course=courses[course_id], term=term + course_id=course_id, term_id=term.id ) user_grades.append(grade) diff --git a/usos_api/models/course.py b/usos_api/models/course.py index 9ef1c2c..ce75689 100644 --- a/usos_api/models/course.py +++ b/usos_api/models/course.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Any, Optional from pydantic import BaseModel @@ -65,13 +65,23 @@ class CourseEdition(BaseModel): Class representing a course edition. """ - course: Course | None = None - term: Term | None = None + course_id: str | None = None + course_name: LangDict | None = None + term_id: str | None = None homepage_url: str | None = None + profile_url: str | None = None + coordinators: list[dict[str, Any]] | None = None + lecturers: list[dict[str, Any]] | None = None + passing_status: str | None = None # passed, failed, not_yet_passed + user_groups: list[Group] | None = None + description: LangDict | None = None bibliography: LangDict | None = None notes: LangDict | None = None - course_units: list[CourseUnit] | None = None + course_units_ids: list[str] | None = None + participants: list[dict[str, Any]] | None = None + grades: list[dict[str, Any]] | None = None + attributes: list[dict[str, list[LangDict]]] | None = None class CourseEditionConducted(BaseModel): diff --git a/usos_api/models/term.py b/usos_api/models/term.py index 40018d0..70374bb 100644 --- a/usos_api/models/term.py +++ b/usos_api/models/term.py @@ -14,7 +14,7 @@ class Term(BaseModel): is_active: bool | None = None @property - def is_current(self) -> bool: + def is_ongoing(self) -> bool: """ Check if the term is currently active. """ diff --git a/usos_api/services/courses.py b/usos_api/services/courses.py index f55325c..f8feaf7 100644 --- a/usos_api/services/courses.py +++ b/usos_api/services/courses.py @@ -1,7 +1,7 @@ from typing import Optional from ..connection import USOSAPIConnection -from ..models import Course +from ..models import Course, CourseEdition, Term class CourseService: @@ -50,3 +50,35 @@ async def get_courses( ) return [Course(**course_data) for course_data in response.values()] + + async def get_user_course_editions( + self, + active_terms_only: bool = False, + ongoing_terms_only: bool = False, + ) -> list[CourseEdition]: + """ + Get information on user's courses. + + This is a BETA method. We're looking for beta-testers. Until we find them, there's a substantial probability it won't stay backwards-compatible! + If you are planning on using this method, please let us know. Then, we will work with you and move it out of beta as soon as we can. + + :param active_terms_only: Return only these course editions which are related to the currently active academic terms. Apparently, this parameter does not always work as expected, so you can use `ongoing_terms_only` instead. + :param ongoing_terms_only: Return only these course editions which are related to the currently ongoing academic terms (filtered locally based on start and finish dates). + :return: A dictionary of selected fields and their values. + """ + params = { + "fields": "course_editions[course_id|course_name|term_id|homepage_url|profile_url|coordinators|lecturers|passing_status|user_groups|grades|attributes]|terms", + "active_terms_only": str(active_terms_only).lower(), + } + + response = await self.connection.post("services/courses/user", **params) + + terms = {term["id"]: Term(**term) for term in response["terms"]} + ongoing_terms = {term_id for term_id, term in terms.items() if term.is_ongoing} + + return [ + CourseEdition(**course_edition) + for term_id, course_editions in response["course_editions"].items() + if not ongoing_terms_only or term_id in ongoing_terms + for course_edition in course_editions + ] diff --git a/usos_api/services/grades.py b/usos_api/services/grades.py index bdf4115..7b49d9b 100644 --- a/usos_api/services/grades.py +++ b/usos_api/services/grades.py @@ -84,6 +84,7 @@ def _process_course_units_grades( exam_session_number: Grade(**grade) for unit in units for exam_session_number, grade in unit.items() + if grade } return processed_units_grades @@ -95,5 +96,8 @@ def _process_course_grades(self, course_grades: list) -> list[Grade]: :return: The processed course grades. """ return [ - Grade(**grade) for session in course_grades for grade in session.values() + Grade(**grade) + for session in course_grades + for grade in session.values() + if grade ] diff --git a/usos_api/services/groups.py b/usos_api/services/groups.py index af70e98..9d66c06 100644 --- a/usos_api/services/groups.py +++ b/usos_api/services/groups.py @@ -10,7 +10,7 @@ def _filter_ongoing_terms(terms: list[Term]) -> list[Term]: :param terms: The terms to filter. :return: The ongoing terms. """ - return [term for term in terms if term.is_current] + return [term for term in terms if term.is_ongoing] def _deserialize_term(data: dict) -> Term: