Skip to content

Commit 5ea25c7

Browse files
authored
feat: adding pyi files to provide type hints in IDEs (#25)
* Rework the results a little bit to account for Mauros missing component. * Added stub generation script * classes loading correctly * WIP (not working) * Generating a nice stub already * Already working really really nice! * Already working really really really nice! * Only type attribute is generated wrongly * Different approach, more unified * Working nicely * WIP * WIP (but not working yet) * results is fine now * implement type checking * Working on "courses" * re-ran everything * Fix floating point comparisons * reformat * Resolving sonar issues * Resolving sonar issues
1 parent 33426f6 commit 5ea25c7

File tree

8 files changed

+651
-7
lines changed

8 files changed

+651
-7
lines changed

dev/generate_stubs.py

Lines changed: 477 additions & 0 deletions
Large diffs are not rendered by default.

src/smartschool/courses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def download_to_dir(self, target_directory: Path, *, overwrite: bool = False) ->
168168
return self.download(target_directory / self.filename, overwrite=overwrite)
169169

170170
@overload
171-
def download(self, to_file: Path | str, overwrite: bool) -> Path: ... # noqa: FBT001
171+
def download(self, to_file: Path | str, *, overwrite: bool) -> Path: ...
172172

173173
@overload
174174
def download(self) -> bytes: ...

src/smartschool/courses.pyi

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Auto-generated stub file
2+
from __future__ import annotations
3+
4+
from collections.abc import Iterator
5+
from datetime import datetime
6+
from pathlib import Path
7+
from typing import overload
8+
9+
from . import objects
10+
from .objects import Course
11+
from .session import SessionMixin, Smartschool
12+
13+
class CourseCondensed(objects.CourseCondensed, SessionMixin):
14+
session: Smartschool
15+
name: str
16+
teacher: str
17+
url: str
18+
id: int
19+
platformId: int
20+
descr: str
21+
icon: str
22+
def __init__(
23+
self, session: Smartschool, name: str, teacher: str, url: str, id: int | None = None, platformId: int | None = None, descr: str = "", icon: str = ""
24+
): ...
25+
def __str__(self): ...
26+
27+
class TopNavCourses(SessionMixin):
28+
session: Smartschool
29+
def __init__(self, session: Smartschool): ...
30+
def __iter__(self) -> Iterator[CourseCondensed]: ...
31+
32+
class Courses(SessionMixin):
33+
session: Smartschool
34+
def __init__(self, session: Smartschool): ...
35+
def __iter__(self) -> Iterator[Course]: ...
36+
37+
class FileItem(SessionMixin):
38+
session: Smartschool
39+
parent: FolderItem
40+
id: int
41+
name: str
42+
mime_type: str
43+
size_kb: float | str
44+
last_modified: datetime | str
45+
download_url: str | None
46+
view_url: str | None
47+
def __init__(
48+
self,
49+
session: Smartschool,
50+
parent: FolderItem,
51+
id: int,
52+
name: str,
53+
mime_type: str,
54+
size_kb: float | str,
55+
last_modified: datetime | str,
56+
download_url: str | None = None,
57+
view_url: str | None = None,
58+
): ...
59+
def download_to_dir(self, target_directory: Path, overwrite: bool = False) -> Path: ...
60+
@overload
61+
def download(self, to_file: Path | str, *, overwrite: bool) -> Path: ...
62+
@overload
63+
def download(self) -> bytes: ...
64+
def download(self, to_file: Path | str | None = None, *, overwrite: bool = False) -> bytes | Path: ...
65+
66+
class InternetShortcut(FileItem):
67+
session: Smartschool
68+
parent: FolderItem
69+
id: int
70+
name: str
71+
mime_type: str
72+
size_kb: float | str
73+
last_modified: datetime | str
74+
download_url: str | None
75+
view_url: str | None
76+
link: str
77+
def __init__(
78+
self,
79+
session: Smartschool,
80+
parent: FolderItem,
81+
id: int,
82+
name: str,
83+
mime_type: str,
84+
size_kb: float | str,
85+
last_modified: datetime | str,
86+
download_url: str | None = None,
87+
view_url: str | None = None,
88+
link: str = "",
89+
): ...
90+
91+
class FolderItem(SessionMixin):
92+
session: Smartschool
93+
parent: FolderItem | None
94+
course: CourseCondensed
95+
name: str
96+
browse_url: str | None
97+
def __init__(self, session: Smartschool, parent: FolderItem | None, course: CourseCondensed, name: str, browse_url: str | None = None): ...

src/smartschool/objects.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,12 @@ class Component:
113113
abbreviation: String
114114

115115

116-
Teacher = _User
117-
Student = _User
116+
@dataclass
117+
class Teacher(_User): ...
118+
119+
120+
@dataclass
121+
class Student(_User): ...
118122

119123

120124
@dataclass
@@ -190,7 +194,7 @@ class ResultDetails:
190194
class CourseCondensed:
191195
name: String
192196
teacher: String
193-
url: Url
197+
url: Url = Field()
194198

195199
id: int = Field(repr=False, default=None)
196200
platformId: int = Field(repr=False, default=None)

src/smartschool/results.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1-
from collections.abc import Iterable, Iterator
1+
from __future__ import annotations
2+
3+
from collections.abc import Iterable
24
from dataclasses import dataclass
35
from itertools import count
46

57
from . import objects
68

79
__all__ = ["Results"]
810

11+
from typing import TYPE_CHECKING
12+
913
from .session import SessionMixin
1014

15+
if TYPE_CHECKING:
16+
from collections.abc import Iterator
17+
1118
RESULTS_PER_PAGE = 50
1219

1320

src/smartschool/results.pyi

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Auto-generated stub file
2+
from __future__ import annotations
3+
4+
from collections.abc import Iterable, Iterator
5+
from datetime import datetime
6+
from typing import Literal
7+
8+
from . import objects
9+
from .objects import Component, Course, Feedback, FeedbackFull, Period, ResultDetails, ResultGraphic, Teacher
10+
from .session import SessionMixin, Smartschool
11+
12+
class Result(objects.Result, SessionMixin):
13+
session: Smartschool
14+
identifier: str
15+
type: Literal["normal"]
16+
name: str
17+
graphic: ResultGraphic
18+
date: datetime
19+
gradebookOwner: Teacher
20+
component: Component | None
21+
courses: list[Course]
22+
period: Period
23+
feedback: list[Feedback]
24+
feedbacks: list[FeedbackFull]
25+
availabilityDate: datetime
26+
isPublished: bool
27+
doesCount: bool
28+
deleted: bool
29+
details: ResultDetails | None
30+
def __init__(
31+
self,
32+
session: Smartschool,
33+
identifier: str,
34+
type: Literal["normal"],
35+
name: str,
36+
graphic: ResultGraphic,
37+
date: datetime,
38+
gradebookOwner: Teacher,
39+
component: Component | None,
40+
courses: list[Course],
41+
period: Period,
42+
feedback: list[Feedback],
43+
feedbacks: list[FeedbackFull],
44+
availabilityDate: datetime,
45+
isPublished: bool,
46+
doesCount: bool,
47+
deleted: bool = False,
48+
details: ResultDetails | None = None,
49+
): ...
50+
def __getattribute__(self, name: str): ...
51+
52+
class Results(SessionMixin, Iterable[Result]):
53+
session: Smartschool
54+
def __init__(self, session: Smartschool): ...
55+
def __iter__(self) -> Iterator[Result]: ...

tests/requests/get/results/api/v1/evaluations/empty_return_value.json

Whitespace-only changes.

tests/results_tests.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from pathlib import Path
22

3+
import pytest
4+
35
from smartschool import Results, Smartschool
46

57

@@ -13,8 +15,8 @@ def test_results_normal_flow(mocker, session: Smartschool):
1315
result = sut[0]
1416
assert result.name == "Repetitie hoofdstuk 1"
1517
assert result.graphic.total_points == 18
16-
assert result.graphic.achieved_points == 13.5
17-
assert result.graphic.percentage == 0.75
18+
assert result.graphic.achieved_points == pytest.approx(13.5)
19+
assert result.graphic.percentage == pytest.approx(0.75)
1820

1921
assert result.details is not None
2022
assert result.details.class_.name == "3ENW"
@@ -24,7 +26,9 @@ def test_mauro_results(session: Smartschool, requests_mock):
2426
component_file = Path(__file__).parent / "requests/get/results/api/v1/evaluations/mauro_component.json"
2527
requests_mock.get("https://site/results/api/v1/evaluations/?pageNumber=1&itemsOnPage=50", content=component_file.read_bytes())
2628
sut = list(Results(session))
29+
2730
assert len(sut) == 1
31+
2832
result = sut[0]
2933
assert result.graphic.color == "olive"
3034
assert result.component is None

0 commit comments

Comments
 (0)