1
0
Fork 0
forked from Fuji/Fuji

SDK: Move from dataclasses to pydantic, add serialization/deserialization section in docs

This commit is contained in:
Marioneq 2025-02-08 21:43:06 +01:00
parent 09f0cd9415
commit 10061cd0c4
12 changed files with 100 additions and 46 deletions

View file

@ -92,6 +92,70 @@ No możesz pobrać na przykład oceny, możesz to zrobić (gdy jesteś zalogowan
grades = interface.get_grades(<numer semestru>) grades = interface.get_grades(<numer semestru>)
``` ```
### Serializacja i wczytywanie danych
SDK używa modeli pydanic, nie są to zwykłe dict'y czy JSON.
#### Dict
`Model -> dict`
```py
model.model_dump()
```
`dict -> model`
```py
model.model_validate()
```
#### Lista dictów
`list[Model] -> list[dict]`
```py
ModelList = pydantic.TypeAdapter(list[Model])
lista_dictow = ModelList.dump_python(lista_modeli)
```
`list[dict] -> list[Model]`
```py
ModelList = pydantic.TypeAdapter(list[Model])
lista_modeli = ModelList.validate_python(lista_dictow)
```
#### JSON (pojedyńczy obiekt)
`Model -> str`
```py
Model.model_dump_json()
```
`str -> model`
```py
Model.model_validate_json()
```
#### JSON (lista)
`list[Model] -> str`
```py
ModelList = pydantic.TypeAdapter(list[Model])
json_ = ModelList.dump_json(lista_modeli)
```
`str -> list[Model]`
```py
ModelList = pydantic.TypeAdapter(list[Model])
lista_modeli = ModelList.validate_json(json_)
```
## Jak dodawać funkcjonalności: ## Jak dodawać funkcjonalności:
1. Zrób model(e) do danych, nie dawaj zbyt dużo danych, bo pamiętaj, że ma być to uniwersalny model dla wielu dzienników. 1. Zrób model(e) do danych, nie dawaj zbyt dużo danych, bo pamiętaj, że ma być to uniwersalny model dla wielu dzienników.
@ -122,8 +186,7 @@ class ExamType(Enum):
return ExamType.OTHER return ExamType.OTHER
@dataclass class Exam(BaseModel):
class Exam:
deadline: date deadline: date
subject: str subject: str
type: ExamType type: ExamType

View file

@ -1,15 +1,13 @@
from dataclasses import dataclass from pydantic import BaseModel
@dataclass class FsLsQuery(BaseModel):
class FsLsQuery:
wa: str wa: str
wtrealm: str wtrealm: str
wctx: str wctx: str
@dataclass class FsLsResponse(BaseModel):
class FsLsResponse:
wa: str wa: str
wresult: str wresult: str
wctx: str wctx: str

View file

@ -1,4 +1,3 @@
import dataclasses
import requests import requests
from sdk.src.apis.common.models import FsLsResponse, FsLsQuery from sdk.src.apis.common.models import FsLsResponse, FsLsQuery
@ -33,9 +32,9 @@ class EfebClient:
method="POST" if prometheus_response else "GET", method="POST" if prometheus_response else "GET",
url=f"{BASE_LOGIN}/{self._symbol}/{ENDPOINT_LOGIN_FS_LS}", url=f"{BASE_LOGIN}/{self._symbol}/{ENDPOINT_LOGIN_FS_LS}",
data=( data=(
dataclasses.asdict(prometheus_response) if prometheus_response else None dict(prometheus_response) if prometheus_response else None
), ),
params=dataclasses.asdict(query), params=dict(query),
) )
return parse_fs_ls_response_form(response.text) return parse_fs_ls_response_form(response.text)
@ -43,7 +42,7 @@ class EfebClient:
response = self._session.request( response = self._session.request(
method="POST" if login_response else "GET", method="POST" if login_response else "GET",
url=f"{BASE_STUDENT_CE if self._is_ce else BASE_STUDENT}/{self._symbol}/{ENDPOINT_STUDENT_APP}", url=f"{BASE_STUDENT_CE if self._is_ce else BASE_STUDENT}/{self._symbol}/{ENDPOINT_STUDENT_APP}",
data=dataclasses.asdict(login_response) if login_response else None, data=dict(login_response) if login_response else None,
) )
return parse_app_html(response.text) return parse_app_html(response.text)
@ -51,6 +50,6 @@ class EfebClient:
response = self._session.request( response = self._session.request(
method="POST" if login_response else "GET", method="POST" if login_response else "GET",
url=f"{BASE_MESSAGES_CE if self._is_ce else BASE_MESSAGES}/{self._symbol}/{ENDPOINT_MESSAGES_APP}", url=f"{BASE_MESSAGES_CE if self._is_ce else BASE_MESSAGES}/{self._symbol}/{ENDPOINT_MESSAGES_APP}",
data=dataclasses.asdict(login_response) if login_response else None, data=dict(login_response) if login_response else None,
) )
return parse_app_html(response.text) return parse_app_html(response.text)

View file

@ -1,9 +1,8 @@
from dataclasses import dataclass from pydantic import BaseModel
from sdk.src.apis.hebe.signer import generate_key_pair from sdk.src.apis.hebe.signer import generate_key_pair
@dataclass class Certificate(BaseModel):
class Certificate:
certificate: str certificate: str
fingerprint: str fingerprint: str
private_key: str private_key: str
@ -12,4 +11,4 @@ class Certificate:
@staticmethod @staticmethod
def generate(): def generate():
certificate, fingerprint, private_key = generate_key_pair() certificate, fingerprint, private_key = generate_key_pair()
return Certificate(certificate, fingerprint, private_key, "X509") return Certificate(certificate=certificate, fingerprint=fingerprint, private_key=private_key, type="X509")

View file

@ -1,9 +1,7 @@
from dataclasses import dataclass
from datetime import date from datetime import date
from pydantic import BaseModel
class HebePeriod(BaseModel):
@dataclass
class HebePeriod:
id: int id: int
number: int number: int
current: bool current: bool
@ -21,8 +19,7 @@ class HebePeriod:
) )
@dataclass class HebeStudent(BaseModel):
class HebeStudent:
id: int id: int
full_name: str full_name: str
unit_id: int unit_id: int

View file

@ -1,4 +1,3 @@
import dataclasses
import json import json
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import requests import requests
@ -97,7 +96,7 @@ class PrometheusWebClient:
def fs_ls(self, query: FsLsQuery): def fs_ls(self, query: FsLsQuery):
response = self._session.get( response = self._session.get(
f"{PROMETHEUS_WEB_BASE}/{ENDPOINT_FS_LS}", f"{PROMETHEUS_WEB_BASE}/{ENDPOINT_FS_LS}",
params=dataclasses.asdict(query), params=dict(query),
) )
if "Logowanie" in response.text: if "Logowanie" in response.text:

View file

@ -1,6 +1,7 @@
from datetime import date from datetime import date
from sdk.src.models.exam import Exam from sdk.src.models.exam import Exam
from sdk.src.models.grade import Grade from sdk.src.models.grade import Grade
from sdk.src.models.note import Note
from sdk.src.models.student import Student from sdk.src.models.student import Student
@ -22,3 +23,6 @@ class CoreInterface:
def get_exams(from_: date, to: date) -> list[Exam]: def get_exams(from_: date, to: date) -> list[Exam]:
pass pass
def get_notes() -> list[Note]:
pass

View file

@ -1,12 +1,11 @@
from dataclasses import dataclass from pydantic import BaseModel
from sdk.src.apis.hebe import HebeCertificate from sdk.src.apis.hebe import HebeCertificate
from sdk.src.apis.hebe.student import HebeStudent from sdk.src.apis.hebe.student import HebeStudent
from sdk.src.interfaces.prometheus.utils import get_context_periods_from_hebe_periods from sdk.src.interfaces.prometheus.utils import get_context_periods_from_hebe_periods
@dataclass class PrometheusStudentContext(BaseModel):
class PrometheusStudentContext:
student_id: int student_id: int
unit_id: int unit_id: int
constituent_id: int constituent_id: int
@ -32,16 +31,14 @@ class PrometheusStudentContext:
) )
@dataclass class PrometheusWebCredentials(BaseModel):
class PrometheusWebCredentials:
username: str username: str
password: str password: str
@dataclass class PrometheusAuthContext(BaseModel):
class PrometheusAuthContext:
prometheus_web_credentials: PrometheusWebCredentials prometheus_web_credentials: PrometheusWebCredentials
symbols: list[str] | None = None symbols: list[str] | None = None
hebe_certificate: HebeCertificate | None = None hebe_certificate: HebeCertificate | None = None
prometheus_web_cookies: dict[str, str] | None = None prometheus_web_cookies: dict[str, str] | None = None
efeb_web_cookies: dict[str, str | None] | None = None efeb_web_cookies: dict[str, dict[str, str] | None] | None = None

View file

@ -1,7 +1,8 @@
from dataclasses import dataclass
from datetime import date, datetime from datetime import date, datetime
from enum import Enum from enum import Enum
from pydantic import BaseModel
class ExamType(Enum): class ExamType(Enum):
TEST = 0 TEST = 0
@ -22,8 +23,7 @@ class ExamType(Enum):
return ExamType.OTHER return ExamType.OTHER
@dataclass class Exam(BaseModel):
class Exam:
deadline: date deadline: date
subject: str subject: str
type: ExamType type: ExamType

View file

@ -1,9 +1,9 @@
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pydantic import BaseModel
@dataclass
class Grade: class Grade(BaseModel):
value: str value: str
is_point: bool is_point: bool
point_numerator: int point_numerator: int

View file

@ -1,9 +1,8 @@
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from pydantic import BaseModel
@dataclass class Note(BaseModel):
class Note:
name: str | None name: str | None
content: str content: str
points: str | None points: str | None

View file

@ -1,12 +1,12 @@
from dataclasses import dataclass
from datetime import date from datetime import date
from typing import Generic, TypeVar from typing import Generic, TypeVar
from pydantic import BaseModel
from sdk.src.apis.hebe.student import HebePeriod, HebeStudent from sdk.src.apis.hebe.student import HebePeriod, HebeStudent
@dataclass class Period(BaseModel):
class Period:
id: int id: int
number: int number: int
current: bool current: bool
@ -27,8 +27,7 @@ class Period:
T = TypeVar("T") T = TypeVar("T")
@dataclass class Student(BaseModel, Generic[T]):
class Student(Generic[T]):
full_name: str full_name: str
is_parent: bool is_parent: bool
class_name: str class_name: str