schoolmospy 0.2.1__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/PKG-INFO +4 -7
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/README.md +4 -6
- schoolmospy-0.2.3/pyproject.toml +91 -0
- schoolmospy-0.2.3/schoolmospy/__init__.py +4 -0
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/core/basic_client.py +39 -15
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/core/events_client.py +9 -8
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/core/homeworks_client.py +4 -4
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/core/marks_client.py +4 -3
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/core/student_client.py +12 -15
- schoolmospy-0.2.3/schoolmospy/models/events.py +37 -0
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/models/homeworks.py +9 -9
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/models/marks.py +3 -3
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/models/profile.py +7 -7
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/models/userinfo.py +5 -7
- schoolmospy-0.2.3/schoolmospy/utils/exceptions.py +28 -0
- schoolmospy-0.2.3/schoolmospy/utils/logging.py +0 -0
- schoolmospy-0.2.1/pyproject.toml +0 -42
- schoolmospy-0.2.1/schoolmospy/__init__.py +0 -2
- schoolmospy-0.2.1/schoolmospy/models/events.py +0 -37
- schoolmospy-0.2.1/schoolmospy/utils/exceptions.py +0 -26
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/LICENSE +0 -0
- {schoolmospy-0.2.1 → schoolmospy-0.2.3}/schoolmospy/__main__.py +0 -0
- /schoolmospy-0.2.1/schoolmospy/utils/logging.py → /schoolmospy-0.2.3/schoolmospy/models/enums/usertype.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: schoolmospy
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A lightweight async Python wrapper for school.mos.ru APIs
|
|
5
5
|
License: GPL-3.0-only
|
|
6
6
|
License-File: LICENSE
|
|
@@ -67,19 +67,19 @@ async def main():
|
|
|
67
67
|
print(f"Name: {profile.name}")
|
|
68
68
|
|
|
69
69
|
# Get marks
|
|
70
|
-
marks = await client.marks.
|
|
70
|
+
marks = await client.marks.get(
|
|
71
71
|
from_date="2024-01-01",
|
|
72
72
|
to_date="2024-12-31"
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
# Get homeworks
|
|
76
|
-
homeworks = await client.homeworks.
|
|
76
|
+
homeworks = await client.homeworks.get(
|
|
77
77
|
from_date="2024-01-01",
|
|
78
78
|
to_date="2024-12-31"
|
|
79
79
|
)
|
|
80
80
|
|
|
81
81
|
# Get events/schedule
|
|
82
|
-
events = await client.events.
|
|
82
|
+
events = await client.events.get(
|
|
83
83
|
from_date="2024-01-01",
|
|
84
84
|
to_date="2024-12-31"
|
|
85
85
|
)
|
|
@@ -103,6 +103,3 @@ This project is licensed under the GPL-3.0 License. See the [LICENSE](LICENSE) f
|
|
|
103
103
|
Ivan Kriventsev - [xd2dd@icloud.com](mailto:xd2dd@icloud.com)
|
|
104
104
|
|
|
105
105
|
---
|
|
106
|
-
|
|
107
|
-
⭐ If you like this project, please give it a star on GitHub!
|
|
108
|
-
|
|
@@ -44,19 +44,19 @@ async def main():
|
|
|
44
44
|
print(f"Name: {profile.name}")
|
|
45
45
|
|
|
46
46
|
# Get marks
|
|
47
|
-
marks = await client.marks.
|
|
47
|
+
marks = await client.marks.get(
|
|
48
48
|
from_date="2024-01-01",
|
|
49
49
|
to_date="2024-12-31"
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
# Get homeworks
|
|
53
|
-
homeworks = await client.homeworks.
|
|
53
|
+
homeworks = await client.homeworks.get(
|
|
54
54
|
from_date="2024-01-01",
|
|
55
55
|
to_date="2024-12-31"
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
# Get events/schedule
|
|
59
|
-
events = await client.events.
|
|
59
|
+
events = await client.events.get(
|
|
60
60
|
from_date="2024-01-01",
|
|
61
61
|
to_date="2024-12-31"
|
|
62
62
|
)
|
|
@@ -79,6 +79,4 @@ This project is licensed under the GPL-3.0 License. See the [LICENSE](LICENSE) f
|
|
|
79
79
|
|
|
80
80
|
Ivan Kriventsev - [xd2dd@icloud.com](mailto:xd2dd@icloud.com)
|
|
81
81
|
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
⭐ If you like this project, please give it a star on GitHub!
|
|
82
|
+
---
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "schoolmospy"
|
|
3
|
+
version = "0.2.3"
|
|
4
|
+
description = "A lightweight async Python wrapper for school.mos.ru APIs"
|
|
5
|
+
authors = [{ name = "Ivan Kriventsev", email = "xd2dd@icloud.com" }]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
license = { text = "GPL-3.0-only" }
|
|
8
|
+
requires-python = ">=3.12"
|
|
9
|
+
|
|
10
|
+
keywords = ["school", "mos", "api", "education", "async", "wrapper", "python"]
|
|
11
|
+
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3.12",
|
|
14
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
dependencies = [
|
|
21
|
+
"aiohttp >=3.12.15,<4.0.0",
|
|
22
|
+
"pydantic >=2.11.9,<3.0.0",
|
|
23
|
+
"httpx (>=0.28.1,<0.29.0)",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
packages = [{ include = "schoolmospy" }]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
"Homepage" = "https://github.com/xd2dd/schoolmospy"
|
|
30
|
+
"Documentation" = "https://xd2dd.github.io/schoolmospy"
|
|
31
|
+
"Issues" = "https://github.com/xd2dd/schoolmospy/issues"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
36
|
+
build-backend = "poetry.core.masonry.api"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.group.dev.dependencies]
|
|
39
|
+
ruff = "^0.13.2"
|
|
40
|
+
mypy = "^1.18.2"
|
|
41
|
+
black = "^25.9.0"
|
|
42
|
+
isort = "^6.0.1"
|
|
43
|
+
pre-commit = "^4.0.0"
|
|
44
|
+
|
|
45
|
+
[tool.black]
|
|
46
|
+
line-length = 100
|
|
47
|
+
target-version = ["py312"]
|
|
48
|
+
|
|
49
|
+
[tool.isort]
|
|
50
|
+
profile = "black"
|
|
51
|
+
line_length = 100
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 100
|
|
55
|
+
target-version = "py312"
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = [
|
|
59
|
+
"E", # pycodestyle errors
|
|
60
|
+
"F", # pyflakes
|
|
61
|
+
"W", # pycodestyle warnings
|
|
62
|
+
"I", # isort
|
|
63
|
+
"N", # pep8-naming
|
|
64
|
+
"UP", # pyupgrade
|
|
65
|
+
"ANN", # flake8-annotations
|
|
66
|
+
"B", # flake8-bugbear
|
|
67
|
+
"A", # flake8-builtins
|
|
68
|
+
"C4", # flake8-comprehensions
|
|
69
|
+
"RET", # flake8-return
|
|
70
|
+
"SIM", # flake8-simplify
|
|
71
|
+
"ARG", # flake8-unused-arguments
|
|
72
|
+
"PTH", # flake8-use-pathlib
|
|
73
|
+
"RUF", # Ruff-specific rules
|
|
74
|
+
]
|
|
75
|
+
ignore = [
|
|
76
|
+
"ANN101", # Missing type annotation for self in method
|
|
77
|
+
"ANN102", # Missing type annotation for cls in classmethod
|
|
78
|
+
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
|
|
79
|
+
"TRY003", # Avoid specifying long messages outside the exception class
|
|
80
|
+
"EM101", # Exception must not use a string literal
|
|
81
|
+
"PLR2004", # Magic value used in comparison
|
|
82
|
+
"COM812", # Trailing comma missing (conflicts with formatter)
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.mypy]
|
|
86
|
+
python_version = "3.12"
|
|
87
|
+
ignore_missing_imports = true
|
|
88
|
+
warn_return_any = false
|
|
89
|
+
warn_unused_ignores = true
|
|
90
|
+
disallow_untyped_defs = false
|
|
91
|
+
check_untyped_defs = true
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
1
3
|
import httpx
|
|
2
|
-
from typing import Any, Optional, Type
|
|
3
4
|
from pydantic import BaseModel
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
from schoolmospy.utils.exceptions import APIError, AuthError, HTTPError, NotFoundError, ServerError
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class BasicClient:
|
|
8
10
|
def __init__(
|
|
9
11
|
self,
|
|
10
12
|
base_url: str,
|
|
11
|
-
token:
|
|
12
|
-
profile_id:
|
|
13
|
+
token: str | None = None,
|
|
14
|
+
profile_id: int | None = None,
|
|
13
15
|
profile_type: str = "student",
|
|
14
16
|
timeout: float = 15.0,
|
|
15
|
-
):
|
|
17
|
+
) -> None:
|
|
16
18
|
self.base_url = base_url.rstrip("/")
|
|
17
19
|
self.token = token
|
|
18
20
|
self.profile_id = profile_id
|
|
19
21
|
self.profile_type = profile_type
|
|
20
22
|
self.timeout = timeout
|
|
23
|
+
self.extra_headers: dict[str, str] = {}
|
|
21
24
|
|
|
22
25
|
@property
|
|
23
26
|
def headers(self) -> dict[str, str]:
|
|
@@ -33,10 +36,15 @@ class BasicClient:
|
|
|
33
36
|
headers["Profile-Id"] = str(self.profile_id)
|
|
34
37
|
if self.profile_type:
|
|
35
38
|
headers["Profile-Type"] = self.profile_type
|
|
36
|
-
|
|
39
|
+
if self.extra_headers:
|
|
40
|
+
headers.update(self.extra_headers)
|
|
37
41
|
return headers
|
|
38
42
|
|
|
39
|
-
async def _handle_response(
|
|
43
|
+
async def _handle_response(
|
|
44
|
+
self,
|
|
45
|
+
response: httpx.Response,
|
|
46
|
+
response_model: type[BaseModel] | None = None,
|
|
47
|
+
) -> Any:
|
|
40
48
|
if response.is_success:
|
|
41
49
|
if response_model:
|
|
42
50
|
return response_model.model_validate(response.json())
|
|
@@ -47,25 +55,41 @@ class BasicClient:
|
|
|
47
55
|
|
|
48
56
|
if status == 401:
|
|
49
57
|
raise AuthError("Unauthorized or invalid token", status, text)
|
|
50
|
-
|
|
58
|
+
if status == 404:
|
|
51
59
|
raise NotFoundError("Resource not found", status, text)
|
|
52
|
-
|
|
60
|
+
if status >= 500:
|
|
53
61
|
raise ServerError("Server error", status, text)
|
|
54
|
-
|
|
55
|
-
raise HTTPError(f"Unexpected response ({status})", status, text)
|
|
62
|
+
raise HTTPError(f"Unexpected response ({status})", status, text)
|
|
56
63
|
|
|
57
|
-
async def get(
|
|
64
|
+
async def get(
|
|
65
|
+
self,
|
|
66
|
+
endpoint: str,
|
|
67
|
+
response_model: type[BaseModel] | None = None,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> Any:
|
|
58
70
|
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
59
|
-
|
|
71
|
+
headers = self.headers
|
|
72
|
+
if "headers" in kwargs:
|
|
73
|
+
headers = {**headers, **kwargs.pop("headers")}
|
|
74
|
+
async with httpx.AsyncClient(headers=headers, timeout=self.timeout) as client:
|
|
60
75
|
try:
|
|
61
76
|
resp = await client.get(url, **kwargs)
|
|
62
77
|
except httpx.RequestError as e:
|
|
63
78
|
raise APIError(f"Request failed: {e}") from e
|
|
64
79
|
return await self._handle_response(resp, response_model)
|
|
65
80
|
|
|
66
|
-
async def post(
|
|
81
|
+
async def post(
|
|
82
|
+
self,
|
|
83
|
+
endpoint: str,
|
|
84
|
+
data: Any = None,
|
|
85
|
+
response_model: type[BaseModel] | None = None,
|
|
86
|
+
**kwargs: Any,
|
|
87
|
+
) -> Any:
|
|
67
88
|
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
68
|
-
|
|
89
|
+
headers = self.headers
|
|
90
|
+
if "headers" in kwargs:
|
|
91
|
+
headers = {**headers, **kwargs.pop("headers")}
|
|
92
|
+
async with httpx.AsyncClient(headers=headers, timeout=self.timeout) as client:
|
|
69
93
|
try:
|
|
70
94
|
resp = await client.post(url, json=data, **kwargs)
|
|
71
95
|
except httpx.RequestError as e:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
|
|
2
3
|
from schoolmospy.core.basic_client import BasicClient
|
|
3
4
|
from schoolmospy.models.events import Events
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class EventClient:
|
|
7
|
-
def __init__(self, client: BasicClient):
|
|
8
|
+
def __init__(self, client: BasicClient) -> None:
|
|
8
9
|
"""
|
|
9
10
|
Initialization the EventClient instance.
|
|
10
11
|
|
|
@@ -14,7 +15,7 @@ class EventClient:
|
|
|
14
15
|
"""
|
|
15
16
|
self.client = client
|
|
16
17
|
|
|
17
|
-
async def get(self, from_date: datetime, to_date: datetime, contingent_guid: str
|
|
18
|
+
async def get(self, from_date: datetime, to_date: datetime, contingent_guid: str) -> Events:
|
|
18
19
|
"""
|
|
19
20
|
Method for getting events done within a certain period of time
|
|
20
21
|
|
|
@@ -36,7 +37,7 @@ class EventClient:
|
|
|
36
37
|
from schoolmospy.clients.student_client import StudentClient
|
|
37
38
|
|
|
38
39
|
client = StudentClient(token="YOUR_TOKEN", profile_id=17234613)
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
me = await client.get_me()
|
|
41
42
|
|
|
42
43
|
events = await client.events.get(
|
|
@@ -48,15 +49,15 @@ class EventClient:
|
|
|
48
49
|
```
|
|
49
50
|
"""
|
|
50
51
|
|
|
51
|
-
|
|
52
52
|
return await self.client.get(
|
|
53
53
|
"/api/eventcalendar/v1/api/events",
|
|
54
54
|
Events,
|
|
55
55
|
params={
|
|
56
|
-
"
|
|
57
|
-
"
|
|
56
|
+
"begin_date": from_date.strftime("%Y-%m-%d"),
|
|
57
|
+
"end_date": to_date.strftime("%Y-%m-%d"),
|
|
58
58
|
"person_ids": contingent_guid,
|
|
59
59
|
"expand": "marks,homework,absence_reason_id,health_status,nonattendance_reason_id",
|
|
60
|
-
"source_types": "PLAN,AE,EC,EVENTS,AFISHA,ORGANIZER,OLYMPIAD,PROF"
|
|
61
|
-
}
|
|
60
|
+
"source_types": "PLAN,AE,EC,EVENTS,AFISHA,ORGANIZER,OLYMPIAD,PROF",
|
|
61
|
+
},
|
|
62
|
+
headers={"X-Mes-Role": "student"},
|
|
62
63
|
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
|
|
2
3
|
from schoolmospy.core.basic_client import BasicClient
|
|
3
4
|
from schoolmospy.models.homeworks import Homeworks
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class HomeworkClient:
|
|
7
|
-
def __init__(self, client: BasicClient):
|
|
8
|
+
def __init__(self, client: BasicClient) -> None:
|
|
8
9
|
"""
|
|
9
10
|
Initialization the HomeworkClient instance.
|
|
10
11
|
|
|
@@ -43,13 +44,12 @@ class HomeworkClient:
|
|
|
43
44
|
```
|
|
44
45
|
"""
|
|
45
46
|
|
|
46
|
-
|
|
47
47
|
return await self.client.get(
|
|
48
48
|
"/api/family/web/v1/homeworks",
|
|
49
49
|
Homeworks,
|
|
50
50
|
params={
|
|
51
51
|
"from": from_date.strftime("%Y-%m-%d"),
|
|
52
52
|
"to": to_date.strftime("%Y-%m-%d"),
|
|
53
|
-
"student_id": self.client.profile_id
|
|
54
|
-
}
|
|
53
|
+
"student_id": self.client.profile_id,
|
|
54
|
+
},
|
|
55
55
|
)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
+
|
|
2
3
|
from schoolmospy.core.basic_client import BasicClient
|
|
3
4
|
from schoolmospy.models.marks import Marks
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class MarksClient:
|
|
7
|
-
def __init__(self, client: BasicClient):
|
|
8
|
+
def __init__(self, client: BasicClient) -> None:
|
|
8
9
|
self.client = client
|
|
9
10
|
|
|
10
11
|
async def get(self, from_date: datetime, to_date: datetime) -> Marks:
|
|
@@ -41,6 +42,6 @@ class MarksClient:
|
|
|
41
42
|
params={
|
|
42
43
|
"from": from_date.strftime("%Y-%m-%d"),
|
|
43
44
|
"to": to_date.strftime("%Y-%m-%d"),
|
|
44
|
-
"student_id": self.client.profile_id
|
|
45
|
-
}
|
|
45
|
+
"student_id": self.client.profile_id,
|
|
46
|
+
},
|
|
46
47
|
)
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
1
|
from schoolmospy.core.basic_client import BasicClient
|
|
3
|
-
from schoolmospy.
|
|
4
|
-
from schoolmospy.models.userinfo import Userinfo
|
|
2
|
+
from schoolmospy.core.events_client import EventClient
|
|
5
3
|
from schoolmospy.core.homeworks_client import HomeworkClient
|
|
6
4
|
from schoolmospy.core.marks_client import MarksClient
|
|
7
|
-
from schoolmospy.
|
|
5
|
+
from schoolmospy.models.profile import Profile
|
|
6
|
+
from schoolmospy.models.userinfo import Userinfo
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
class StudentClient(BasicClient):
|
|
11
|
-
def __init__(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
base_url: str = "https://school.mos.ru",
|
|
13
|
+
token: str | None = None,
|
|
14
|
+
profile_id: int | None = None,
|
|
15
|
+
profile_type: str = "student",
|
|
16
|
+
timeout: float = 15.0,
|
|
17
|
+
) -> None:
|
|
17
18
|
super().__init__(base_url, token, profile_id, profile_type, timeout)
|
|
18
19
|
self.homeworks = HomeworkClient(self)
|
|
19
20
|
self.marks = MarksClient(self)
|
|
20
21
|
self.events = EventClient(self)
|
|
21
22
|
|
|
22
|
-
|
|
23
23
|
async def get_me(self) -> Profile:
|
|
24
24
|
"""
|
|
25
25
|
Get the current user's profile information.
|
|
@@ -39,7 +39,4 @@ class StudentClient(BasicClient):
|
|
|
39
39
|
Returns:
|
|
40
40
|
Userinfo: Object with basic user information
|
|
41
41
|
"""
|
|
42
|
-
return await self.get(
|
|
43
|
-
"/v1/oauth/userinfo",
|
|
44
|
-
Userinfo
|
|
45
|
-
)
|
|
42
|
+
return await self.get("/v1/oauth/userinfo", Userinfo)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventItem(BaseModel):
|
|
9
|
+
id: int
|
|
10
|
+
source_id: str
|
|
11
|
+
source: str
|
|
12
|
+
start_at: str
|
|
13
|
+
finish_at: str
|
|
14
|
+
cancelled: bool | None = None
|
|
15
|
+
lesson_type: str | None = None
|
|
16
|
+
course_lesson_type: Any | None = None
|
|
17
|
+
lesson_form: Any | None = None
|
|
18
|
+
replaced: bool | None = None
|
|
19
|
+
room_name: str | None
|
|
20
|
+
room_number: str | None
|
|
21
|
+
subject_id: int | None = None
|
|
22
|
+
subject_name: str
|
|
23
|
+
link_to_join: Any | None = None
|
|
24
|
+
health_status: Any
|
|
25
|
+
absence_reason_id: Any
|
|
26
|
+
nonattendance_reason_id: Any
|
|
27
|
+
homework: Any | None = None
|
|
28
|
+
marks: Any | None = None
|
|
29
|
+
is_missed_lesson: bool
|
|
30
|
+
esz_field_id: int | None = None
|
|
31
|
+
lesson_theme: Any | None = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Events(BaseModel):
|
|
35
|
+
total_count: int
|
|
36
|
+
response: list[EventItem]
|
|
37
|
+
errors: Any
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
@@ -11,12 +11,12 @@ class Url(BaseModel):
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class Material(BaseModel):
|
|
14
|
-
uuid:
|
|
14
|
+
uuid: str | None
|
|
15
15
|
type: str
|
|
16
|
-
selected_mode:
|
|
16
|
+
selected_mode: str | None
|
|
17
17
|
type_name: str
|
|
18
|
-
id:
|
|
19
|
-
urls:
|
|
18
|
+
id: int | None
|
|
19
|
+
urls: list[Url]
|
|
20
20
|
description: Any
|
|
21
21
|
content_type: Any
|
|
22
22
|
title: str
|
|
@@ -27,11 +27,11 @@ class Material(BaseModel):
|
|
|
27
27
|
class HomeworkItem(BaseModel):
|
|
28
28
|
type: str
|
|
29
29
|
description: str
|
|
30
|
-
comments:
|
|
31
|
-
materials:
|
|
30
|
+
comments: list
|
|
31
|
+
materials: list[Material]
|
|
32
32
|
homework: str
|
|
33
33
|
homework_entry_student_id: int
|
|
34
|
-
attachments:
|
|
34
|
+
attachments: list
|
|
35
35
|
subject_id: int
|
|
36
36
|
group_id: int
|
|
37
37
|
date: str
|
|
@@ -49,4 +49,4 @@ class HomeworkItem(BaseModel):
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
class Homeworks(BaseModel):
|
|
52
|
-
payload:
|
|
52
|
+
payload: list[HomeworkItem]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
@@ -23,7 +23,7 @@ class Value(BaseModel):
|
|
|
23
23
|
class MarkItem(BaseModel):
|
|
24
24
|
id: int
|
|
25
25
|
value: str
|
|
26
|
-
values:
|
|
26
|
+
values: list[Value]
|
|
27
27
|
comment: str
|
|
28
28
|
weight: int
|
|
29
29
|
point_date: Any
|
|
@@ -42,4 +42,4 @@ class MarkItem(BaseModel):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class Marks(BaseModel):
|
|
45
|
-
payload:
|
|
45
|
+
payload: list[MarkItem]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
@@ -44,7 +44,7 @@ class Representative(BaseModel):
|
|
|
44
44
|
middle_name: str
|
|
45
45
|
type_id: int
|
|
46
46
|
type: str
|
|
47
|
-
email:
|
|
47
|
+
email: str | None
|
|
48
48
|
phone: str
|
|
49
49
|
snils: str
|
|
50
50
|
|
|
@@ -74,9 +74,9 @@ class Child(BaseModel):
|
|
|
74
74
|
class_unit_id: int
|
|
75
75
|
class_uid: str
|
|
76
76
|
age: int
|
|
77
|
-
groups:
|
|
78
|
-
representatives:
|
|
79
|
-
sections:
|
|
77
|
+
groups: list[Group]
|
|
78
|
+
representatives: list[Representative]
|
|
79
|
+
sections: list[Section]
|
|
80
80
|
sudir_account_exists: bool
|
|
81
81
|
sudir_login: Any
|
|
82
82
|
is_legal_representative: bool
|
|
@@ -90,5 +90,5 @@ class Child(BaseModel):
|
|
|
90
90
|
|
|
91
91
|
class Profile(BaseModel):
|
|
92
92
|
profile: _Profile
|
|
93
|
-
children:
|
|
94
|
-
hash: str
|
|
93
|
+
children: list[Child]
|
|
94
|
+
hash: str
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
3
|
from pydantic import BaseModel, Field
|
|
6
4
|
|
|
7
5
|
|
|
@@ -23,7 +21,7 @@ class Class(BaseModel):
|
|
|
23
21
|
parallel: Parallel
|
|
24
22
|
organization: Organization
|
|
25
23
|
education_stage_id: int
|
|
26
|
-
staff_ids:
|
|
24
|
+
staff_ids: list[int]
|
|
27
25
|
|
|
28
26
|
|
|
29
27
|
class ServiceType(BaseModel):
|
|
@@ -34,7 +32,7 @@ class ServiceType(BaseModel):
|
|
|
34
32
|
class EducationItem(BaseModel):
|
|
35
33
|
training_begin_at: str
|
|
36
34
|
training_end_at: str
|
|
37
|
-
class_: Class = Field(..., alias=
|
|
35
|
+
class_: Class = Field(..., alias="class")
|
|
38
36
|
service_type: ServiceType
|
|
39
37
|
|
|
40
38
|
|
|
@@ -48,9 +46,9 @@ class Userinfo(BaseModel):
|
|
|
48
46
|
phone: str
|
|
49
47
|
name: str
|
|
50
48
|
gender: str
|
|
51
|
-
education:
|
|
52
|
-
children:
|
|
53
|
-
agents:
|
|
49
|
+
education: list[EducationItem]
|
|
50
|
+
children: list
|
|
51
|
+
agents: list[str]
|
|
54
52
|
mesh_id: str
|
|
55
53
|
given_name: str
|
|
56
54
|
family_name: str
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class APIError(Exception):
|
|
5
|
+
def __init__(
|
|
6
|
+
self,
|
|
7
|
+
message: str,
|
|
8
|
+
status_code: int | None = None,
|
|
9
|
+
response: Any | None = None,
|
|
10
|
+
) -> None:
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
self.status_code = status_code
|
|
13
|
+
self.response = response
|
|
14
|
+
|
|
15
|
+
def __str__(self) -> str:
|
|
16
|
+
return f"{self.__class__.__name__}: {self.args[0]} ({self.status_code}) {self.response}"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AuthError(APIError): ...
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class NotFoundError(APIError): ...
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ServerError(APIError): ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class HTTPError(APIError): ...
|
|
File without changes
|
schoolmospy-0.2.1/pyproject.toml
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "schoolmospy"
|
|
3
|
-
version = "0.2.1"
|
|
4
|
-
description = "A lightweight async Python wrapper for school.mos.ru APIs"
|
|
5
|
-
authors = [{ name = "Ivan Kriventsev", email = "xd2dd@icloud.com" }]
|
|
6
|
-
readme = "README.md"
|
|
7
|
-
license = { text = "GPL-3.0-only" }
|
|
8
|
-
requires-python = ">=3.12"
|
|
9
|
-
|
|
10
|
-
keywords = ["school", "mos", "api", "education", "async", "wrapper", "python"]
|
|
11
|
-
|
|
12
|
-
classifiers = [
|
|
13
|
-
"Programming Language :: Python :: 3.12",
|
|
14
|
-
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
15
|
-
"Operating System :: OS Independent",
|
|
16
|
-
"Intended Audience :: Developers",
|
|
17
|
-
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
dependencies = [
|
|
21
|
-
"aiohttp >=3.12.15,<4.0.0",
|
|
22
|
-
"pydantic >=2.11.9,<3.0.0",
|
|
23
|
-
"httpx (>=0.28.1,<0.29.0)",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
packages = [{ include = "schoolmospy" }]
|
|
27
|
-
|
|
28
|
-
[project.urls]
|
|
29
|
-
"Homepage" = "https://github.com/xd2dd/schoolmospy"
|
|
30
|
-
"Documentation" = "https://xd2dd.github.io/schoolmospy"
|
|
31
|
-
"Issues" = "https://github.com/xd2dd/schoolmospy/issues"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
[build-system]
|
|
35
|
-
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
36
|
-
build-backend = "poetry.core.masonry.api"
|
|
37
|
-
|
|
38
|
-
[tool.poetry.group.dev.dependencies]
|
|
39
|
-
ruff = "^0.13.2"
|
|
40
|
-
mypy = "^1.18.2"
|
|
41
|
-
black = "^25.9.0"
|
|
42
|
-
isort = "^6.0.1"
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any, List, Optional
|
|
4
|
-
|
|
5
|
-
from pydantic import BaseModel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class EventItem(BaseModel):
|
|
9
|
-
id: int
|
|
10
|
-
source_id: str
|
|
11
|
-
source: str
|
|
12
|
-
start_at: str
|
|
13
|
-
finish_at: str
|
|
14
|
-
cancelled: Optional[bool] = None
|
|
15
|
-
lesson_type: Optional[str] = None
|
|
16
|
-
course_lesson_type: Optional[Any] = None
|
|
17
|
-
lesson_form: Optional[Any] = None
|
|
18
|
-
replaced: Optional[bool] = None
|
|
19
|
-
room_name: Optional[str]
|
|
20
|
-
room_number: Optional[str]
|
|
21
|
-
subject_id: Optional[int] = None
|
|
22
|
-
subject_name: str
|
|
23
|
-
link_to_join: Optional[Any] = None
|
|
24
|
-
health_status: Any
|
|
25
|
-
absence_reason_id: Any
|
|
26
|
-
nonattendance_reason_id: Any
|
|
27
|
-
homework: Optional[Any] = None
|
|
28
|
-
marks: Optional[Any] = None
|
|
29
|
-
is_missed_lesson: bool
|
|
30
|
-
esz_field_id: Optional[int] = None
|
|
31
|
-
lesson_theme: Optional[Any] = None
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class Events(BaseModel):
|
|
35
|
-
total_count: int
|
|
36
|
-
response: List[EventItem]
|
|
37
|
-
errors: Any
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from typing import Optional, Any
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class APIError(Exception):
|
|
5
|
-
def __init__(self, message: str, status_code: Optional[int] = None, response: Optional[Any] = None):
|
|
6
|
-
super().__init__(message)
|
|
7
|
-
self.status_code = status_code
|
|
8
|
-
self.response = response
|
|
9
|
-
|
|
10
|
-
def __str__(self):
|
|
11
|
-
return f"{self.__class__.__name__}: {self.args[0]} (status={self.status_code})"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class AuthError(APIError):
|
|
15
|
-
...
|
|
16
|
-
|
|
17
|
-
class NotFoundError(APIError):
|
|
18
|
-
...
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ServerError(APIError):
|
|
22
|
-
...
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class HTTPError(APIError):
|
|
26
|
-
...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|