arpakitlib 1.4.0__py3-none-any.whl
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.
Potentially problematic release.
This version of arpakitlib might be problematic. Click here for more details.
- arpakitlib/AUTHOR.md +6 -0
- arpakitlib/LICENSE +201 -0
- arpakitlib/NOTICE +2 -0
- arpakitlib/README.md +6 -0
- arpakitlib/__init__.py +0 -0
- arpakitlib/ar_additional_model_util.py +8 -0
- arpakitlib/ar_aiogram_util.py +363 -0
- arpakitlib/ar_arpakit_lib_module_util.py +150 -0
- arpakitlib/ar_arpakit_schedule_uust_api_client.py +527 -0
- arpakitlib/ar_arpakitlib_info.py +11 -0
- arpakitlib/ar_base64_util.py +30 -0
- arpakitlib/ar_base_worker.py +77 -0
- arpakitlib/ar_cache_file.py +124 -0
- arpakitlib/ar_datetime_util.py +38 -0
- arpakitlib/ar_dict_util.py +24 -0
- arpakitlib/ar_dream_ai_api_client.py +120 -0
- arpakitlib/ar_encrypt_and_decrypt_util.py +23 -0
- arpakitlib/ar_enumeration.py +76 -0
- arpakitlib/ar_fastapi_static/redoc/redoc.standalone.js +1826 -0
- arpakitlib/ar_fastapi_static/swagger-ui/favicon-16x16.png +0 -0
- arpakitlib/ar_fastapi_static/swagger-ui/favicon-32x32.png +0 -0
- arpakitlib/ar_fastapi_static/swagger-ui/index.css +16 -0
- arpakitlib/ar_fastapi_static/swagger-ui/index.html +19 -0
- arpakitlib/ar_fastapi_static/swagger-ui/oauth2-redirect.html +79 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-initializer.js +20 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js +2 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js.map +1 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js +3 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js.map +1 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js +2 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js.map +1 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js +2 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js.map +1 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css +3 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css.map +1 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js +2 -0
- arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js.map +1 -0
- arpakitlib/ar_fastapi_util.py +294 -0
- arpakitlib/ar_file_storage_in_dir.py +127 -0
- arpakitlib/ar_generate_env_example.py +16 -0
- arpakitlib/ar_hash_util.py +19 -0
- arpakitlib/ar_http_request_util.py +75 -0
- arpakitlib/ar_ip_util.py +50 -0
- arpakitlib/ar_json_db.py +231 -0
- arpakitlib/ar_json_util.py +28 -0
- arpakitlib/ar_jwt_util.py +38 -0
- arpakitlib/ar_list_of_dicts_to_xlsx.py +32 -0
- arpakitlib/ar_list_util.py +26 -0
- arpakitlib/ar_logging_util.py +45 -0
- arpakitlib/ar_mongodb_util.py +143 -0
- arpakitlib/ar_need_type_util.py +58 -0
- arpakitlib/ar_openai_util.py +59 -0
- arpakitlib/ar_parse_command.py +102 -0
- arpakitlib/ar_postgresql_util.py +45 -0
- arpakitlib/ar_run_cmd.py +48 -0
- arpakitlib/ar_safe_sleep.py +23 -0
- arpakitlib/ar_schedule_uust_api_client.py +216 -0
- arpakitlib/ar_sqlalchemy_util.py +124 -0
- arpakitlib/ar_ssh_runner.py +260 -0
- arpakitlib/ar_str_util.py +79 -0
- arpakitlib/ar_type_util.py +82 -0
- arpakitlib/ar_yookassa_api_client.py +224 -0
- arpakitlib/ar_zabbix_util.py +190 -0
- arpakitlib-1.4.0.dist-info/LICENSE +201 -0
- arpakitlib-1.4.0.dist-info/METADATA +327 -0
- arpakitlib-1.4.0.dist-info/NOTICE +2 -0
- arpakitlib-1.4.0.dist-info/RECORD +68 -0
- arpakitlib-1.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
from importlib.util import spec_from_file_location, module_from_spec
|
|
6
|
+
from typing import NamedTuple, Any, Iterator, Optional
|
|
7
|
+
|
|
8
|
+
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ArpakitLibModule(NamedTuple):
|
|
12
|
+
module_name: str
|
|
13
|
+
module_version: Optional[str]
|
|
14
|
+
module_content: str
|
|
15
|
+
module_hash: str
|
|
16
|
+
module_has_error: bool
|
|
17
|
+
module_exception: BaseException | None
|
|
18
|
+
filename: str
|
|
19
|
+
filepath: str
|
|
20
|
+
|
|
21
|
+
def simple_dict(self) -> dict[str, Any]:
|
|
22
|
+
return {
|
|
23
|
+
"module_name": self.module_name,
|
|
24
|
+
"module_version": self.module_version,
|
|
25
|
+
"module_content": self.module_content,
|
|
26
|
+
"module_hash": self.module_hash,
|
|
27
|
+
"module_has_error": self.module_has_error,
|
|
28
|
+
"module_exception": self.module_exception,
|
|
29
|
+
"filename": self.filename,
|
|
30
|
+
"filepath": self.filepath,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ArpakitLibModules(NamedTuple):
|
|
35
|
+
arpakit_lib_modules: list[ArpakitLibModule]
|
|
36
|
+
|
|
37
|
+
def __len__(self):
|
|
38
|
+
return len(self.arpakit_lib_modules)
|
|
39
|
+
|
|
40
|
+
def __repr__(self):
|
|
41
|
+
return f"ArpakitLibModules (len={len(self.arpakit_lib_modules)})"
|
|
42
|
+
|
|
43
|
+
def __iter__(self) -> Iterator[ArpakitLibModule]:
|
|
44
|
+
for arpakit_lib_module in self.arpakit_lib_modules:
|
|
45
|
+
yield arpakit_lib_module
|
|
46
|
+
|
|
47
|
+
def __hash__(self) -> str:
|
|
48
|
+
return self.modules_hash()
|
|
49
|
+
|
|
50
|
+
def modules_hash(self) -> str:
|
|
51
|
+
return hashlib.sha256(
|
|
52
|
+
json.dumps(self.module_name_to_module_hash()).encode("utf-8")
|
|
53
|
+
).hexdigest()
|
|
54
|
+
|
|
55
|
+
def simple_dict(self) -> dict[str, list[dict[str, Any]]]:
|
|
56
|
+
return {
|
|
57
|
+
"arpakit_lib_modules": [arpakit_lib_module.simple_dict() for arpakit_lib_module in self.arpakit_lib_modules]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
def module_name_to_module_simple_dict(self) -> dict[str, dict]:
|
|
61
|
+
return {module.module_name: module.simple_dict() for module in self.arpakit_lib_modules}
|
|
62
|
+
|
|
63
|
+
def module_name_to_module_version(self) -> dict[str, str]:
|
|
64
|
+
return {module.module_name: module.module_version for module in self.arpakit_lib_modules}
|
|
65
|
+
|
|
66
|
+
def module_name_to_module_version_and_module_has_errors(self) -> dict[str, dict[str, Any]]:
|
|
67
|
+
return {
|
|
68
|
+
module.module_name: {
|
|
69
|
+
"module_version": module.module_version,
|
|
70
|
+
"module_has_errors": module.module_has_error,
|
|
71
|
+
} for module in self.arpakit_lib_modules
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def module_names_who_has_errors(self) -> list[str]:
|
|
75
|
+
return [
|
|
76
|
+
arpakit_lib_module.module_name
|
|
77
|
+
for arpakit_lib_module in self.arpakit_lib_modules
|
|
78
|
+
if arpakit_lib_module.module_has_error
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
def module_name_to_module_exception(self, *, filter_module_has_error: bool = False) -> dict[str, BaseException]:
|
|
82
|
+
if filter_module_has_error:
|
|
83
|
+
return {
|
|
84
|
+
module.module_name: module.module_exception
|
|
85
|
+
for module in self.arpakit_lib_modules if module.module_has_error
|
|
86
|
+
}
|
|
87
|
+
else:
|
|
88
|
+
return {
|
|
89
|
+
module.module_name: module.module_exception
|
|
90
|
+
for module in self.arpakit_lib_modules
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def module_name_to_module_hash(self) -> dict[str, str]:
|
|
94
|
+
return {module.module_name: module.module_hash for module in self.arpakit_lib_modules}
|
|
95
|
+
|
|
96
|
+
def module_name_to_module_content(self) -> dict[str, str]:
|
|
97
|
+
return {module.module_name: module.module_content for module in self.arpakit_lib_modules}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_arpakit_lib_modules() -> ArpakitLibModules:
|
|
101
|
+
base_dirpath: str = str(pathlib.Path(__file__).parent)
|
|
102
|
+
|
|
103
|
+
filenames: list[str] = os.listdir(base_dirpath)
|
|
104
|
+
filenames.sort()
|
|
105
|
+
|
|
106
|
+
arpakit_lib_modules = ArpakitLibModules(arpakit_lib_modules=[])
|
|
107
|
+
|
|
108
|
+
for filename in filenames:
|
|
109
|
+
if not filename.endswith(".py") or filename == "__init__.py":
|
|
110
|
+
continue
|
|
111
|
+
module_name = filename.replace(".py", "")
|
|
112
|
+
module_version: Optional[str] = None
|
|
113
|
+
try:
|
|
114
|
+
spec = spec_from_file_location(module_name, os.path.join(base_dirpath, filename))
|
|
115
|
+
module = module_from_spec(spec)
|
|
116
|
+
spec.loader.exec_module(module)
|
|
117
|
+
module_version = getattr(module, "_ARPAKIT_LIB_MODULE_VERSION", None)
|
|
118
|
+
module_has_error = False
|
|
119
|
+
module_exception = None
|
|
120
|
+
except BaseException as error:
|
|
121
|
+
module_has_error = True
|
|
122
|
+
module_exception = error
|
|
123
|
+
if module_version is not None and not isinstance(module_version, str):
|
|
124
|
+
continue
|
|
125
|
+
if module_name in [
|
|
126
|
+
arpakit_lib_module.module_name for arpakit_lib_module in arpakit_lib_modules.arpakit_lib_modules
|
|
127
|
+
]:
|
|
128
|
+
raise KeyError(f"module_name {module_name} is duplicated")
|
|
129
|
+
module_content = open(file=os.path.join(base_dirpath, filename), mode="r").read().strip()
|
|
130
|
+
module_hash = hashlib.sha256(module_content.encode('utf-8')).hexdigest()
|
|
131
|
+
arpakit_lib_modules.arpakit_lib_modules.append(ArpakitLibModule(
|
|
132
|
+
module_name=module_name,
|
|
133
|
+
module_version=module_version,
|
|
134
|
+
module_content=module_content,
|
|
135
|
+
module_hash=module_hash,
|
|
136
|
+
module_has_error=module_has_error,
|
|
137
|
+
module_exception=module_exception,
|
|
138
|
+
filename=filename,
|
|
139
|
+
filepath=os.path.join(base_dirpath, filename)
|
|
140
|
+
))
|
|
141
|
+
|
|
142
|
+
return arpakit_lib_modules
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def __example():
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == '__main__':
|
|
150
|
+
__example()
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import hashlib
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from asyncio import sleep
|
|
8
|
+
from datetime import timedelta, datetime, time
|
|
9
|
+
from typing import Any
|
|
10
|
+
from urllib.parse import urljoin
|
|
11
|
+
|
|
12
|
+
import aiohttp
|
|
13
|
+
import cachetools
|
|
14
|
+
from aiohttp import ClientResponse, ClientTimeout, ClientResponseError
|
|
15
|
+
from pydantic import ConfigDict, BaseModel
|
|
16
|
+
|
|
17
|
+
from arpakitlib.ar_dict_util import combine_dicts
|
|
18
|
+
from arpakitlib.ar_enumeration import EasyEnumeration
|
|
19
|
+
from arpakitlib.ar_json_util import safely_transfer_to_json_str
|
|
20
|
+
from arpakitlib.ar_type_util import raise_for_type
|
|
21
|
+
|
|
22
|
+
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Weekdays(EasyEnumeration):
|
|
26
|
+
monday = 1
|
|
27
|
+
tuesday = 2
|
|
28
|
+
wednesday = 3
|
|
29
|
+
thursday = 4
|
|
30
|
+
friday = 5
|
|
31
|
+
saturday = 6
|
|
32
|
+
sunday = 7
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Months(EasyEnumeration):
|
|
36
|
+
january = 1
|
|
37
|
+
february = 2
|
|
38
|
+
march = 3
|
|
39
|
+
april = 4
|
|
40
|
+
may = 5
|
|
41
|
+
june = 6
|
|
42
|
+
july = 7
|
|
43
|
+
august = 8
|
|
44
|
+
september = 9
|
|
45
|
+
october = 10
|
|
46
|
+
november = 11
|
|
47
|
+
december = 12
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BaseAPIModel(BaseModel):
|
|
51
|
+
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
|
|
52
|
+
|
|
53
|
+
def simple_json(self) -> str:
|
|
54
|
+
return safely_transfer_to_json_str(self.model_dump(mode="json"))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class GroupAPIModel(BaseAPIModel):
|
|
58
|
+
id: int
|
|
59
|
+
creation_dt: datetime
|
|
60
|
+
sync_from_uust_api_dt: datetime
|
|
61
|
+
uust_api_id: int
|
|
62
|
+
title: str
|
|
63
|
+
faculty: str | None
|
|
64
|
+
course: int | None
|
|
65
|
+
difference_level: int | None = None
|
|
66
|
+
uust_api_data: dict[str, Any]
|
|
67
|
+
|
|
68
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> GroupAPIModel:
|
|
72
|
+
return GroupAPIModel.model_validate(combine_dicts(
|
|
73
|
+
arpakit_uust_api_data,
|
|
74
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data}
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TeacherAPIModel(BaseAPIModel):
|
|
79
|
+
id: int
|
|
80
|
+
creation_dt: datetime
|
|
81
|
+
sync_from_uust_api_dt: datetime
|
|
82
|
+
uust_api_id: int
|
|
83
|
+
name: str | None
|
|
84
|
+
surname: str | None
|
|
85
|
+
patronymic: str | None
|
|
86
|
+
fullname: str | None
|
|
87
|
+
shortname: str | None
|
|
88
|
+
posts: list[str]
|
|
89
|
+
post: str | None
|
|
90
|
+
units: list[str]
|
|
91
|
+
unit: str | None
|
|
92
|
+
difference_level: int | None
|
|
93
|
+
uust_api_data: dict[str, Any]
|
|
94
|
+
|
|
95
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> TeacherAPIModel:
|
|
99
|
+
return TeacherAPIModel.model_validate(combine_dicts(
|
|
100
|
+
arpakit_uust_api_data,
|
|
101
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data}
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class GroupLessonAPIModel(BaseAPIModel):
|
|
106
|
+
id: int
|
|
107
|
+
creation_dt: datetime
|
|
108
|
+
sync_from_uust_api_dt: datetime
|
|
109
|
+
uust_api_id: int
|
|
110
|
+
type: str
|
|
111
|
+
title: str
|
|
112
|
+
weeks: list[int]
|
|
113
|
+
weekday: int
|
|
114
|
+
comment: str | None
|
|
115
|
+
time_title: str | None
|
|
116
|
+
time_start: time | None
|
|
117
|
+
time_end: time | None
|
|
118
|
+
numbers: list[int]
|
|
119
|
+
location: str | None
|
|
120
|
+
teacher_uust_api_id: int | None
|
|
121
|
+
group_uust_api_id: int | None
|
|
122
|
+
group: GroupAPIModel
|
|
123
|
+
teacher: TeacherAPIModel | None
|
|
124
|
+
uust_api_data: dict[str, Any]
|
|
125
|
+
|
|
126
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> GroupLessonAPIModel:
|
|
130
|
+
return GroupLessonAPIModel.model_validate(combine_dicts(
|
|
131
|
+
arpakit_uust_api_data,
|
|
132
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data},
|
|
133
|
+
{
|
|
134
|
+
"group": GroupAPIModel.from_arpakit_uust_api_data(
|
|
135
|
+
arpakit_uust_api_data=arpakit_uust_api_data["group"]
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"teacher": (
|
|
140
|
+
TeacherAPIModel.from_arpakit_uust_api_data(
|
|
141
|
+
arpakit_uust_api_data=arpakit_uust_api_data["teacher"]
|
|
142
|
+
)
|
|
143
|
+
if arpakit_uust_api_data["teacher"] is not None
|
|
144
|
+
else None
|
|
145
|
+
)
|
|
146
|
+
},
|
|
147
|
+
))
|
|
148
|
+
|
|
149
|
+
def compare_type(self, *types: str | list[str]) -> bool:
|
|
150
|
+
type_ = self.type.strip().lower()
|
|
151
|
+
for type__ in types:
|
|
152
|
+
if isinstance(type__, str):
|
|
153
|
+
if type_ == type__.strip().lower():
|
|
154
|
+
return True
|
|
155
|
+
elif isinstance(type__, list):
|
|
156
|
+
for type___ in type__:
|
|
157
|
+
if type_ == type___.strip().lower():
|
|
158
|
+
return True
|
|
159
|
+
else:
|
|
160
|
+
raise TypeError()
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class TeacherLessonAPIModel(BaseAPIModel):
|
|
165
|
+
id: int
|
|
166
|
+
creation_dt: datetime
|
|
167
|
+
sync_from_uust_api_dt: datetime
|
|
168
|
+
uust_api_id: int
|
|
169
|
+
type: str
|
|
170
|
+
title: str
|
|
171
|
+
weeks: list[int]
|
|
172
|
+
weekday: int
|
|
173
|
+
comment: str | None
|
|
174
|
+
time_title: str | None
|
|
175
|
+
time_start: time | None
|
|
176
|
+
time_end: time | None
|
|
177
|
+
numbers: list[int]
|
|
178
|
+
location: str | None
|
|
179
|
+
group_uust_api_ids: list[int]
|
|
180
|
+
teacher_uust_api_id: int
|
|
181
|
+
teacher: TeacherAPIModel
|
|
182
|
+
groups: list[GroupAPIModel]
|
|
183
|
+
uust_api_data: dict[str, Any]
|
|
184
|
+
|
|
185
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> TeacherLessonAPIModel:
|
|
189
|
+
return TeacherLessonAPIModel.model_validate(combine_dicts(
|
|
190
|
+
arpakit_uust_api_data,
|
|
191
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data},
|
|
192
|
+
{
|
|
193
|
+
"teacher": TeacherAPIModel.from_arpakit_uust_api_data(
|
|
194
|
+
arpakit_uust_api_data=arpakit_uust_api_data["teacher"]
|
|
195
|
+
)
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"groups": [
|
|
199
|
+
GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d)
|
|
200
|
+
for d in arpakit_uust_api_data["groups"]
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
))
|
|
204
|
+
|
|
205
|
+
def compare_type(self, *types: str | list[str]) -> bool:
|
|
206
|
+
type_ = self.type.strip().lower()
|
|
207
|
+
for type__ in types:
|
|
208
|
+
if isinstance(type__, str):
|
|
209
|
+
if type_ == type__.strip().lower():
|
|
210
|
+
return True
|
|
211
|
+
elif isinstance(type__, list):
|
|
212
|
+
for type___ in type__:
|
|
213
|
+
if type_ == type___.strip().lower():
|
|
214
|
+
return True
|
|
215
|
+
else:
|
|
216
|
+
raise TypeError()
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class CurrentSemesterAPIModel(BaseAPIModel):
|
|
221
|
+
id: int
|
|
222
|
+
creation_dt: datetime
|
|
223
|
+
sync_from_uust_api_dt: datetime
|
|
224
|
+
value: str
|
|
225
|
+
raw_value: str
|
|
226
|
+
|
|
227
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
228
|
+
|
|
229
|
+
@classmethod
|
|
230
|
+
def from_arpakit_uust_api_data(cls, *, arpakit_uust_api_data: dict[str, Any]) -> CurrentSemesterAPIModel:
|
|
231
|
+
return CurrentSemesterAPIModel.model_validate(combine_dicts(
|
|
232
|
+
arpakit_uust_api_data,
|
|
233
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data}
|
|
234
|
+
))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class CurrentWeekAPIModel(BaseAPIModel):
|
|
238
|
+
id: int
|
|
239
|
+
creation_dt: datetime
|
|
240
|
+
sync_from_uust_api_dt: datetime
|
|
241
|
+
value: str
|
|
242
|
+
|
|
243
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
244
|
+
|
|
245
|
+
@classmethod
|
|
246
|
+
def from_arpakit_uust_api_data(cls, *, arpakit_uust_api_data: dict[str, Any]) -> CurrentWeekAPIModel:
|
|
247
|
+
return CurrentWeekAPIModel.model_validate(combine_dicts(
|
|
248
|
+
arpakit_uust_api_data,
|
|
249
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data}
|
|
250
|
+
))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class WeatherInUfaAPIModel(BaseAPIModel):
|
|
254
|
+
temperature: float
|
|
255
|
+
temperature_feels_like: float
|
|
256
|
+
description: str
|
|
257
|
+
wind_speed: float
|
|
258
|
+
sunrise_dt: datetime
|
|
259
|
+
sunset_dt: datetime
|
|
260
|
+
has_rain: bool
|
|
261
|
+
has_snow: bool
|
|
262
|
+
data: dict
|
|
263
|
+
|
|
264
|
+
arpakit_uust_api_data: dict[str, Any]
|
|
265
|
+
|
|
266
|
+
@classmethod
|
|
267
|
+
def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[float, Any]) -> WeatherInUfaAPIModel:
|
|
268
|
+
return WeatherInUfaAPIModel.model_validate(combine_dicts(
|
|
269
|
+
arpakit_uust_api_data,
|
|
270
|
+
{"arpakit_uust_api_data": arpakit_uust_api_data}
|
|
271
|
+
))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
class ARPAKITScheduleUUSTAPIClient:
|
|
275
|
+
def __init__(
|
|
276
|
+
self,
|
|
277
|
+
*,
|
|
278
|
+
base_url: str = "https://api.schedule-uust.arpakit.com/api/v1",
|
|
279
|
+
api_key: str | None = "viewer",
|
|
280
|
+
use_cache: bool = False,
|
|
281
|
+
cache_ttl: int | float | None = timedelta(minutes=10).total_seconds()
|
|
282
|
+
):
|
|
283
|
+
self._logger = logging.getLogger(__name__)
|
|
284
|
+
self.api_key = api_key
|
|
285
|
+
base_url = base_url.strip()
|
|
286
|
+
if not base_url.endswith("/"):
|
|
287
|
+
base_url += "/"
|
|
288
|
+
self.base_url = base_url
|
|
289
|
+
self.headers = {"Content-Type": "application/json"}
|
|
290
|
+
if api_key is not None:
|
|
291
|
+
self.headers.update({"apikey": api_key})
|
|
292
|
+
self.use_cache = use_cache
|
|
293
|
+
self.cache_ttl = cache_ttl
|
|
294
|
+
if cache_ttl is not None:
|
|
295
|
+
self.ttl_cache = cachetools.TTLCache(maxsize=100, ttl=cache_ttl)
|
|
296
|
+
else:
|
|
297
|
+
self.ttl_cache = None
|
|
298
|
+
|
|
299
|
+
def clear_a_s_u_api_client(self):
|
|
300
|
+
if self.ttl_cache is not None:
|
|
301
|
+
self.ttl_cache.clear()
|
|
302
|
+
|
|
303
|
+
async def _async_make_request(self, *, method: str = "GET", url: str, **kwargs) -> ClientResponse:
|
|
304
|
+
max_tries = 7
|
|
305
|
+
tries = 0
|
|
306
|
+
|
|
307
|
+
kwargs["url"] = url
|
|
308
|
+
kwargs["method"] = method
|
|
309
|
+
kwargs["timeout"] = ClientTimeout(total=timedelta(seconds=15).total_seconds())
|
|
310
|
+
kwargs["headers"] = self.headers
|
|
311
|
+
|
|
312
|
+
cache_key = (
|
|
313
|
+
"_async_make_request",
|
|
314
|
+
hashlib.sha256(json.dumps(kwargs, ensure_ascii=False, default=str).encode()).hexdigest()
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
if self.use_cache and self.ttl_cache is not None:
|
|
318
|
+
if cache_key in self.ttl_cache:
|
|
319
|
+
return self.ttl_cache[cache_key]
|
|
320
|
+
|
|
321
|
+
while True:
|
|
322
|
+
tries += 1
|
|
323
|
+
self._logger.info(f"{method} {url}")
|
|
324
|
+
try:
|
|
325
|
+
async with aiohttp.ClientSession() as session:
|
|
326
|
+
async with session.request(**kwargs) as response:
|
|
327
|
+
await response.read()
|
|
328
|
+
if self.use_cache and self.ttl_cache is not None:
|
|
329
|
+
self.ttl_cache[cache_key] = response
|
|
330
|
+
return response
|
|
331
|
+
except Exception as err:
|
|
332
|
+
self._logger.warning(f"{tries}/{max_tries} {err} {method} {url}")
|
|
333
|
+
if tries >= max_tries:
|
|
334
|
+
raise err
|
|
335
|
+
await sleep(timedelta(seconds=0.1).total_seconds())
|
|
336
|
+
continue
|
|
337
|
+
|
|
338
|
+
async def healthcheck(self) -> bool:
|
|
339
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "healthcheck"))
|
|
340
|
+
response.raise_for_status()
|
|
341
|
+
json_data = await response.json()
|
|
342
|
+
return json_data["data"]["healthcheck"]
|
|
343
|
+
|
|
344
|
+
async def is_healthcheck_good(self) -> bool:
|
|
345
|
+
try:
|
|
346
|
+
return await self.healthcheck()
|
|
347
|
+
except ClientResponseError:
|
|
348
|
+
return False
|
|
349
|
+
|
|
350
|
+
async def auth_healthcheck(self) -> bool:
|
|
351
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "auth_healthcheck"))
|
|
352
|
+
response.raise_for_status()
|
|
353
|
+
json_data = await response.json()
|
|
354
|
+
return json_data["data"]["auth_healthcheck"]
|
|
355
|
+
|
|
356
|
+
async def is_auth_healthcheck_good(self) -> bool:
|
|
357
|
+
try:
|
|
358
|
+
return await self.auth_healthcheck()
|
|
359
|
+
except ClientResponseError:
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
async def get_required_current_week_value(self) -> int:
|
|
363
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_week"))
|
|
364
|
+
response.raise_for_status()
|
|
365
|
+
json_data = await response.json()
|
|
366
|
+
raise_for_type(json_data["value"], int)
|
|
367
|
+
return json_data["value"]
|
|
368
|
+
|
|
369
|
+
async def get_current_semester(self) -> CurrentSemesterAPIModel | None:
|
|
370
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_semester"))
|
|
371
|
+
json_data = await response.json()
|
|
372
|
+
if json_data is None:
|
|
373
|
+
return None
|
|
374
|
+
if "error_code" in json_data and json_data["error_code"] == "CURRENT_SEMESTER_NOT_FOUND":
|
|
375
|
+
return None
|
|
376
|
+
response.raise_for_status()
|
|
377
|
+
return CurrentSemesterAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
|
|
378
|
+
|
|
379
|
+
async def get_current_week(self) -> CurrentWeekAPIModel | None:
|
|
380
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_week"))
|
|
381
|
+
json_data = await response.json()
|
|
382
|
+
if json_data is None:
|
|
383
|
+
return None
|
|
384
|
+
if "error_code" in json_data and json_data["error_code"] == "CURRENT_WEEK_NOT_FOUND":
|
|
385
|
+
return None
|
|
386
|
+
response.raise_for_status()
|
|
387
|
+
return CurrentWeekAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
|
|
388
|
+
|
|
389
|
+
async def get_log_file_content(self) -> str | None:
|
|
390
|
+
|
|
391
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "extra/get_log_file"))
|
|
392
|
+
response.raise_for_status()
|
|
393
|
+
text_data = await response.text()
|
|
394
|
+
return text_data
|
|
395
|
+
|
|
396
|
+
async def get_groups(self) -> list[GroupAPIModel]:
|
|
397
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "group/get_groups"))
|
|
398
|
+
response.raise_for_status()
|
|
399
|
+
json_data = await response.json()
|
|
400
|
+
return [GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
401
|
+
|
|
402
|
+
async def get_group(
|
|
403
|
+
self, *, filter_id: int | None = None, filter_uust_api_id: int | None = None
|
|
404
|
+
) -> GroupAPIModel | None:
|
|
405
|
+
params = {}
|
|
406
|
+
if filter_id is not None:
|
|
407
|
+
params["filter_id"] = filter_id
|
|
408
|
+
if filter_uust_api_id is not None:
|
|
409
|
+
params["filter_uust_api_id"] = filter_uust_api_id
|
|
410
|
+
response = await self._async_make_request(
|
|
411
|
+
method="GET",
|
|
412
|
+
url=urljoin(self.base_url, "group/get_group"),
|
|
413
|
+
params=params
|
|
414
|
+
)
|
|
415
|
+
json_data = await response.json()
|
|
416
|
+
if "error_code" in json_data and json_data["error_code"] == "GROUP_NOT_FOUND":
|
|
417
|
+
return None
|
|
418
|
+
response.raise_for_status()
|
|
419
|
+
return GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
|
|
420
|
+
|
|
421
|
+
async def find_groups(
|
|
422
|
+
self, *, q: str
|
|
423
|
+
) -> list[GroupAPIModel]:
|
|
424
|
+
response = await self._async_make_request(
|
|
425
|
+
method="GET",
|
|
426
|
+
url=urljoin(self.base_url, "group/find_groups"),
|
|
427
|
+
params={"q": q.strip()}
|
|
428
|
+
)
|
|
429
|
+
response.raise_for_status()
|
|
430
|
+
json_data = await response.json()
|
|
431
|
+
return [GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
432
|
+
|
|
433
|
+
async def get_teachers(self) -> list[TeacherAPIModel]:
|
|
434
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "teacher/get_teachers"))
|
|
435
|
+
response.raise_for_status()
|
|
436
|
+
json_data = await response.json()
|
|
437
|
+
return [TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
438
|
+
|
|
439
|
+
async def get_teacher(
|
|
440
|
+
self, *, filter_id: int | None = None, filter_uust_api_id: int | None = None
|
|
441
|
+
) -> TeacherAPIModel | None:
|
|
442
|
+
params = {}
|
|
443
|
+
if filter_id is not None:
|
|
444
|
+
params["filter_id"] = filter_id
|
|
445
|
+
if filter_uust_api_id is not None:
|
|
446
|
+
params["filter_uust_api_id"] = filter_uust_api_id
|
|
447
|
+
response = await self._async_make_request(
|
|
448
|
+
method="GET",
|
|
449
|
+
url=urljoin(self.base_url, "teacher/get_teacher"),
|
|
450
|
+
params=params
|
|
451
|
+
)
|
|
452
|
+
json_data = await response.json()
|
|
453
|
+
if "error_code" in json_data and json_data["error_code"] == "TEACHER_NOT_FOUND":
|
|
454
|
+
return None
|
|
455
|
+
response.raise_for_status()
|
|
456
|
+
return TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
|
|
457
|
+
|
|
458
|
+
async def find_teachers(
|
|
459
|
+
self, *, q: str
|
|
460
|
+
) -> list[TeacherAPIModel]:
|
|
461
|
+
response = await self._async_make_request(
|
|
462
|
+
method="GET",
|
|
463
|
+
url=urljoin(self.base_url, "teacher/find_teachers"),
|
|
464
|
+
params={"q": q.strip()}
|
|
465
|
+
)
|
|
466
|
+
response.raise_for_status()
|
|
467
|
+
json_data = await response.json()
|
|
468
|
+
return [TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
469
|
+
|
|
470
|
+
async def get_group_lessons(
|
|
471
|
+
self,
|
|
472
|
+
*,
|
|
473
|
+
filter_group_id: int | None = None,
|
|
474
|
+
filter_group_uust_api_id: int | None = None
|
|
475
|
+
) -> list[GroupLessonAPIModel]:
|
|
476
|
+
params = {}
|
|
477
|
+
if filter_group_id is not None:
|
|
478
|
+
params["filter_group_id"] = filter_group_id
|
|
479
|
+
if filter_group_uust_api_id is not None:
|
|
480
|
+
params["filter_group_uust_api_id"] = filter_group_uust_api_id
|
|
481
|
+
response = await self._async_make_request(
|
|
482
|
+
method="GET",
|
|
483
|
+
url=urljoin(self.base_url, "group_lesson/get_group_lessons"),
|
|
484
|
+
params=params
|
|
485
|
+
)
|
|
486
|
+
response.raise_for_status()
|
|
487
|
+
json_data = await response.json()
|
|
488
|
+
return [GroupLessonAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
489
|
+
|
|
490
|
+
async def get_teacher_lessons(
|
|
491
|
+
self,
|
|
492
|
+
*,
|
|
493
|
+
filter_teacher_id: int | None = None,
|
|
494
|
+
filter_teacher_uust_api_id: int | None = None
|
|
495
|
+
) -> list[TeacherLessonAPIModel]:
|
|
496
|
+
params = {}
|
|
497
|
+
if filter_teacher_id is not None:
|
|
498
|
+
params["filter_teacher_id"] = filter_teacher_id
|
|
499
|
+
if filter_teacher_uust_api_id is not None:
|
|
500
|
+
params["filter_teacher_uust_api_id"] = filter_teacher_uust_api_id
|
|
501
|
+
response = await self._async_make_request(
|
|
502
|
+
method="GET",
|
|
503
|
+
url=urljoin(self.base_url, "teacher_lesson/get_teacher_lessons"),
|
|
504
|
+
params=params
|
|
505
|
+
)
|
|
506
|
+
response.raise_for_status()
|
|
507
|
+
json_data = await response.json()
|
|
508
|
+
return [TeacherLessonAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
|
|
509
|
+
|
|
510
|
+
async def get_weather_in_ufa(self) -> WeatherInUfaAPIModel:
|
|
511
|
+
response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_weather_in_ufa"))
|
|
512
|
+
response.raise_for_status()
|
|
513
|
+
json_data = await response.json()
|
|
514
|
+
return WeatherInUfaAPIModel.from_arpakit_uust_api_data(json_data)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def __example():
|
|
518
|
+
pass
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
async def __async_example():
|
|
522
|
+
pass
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
if __name__ == '__main__':
|
|
526
|
+
__example()
|
|
527
|
+
asyncio.run(__async_example())
|