arpakitlib 1.7.212__py3-none-any.whl → 1.7.216__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.
@@ -3,23 +3,18 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
- import hashlib
7
- import json
8
6
  import logging
9
7
  from datetime import timedelta, datetime, time
10
8
  from typing import Any
11
9
  from urllib.parse import urljoin
12
10
 
13
- import aiohttp
14
11
  import cachetools
15
- from aiohttp import ClientResponse, ClientTimeout, ClientResponseError
12
+ from aiohttp import ClientResponse
16
13
  from pydantic import ConfigDict, BaseModel
17
14
 
18
- from arpakitlib.ar_dict_util import combine_dicts
19
15
  from arpakitlib.ar_enumeration_util import Enumeration
16
+ from arpakitlib.ar_http_request_util import async_make_http_request
20
17
  from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str
21
- from arpakitlib.ar_sleep_util import async_safe_sleep
22
- from arpakitlib.ar_type_util import raise_for_type
23
18
 
24
19
  _ARPAKIT_LIB_MODULE_VERSION = "3.0"
25
20
 
@@ -52,14 +47,31 @@ class Months(Enumeration):
52
47
  class BaseAPIModel(BaseModel):
53
48
  model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True, from_attributes=True)
54
49
 
55
- def simple_json(self) -> str:
56
- return safely_transfer_obj_to_json_str(self.model_dump(mode="json"))
50
+
51
+ class CurrentSemesterAPIModel(BaseAPIModel):
52
+ id: int
53
+ long_id: str
54
+ creation_dt: datetime
55
+ entity_type: str
56
+ actualization_dt: datetime
57
+ value: str
58
+
59
+
60
+ class CurrentWeekAPIModel(BaseAPIModel):
61
+ id: int
62
+ long_id: str
63
+ creation_dt: datetime
64
+ entity_type: str
65
+ actualization_dt: datetime
66
+ value: int
57
67
 
58
68
 
59
69
  class GroupAPIModel(BaseAPIModel):
60
70
  id: int
71
+ long_id: str
61
72
  creation_dt: datetime
62
- sync_from_uust_api_dt: datetime
73
+ entity_type: str
74
+ actualization_dt: datetime
63
75
  uust_api_id: int
64
76
  title: str
65
77
  faculty: str | None
@@ -67,20 +79,13 @@ class GroupAPIModel(BaseAPIModel):
67
79
  difference_level: int | None = None
68
80
  uust_api_data: dict[str, Any]
69
81
 
70
- arpakit_uust_api_data: dict[str, Any]
71
-
72
- @classmethod
73
- def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> GroupAPIModel:
74
- return GroupAPIModel.model_validate(combine_dicts(
75
- arpakit_uust_api_data,
76
- {"arpakit_uust_api_data": arpakit_uust_api_data}
77
- ))
78
-
79
82
 
80
83
  class TeacherAPIModel(BaseAPIModel):
81
84
  id: int
85
+ long_id: str
82
86
  creation_dt: datetime
83
- sync_from_uust_api_dt: datetime
87
+ entity_type: str
88
+ actualization_dt: datetime
84
89
  uust_api_id: int
85
90
  name: str | None
86
91
  surname: str | None
@@ -94,20 +99,13 @@ class TeacherAPIModel(BaseAPIModel):
94
99
  difference_level: int | None
95
100
  uust_api_data: dict[str, Any]
96
101
 
97
- arpakit_uust_api_data: dict[str, Any]
98
-
99
- @classmethod
100
- def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> TeacherAPIModel:
101
- return TeacherAPIModel.model_validate(combine_dicts(
102
- arpakit_uust_api_data,
103
- {"arpakit_uust_api_data": arpakit_uust_api_data}
104
- ))
105
-
106
102
 
107
103
  class GroupLessonAPIModel(BaseAPIModel):
108
104
  id: int
105
+ long_id: str
109
106
  creation_dt: datetime
110
- sync_from_uust_api_dt: datetime
107
+ entity_type: str
108
+ actualization_dt: datetime
111
109
  uust_api_id: int
112
110
  type: str
113
111
  title: str
@@ -125,29 +123,6 @@ class GroupLessonAPIModel(BaseAPIModel):
125
123
  teacher: TeacherAPIModel | None
126
124
  uust_api_data: dict[str, Any]
127
125
 
128
- arpakit_uust_api_data: dict[str, Any]
129
-
130
- @classmethod
131
- def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> GroupLessonAPIModel:
132
- return GroupLessonAPIModel.model_validate(combine_dicts(
133
- arpakit_uust_api_data,
134
- {"arpakit_uust_api_data": arpakit_uust_api_data},
135
- {
136
- "group": GroupAPIModel.from_arpakit_uust_api_data(
137
- arpakit_uust_api_data=arpakit_uust_api_data["group"]
138
- )
139
- },
140
- {
141
- "teacher": (
142
- TeacherAPIModel.from_arpakit_uust_api_data(
143
- arpakit_uust_api_data=arpakit_uust_api_data["teacher"]
144
- )
145
- if arpakit_uust_api_data["teacher"] is not None
146
- else None
147
- )
148
- },
149
- ))
150
-
151
126
  def compare_type(self, *types: str | list[str]) -> bool:
152
127
  type_ = self.type.strip().lower()
153
128
  for type__ in types:
@@ -165,8 +140,10 @@ class GroupLessonAPIModel(BaseAPIModel):
165
140
 
166
141
  class TeacherLessonAPIModel(BaseAPIModel):
167
142
  id: int
143
+ long_id: str
168
144
  creation_dt: datetime
169
- sync_from_uust_api_dt: datetime
145
+ entity_type: str
146
+ actualization_dt: datetime
170
147
  uust_api_id: int
171
148
  type: str
172
149
  title: str
@@ -184,26 +161,6 @@ class TeacherLessonAPIModel(BaseAPIModel):
184
161
  groups: list[GroupAPIModel]
185
162
  uust_api_data: dict[str, Any]
186
163
 
187
- arpakit_uust_api_data: dict[str, Any]
188
-
189
- @classmethod
190
- def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[str, Any]) -> TeacherLessonAPIModel:
191
- return TeacherLessonAPIModel.model_validate(combine_dicts(
192
- arpakit_uust_api_data,
193
- {"arpakit_uust_api_data": arpakit_uust_api_data},
194
- {
195
- "teacher": TeacherAPIModel.from_arpakit_uust_api_data(
196
- arpakit_uust_api_data=arpakit_uust_api_data["teacher"]
197
- )
198
- },
199
- {
200
- "groups": [
201
- GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d)
202
- for d in arpakit_uust_api_data["groups"]
203
- ]
204
- },
205
- ))
206
-
207
164
  def compare_type(self, *types: str | list[str]) -> bool:
208
165
  type_ = self.type.strip().lower()
209
166
  for type__ in types:
@@ -219,39 +176,6 @@ class TeacherLessonAPIModel(BaseAPIModel):
219
176
  return False
220
177
 
221
178
 
222
- class CurrentSemesterAPIModel(BaseAPIModel):
223
- id: int
224
- creation_dt: datetime
225
- sync_from_uust_api_dt: datetime
226
- value: str
227
- raw_value: str
228
-
229
- arpakit_uust_api_data: dict[str, Any]
230
-
231
- @classmethod
232
- def from_arpakit_uust_api_data(cls, *, arpakit_uust_api_data: dict[str, Any]) -> CurrentSemesterAPIModel:
233
- return CurrentSemesterAPIModel.model_validate(combine_dicts(
234
- arpakit_uust_api_data,
235
- {"arpakit_uust_api_data": arpakit_uust_api_data}
236
- ))
237
-
238
-
239
- class CurrentWeekAPIModel(BaseAPIModel):
240
- id: int
241
- creation_dt: datetime
242
- sync_from_uust_api_dt: datetime
243
- value: str
244
-
245
- arpakit_uust_api_data: dict[str, Any]
246
-
247
- @classmethod
248
- def from_arpakit_uust_api_data(cls, *, arpakit_uust_api_data: dict[str, Any]) -> CurrentWeekAPIModel:
249
- return CurrentWeekAPIModel.model_validate(combine_dicts(
250
- arpakit_uust_api_data,
251
- {"arpakit_uust_api_data": arpakit_uust_api_data}
252
- ))
253
-
254
-
255
179
  class WeatherInUfaAPIModel(BaseAPIModel):
256
180
  temperature: float
257
181
  temperature_feels_like: float
@@ -263,15 +187,6 @@ class WeatherInUfaAPIModel(BaseAPIModel):
263
187
  has_snow: bool
264
188
  data: dict
265
189
 
266
- arpakit_uust_api_data: dict[str, Any]
267
-
268
- @classmethod
269
- def from_arpakit_uust_api_data(cls, arpakit_uust_api_data: dict[float, Any]) -> WeatherInUfaAPIModel:
270
- return WeatherInUfaAPIModel.model_validate(combine_dicts(
271
- arpakit_uust_api_data,
272
- {"arpakit_uust_api_data": arpakit_uust_api_data}
273
- ))
274
-
275
190
 
276
191
  class ARPAKITScheduleUUSTAPIClient:
277
192
  def __init__(
@@ -280,7 +195,7 @@ class ARPAKITScheduleUUSTAPIClient:
280
195
  base_url: str = "https://api.schedule-uust.arpakit.com/api/v1",
281
196
  api_key: str | None = "viewer",
282
197
  use_cache: bool = False,
283
- cache_ttl: int | float | None = timedelta(minutes=10).total_seconds()
198
+ cache_ttl: timedelta | None = timedelta(minutes=10)
284
199
  ):
285
200
  self._logger = logging.getLogger(__name__)
286
201
  self.api_key = api_key
@@ -290,116 +205,75 @@ class ARPAKITScheduleUUSTAPIClient:
290
205
  self.base_url = base_url
291
206
  self.headers = {"Content-Type": "application/json"}
292
207
  if api_key is not None:
293
- self.headers.update({"apikey": api_key})
208
+ self.headers.update({"apikey": self.api_key})
294
209
  self.use_cache = use_cache
295
210
  self.cache_ttl = cache_ttl
296
211
  if cache_ttl is not None:
297
- self.ttl_cache = cachetools.TTLCache(maxsize=100, ttl=cache_ttl)
212
+ self.ttl_cache = cachetools.TTLCache(maxsize=100, ttl=cache_ttl.total_seconds())
298
213
  else:
299
214
  self.ttl_cache = None
300
215
 
301
- def clear_a_s_u_api_client(self):
216
+ def clear_cache(self):
302
217
  if self.ttl_cache is not None:
303
218
  self.ttl_cache.clear()
304
219
 
305
- async def _async_make_request(self, *, method: str = "GET", url: str, **kwargs) -> ClientResponse:
306
- max_tries = 7
307
- tries = 0
308
-
309
- kwargs["url"] = url
310
- kwargs["method"] = method
311
- kwargs["timeout"] = ClientTimeout(total=timedelta(seconds=15).total_seconds())
312
- kwargs["headers"] = self.headers
313
-
314
- cache_key = (
315
- "_async_make_request",
316
- hashlib.sha256(json.dumps(kwargs, ensure_ascii=False, default=str).encode()).hexdigest()
220
+ async def _async_make_http_request(
221
+ self,
222
+ *,
223
+ method: str = "GET",
224
+ url: str,
225
+ params: dict[str, Any] | None = None,
226
+ **kwargs
227
+ ) -> ClientResponse:
228
+ response = await async_make_http_request(
229
+ method=method,
230
+ url=url,
231
+ headers=self.headers,
232
+ params=params,
233
+ raise_for_status_=True,
234
+ **kwargs
317
235
  )
236
+ return response
318
237
 
319
- if self.use_cache and self.ttl_cache is not None:
320
- if cache_key in self.ttl_cache:
321
- return self.ttl_cache[cache_key]
322
-
323
- while True:
324
- tries += 1
325
- self._logger.info(f"{method} {url}")
326
- try:
327
- async with aiohttp.ClientSession() as session:
328
- async with session.request(**kwargs) as response:
329
- await response.read()
330
- if self.use_cache and self.ttl_cache is not None:
331
- self.ttl_cache[cache_key] = response
332
- return response
333
- except Exception as err:
334
- self._logger.warning(f"{tries}/{max_tries} {err} {method} {url}")
335
- if tries >= max_tries:
336
- raise err
337
- await async_safe_sleep(timedelta(seconds=0.1).total_seconds())
338
- continue
339
-
340
- async def healthcheck(self) -> bool:
341
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "healthcheck"))
342
- response.raise_for_status()
238
+ async def check_auth(self) -> dict[str, Any]:
239
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "check_auth"))
343
240
  json_data = await response.json()
344
- return json_data["data"]["healthcheck"]
241
+ return json_data
345
242
 
346
- async def is_healthcheck_good(self) -> bool:
347
- try:
348
- return await self.healthcheck()
349
- except ClientResponseError:
350
- return False
351
-
352
- async def auth_healthcheck(self) -> bool:
353
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "auth_healthcheck"))
354
- response.raise_for_status()
355
- json_data = await response.json()
356
- return json_data["data"]["auth_healthcheck"]
357
-
358
- async def is_auth_healthcheck_good(self) -> bool:
359
- try:
360
- return await self.auth_healthcheck()
361
- except ClientResponseError:
362
- return False
363
-
364
- async def get_required_current_week_value(self) -> int:
365
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_week"))
366
- response.raise_for_status()
243
+ async def get_current_week(self) -> CurrentWeekAPIModel | None:
244
+ response = await self._async_make_http_request(
245
+ method="GET", url=urljoin(self.base_url, "get_current_week")
246
+ )
367
247
  json_data = await response.json()
368
- raise_for_type(json_data["value"], int)
369
- return json_data["value"]
248
+ if json_data is None:
249
+ return None
250
+ return CurrentWeekAPIModel.model_validate(json_data)
370
251
 
371
252
  async def get_current_semester(self) -> CurrentSemesterAPIModel | None:
372
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_semester"))
253
+ response = await self._async_make_http_request(
254
+ method="GET", url=urljoin(self.base_url, "get_current_semester")
255
+ )
373
256
  json_data = await response.json()
374
257
  if json_data is None:
375
258
  return None
376
- if "error_code" in json_data and json_data["error_code"] == "CURRENT_SEMESTER_NOT_FOUND":
377
- return None
378
- response.raise_for_status()
379
- return CurrentSemesterAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
259
+ return CurrentSemesterAPIModel.model_validate(json_data)
380
260
 
381
- async def get_current_week(self) -> CurrentWeekAPIModel | None:
382
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_current_week"))
261
+ async def get_weather_in_ufa(self) -> WeatherInUfaAPIModel:
262
+ response = await self._async_make_http_request(
263
+ method="GET", url=urljoin(self.base_url, "get_weather_in_ufa")
264
+ )
383
265
  json_data = await response.json()
384
- if json_data is None:
385
- return None
386
- if "error_code" in json_data and json_data["error_code"] == "CURRENT_WEEK_NOT_FOUND":
387
- return None
388
- response.raise_for_status()
389
- return CurrentWeekAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
266
+ return WeatherInUfaAPIModel.model_validate(json_data)
390
267
 
391
268
  async def get_log_file_content(self) -> str | None:
392
-
393
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "extra/get_log_file"))
394
- response.raise_for_status()
269
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "get_log_file"))
395
270
  text_data = await response.text()
396
271
  return text_data
397
272
 
398
273
  async def get_groups(self) -> list[GroupAPIModel]:
399
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "group/get_groups"))
400
- response.raise_for_status()
274
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "get_groups"))
401
275
  json_data = await response.json()
402
- return [GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
276
+ return [GroupAPIModel.model_validate(d) for d in json_data]
403
277
 
404
278
  async def get_group(
405
279
  self, *, filter_id: int | None = None, filter_uust_api_id: int | None = None
@@ -409,34 +283,25 @@ class ARPAKITScheduleUUSTAPIClient:
409
283
  params["filter_id"] = filter_id
410
284
  if filter_uust_api_id is not None:
411
285
  params["filter_uust_api_id"] = filter_uust_api_id
412
- response = await self._async_make_request(
413
- method="GET",
414
- url=urljoin(self.base_url, "group/get_group"),
415
- params=params
416
- )
286
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "get_group"),
287
+ params=params)
417
288
  json_data = await response.json()
418
- if "error_code" in json_data and json_data["error_code"] == "GROUP_NOT_FOUND":
289
+ if json_data is None:
419
290
  return None
420
- response.raise_for_status()
421
- return GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
291
+ return GroupAPIModel.model_validate(json_data)
422
292
 
423
293
  async def find_groups(
424
294
  self, *, q: str
425
295
  ) -> list[GroupAPIModel]:
426
- response = await self._async_make_request(
427
- method="GET",
428
- url=urljoin(self.base_url, "group/find_groups"),
429
- params={"q": q.strip()}
430
- )
431
- response.raise_for_status()
296
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "find_groups"),
297
+ params={"q": q.strip()})
432
298
  json_data = await response.json()
433
- return [GroupAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
299
+ return [GroupAPIModel.model_validate(d) for d in json_data]
434
300
 
435
301
  async def get_teachers(self) -> list[TeacherAPIModel]:
436
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "teacher/get_teachers"))
437
- response.raise_for_status()
302
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "get_teachers"))
438
303
  json_data = await response.json()
439
- return [TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
304
+ return [TeacherAPIModel.model_validate(d) for d in json_data]
440
305
 
441
306
  async def get_teacher(
442
307
  self, *, filter_id: int | None = None, filter_uust_api_id: int | None = None
@@ -446,28 +311,37 @@ class ARPAKITScheduleUUSTAPIClient:
446
311
  params["filter_id"] = filter_id
447
312
  if filter_uust_api_id is not None:
448
313
  params["filter_uust_api_id"] = filter_uust_api_id
449
- response = await self._async_make_request(
450
- method="GET",
451
- url=urljoin(self.base_url, "teacher/get_teacher"),
452
- params=params
453
- )
314
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "get_teacher"),
315
+ params=params)
454
316
  json_data = await response.json()
455
- if "error_code" in json_data and json_data["error_code"] == "TEACHER_NOT_FOUND":
317
+ if json_data is None:
456
318
  return None
457
- response.raise_for_status()
458
- return TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=json_data)
319
+ return TeacherAPIModel.model_validate(json_data)
459
320
 
460
321
  async def find_teachers(
461
322
  self, *, q: str
462
323
  ) -> list[TeacherAPIModel]:
463
- response = await self._async_make_request(
464
- method="GET",
465
- url=urljoin(self.base_url, "teacher/find_teachers"),
466
- params={"q": q.strip()}
467
- )
468
- response.raise_for_status()
324
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "find_teachers"),
325
+ params={"q": q.strip()})
326
+ json_data = await response.json()
327
+ return [TeacherAPIModel.model_validate(d) for d in json_data]
328
+
329
+ async def find_any(
330
+ self, *, q: str
331
+ ) -> list[TeacherAPIModel | GroupLessonAPIModel]:
332
+ response = await self._async_make_http_request(method="GET", url=urljoin(self.base_url, "find_any"),
333
+ params={"q": q.strip()})
469
334
  json_data = await response.json()
470
- return [TeacherAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
335
+
336
+ results = []
337
+ for i in json_data:
338
+ if i.get("entity_type") == "group":
339
+ results.append(GroupAPIModel.model_validate(i))
340
+ elif i.get("entity_type") == "teacher":
341
+ results.append(TeacherAPIModel.model_validate(i))
342
+ else:
343
+ pass
344
+ return results
471
345
 
472
346
  async def get_group_lessons(
473
347
  self,
@@ -480,14 +354,11 @@ class ARPAKITScheduleUUSTAPIClient:
480
354
  params["filter_group_id"] = filter_group_id
481
355
  if filter_group_uust_api_id is not None:
482
356
  params["filter_group_uust_api_id"] = filter_group_uust_api_id
483
- response = await self._async_make_request(
484
- method="GET",
485
- url=urljoin(self.base_url, "group_lesson/get_group_lessons"),
486
- params=params
357
+ response = await self._async_make_http_request(
358
+ method="GET", url=urljoin(self.base_url, "get_group_lessons"), params=params
487
359
  )
488
- response.raise_for_status()
489
360
  json_data = await response.json()
490
- return [GroupLessonAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
361
+ return [GroupLessonAPIModel.model_validate(d) for d in json_data]
491
362
 
492
363
  async def get_teacher_lessons(
493
364
  self,
@@ -500,20 +371,11 @@ class ARPAKITScheduleUUSTAPIClient:
500
371
  params["filter_teacher_id"] = filter_teacher_id
501
372
  if filter_teacher_uust_api_id is not None:
502
373
  params["filter_teacher_uust_api_id"] = filter_teacher_uust_api_id
503
- response = await self._async_make_request(
504
- method="GET",
505
- url=urljoin(self.base_url, "teacher_lesson/get_teacher_lessons"),
506
- params=params
374
+ response = await self._async_make_http_request(
375
+ method="GET", url=urljoin(self.base_url, "get_teacher_lessons"), params=params
507
376
  )
508
- response.raise_for_status()
509
377
  json_data = await response.json()
510
- return [TeacherLessonAPIModel.from_arpakit_uust_api_data(arpakit_uust_api_data=d) for d in json_data]
511
-
512
- async def get_weather_in_ufa(self) -> WeatherInUfaAPIModel:
513
- response = await self._async_make_request(method="GET", url=urljoin(self.base_url, "get_weather_in_ufa"))
514
- response.raise_for_status()
515
- json_data = await response.json()
516
- return WeatherInUfaAPIModel.from_arpakit_uust_api_data(json_data)
378
+ return [TeacherLessonAPIModel.model_validate(d) for d in json_data]
517
379
 
518
380
 
519
381
  def __example():
@@ -521,28 +383,67 @@ def __example():
521
383
 
522
384
 
523
385
  async def __async_example():
524
- client = ARPAKITScheduleUUSTAPIClient(api_key="TEST_API_KEY", use_cache=True)
525
-
526
- healthcheck = await client.healthcheck()
527
- print(f"Healthcheck: {healthcheck}")
528
-
529
- auth_healthcheck = await client.auth_healthcheck()
530
- print(f"Auth Healthcheck: {auth_healthcheck}")
531
-
532
- current_week = await client.get_current_week()
533
- print(f"Текущая неделя: {current_week.simple_json() if current_week else 'Не найдено'}")
534
-
535
- current_semester = await client.get_current_semester()
536
- print(f"Текущий семестр: {current_semester.simple_json() if current_semester else 'Не найдено'}")
537
-
538
- groups = await client.get_groups()
539
- print(f"Группы: {[group.simple_json() for group in groups]}")
540
-
541
- teachers = await client.get_teachers()
542
- print(f"Преподаватели: {[teacher.simple_json() for teacher in teachers]}")
543
-
544
- weather = await client.get_weather_in_ufa()
545
- print(f"Погода в Уфе: {weather.simple_json()}")
386
+ client = ARPAKITScheduleUUSTAPIClient(api_key="viewer", use_cache=True)
387
+
388
+ print(f"check_auth")
389
+ print(safely_transfer_obj_to_json_str(await client.check_auth()))
390
+
391
+ print(f"get_weather_in_ufa")
392
+ print(safely_transfer_obj_to_json_str((await client.get_weather_in_ufa()).model_dump()))
393
+
394
+ print(f"get_current_week")
395
+ print(safely_transfer_obj_to_json_str((await client.get_current_week()).model_dump()))
396
+
397
+ print(f"get_current_semester")
398
+ print(safely_transfer_obj_to_json_str((await client.get_current_semester()).model_dump()))
399
+
400
+ # Group
401
+ print(f"get_groups")
402
+ print(safely_transfer_obj_to_json_str((await client.get_groups())))
403
+
404
+ print(f"get_group")
405
+ if await client.get_group(filter_id=25285, filter_uust_api_id=6674):
406
+ print(safely_transfer_obj_to_json_str(
407
+ (await client.get_group(filter_id=25285, filter_uust_api_id=6674)).model_dump()))
408
+ else:
409
+ print("Group is none")
410
+
411
+ print(f"find_groups")
412
+ print(safely_transfer_obj_to_json_str((await client.find_groups(q="ПИ-427Б"))))
413
+
414
+ # Teacher
415
+ print(f"get_teachers")
416
+ print(safely_transfer_obj_to_json_str((await client.get_teachers())))
417
+
418
+ print(f"get_teacher")
419
+ if await client.get_teacher(filter_id=16975, filter_uust_api_id=112978):
420
+ print(safely_transfer_obj_to_json_str(
421
+ (await client.get_teacher(filter_id=16975, filter_uust_api_id=112978)).model_dump()))
422
+ else:
423
+ print("Teacher is none")
424
+
425
+ print(f"find_teachers")
426
+ print(safely_transfer_obj_to_json_str((await client.find_teachers(q="Казанцев"))))
427
+
428
+ # Group Lesson
429
+ print(f"get_group_lessons")
430
+ if await client.get_group_lessons(filter_group_id=25285, filter_group_uust_api_id=6674):
431
+ print(safely_transfer_obj_to_json_str((await client.get_group_lessons(filter_group_id=25285,
432
+ filter_group_uust_api_id=6674))))
433
+ else:
434
+ print("Group lessons is none")
435
+
436
+ # Teacher Lesson
437
+ print(f"get_teacher_lessons")
438
+ if await client.get_teacher_lessons(filter_teacher_id=16975, filter_teacher_uust_api_id=112978):
439
+ print(safely_transfer_obj_to_json_str((await client.get_teacher_lessons(filter_teacher_id=16975,
440
+ filter_teacher_uust_api_id=112978))))
441
+ else:
442
+ print("Teacher lessons is none")
443
+
444
+ # Find Any
445
+ print(f"find_any")
446
+ print(safely_transfer_obj_to_json_str((await client.find_any(q="ПИ"))))
546
447
 
547
448
 
548
449
  if __name__ == '__main__':
@@ -282,7 +282,8 @@ def create_handle_exception(
282
282
  def create_story_log_before_response_in_handle_exception(
283
283
  *,
284
284
  sqlalchemy_db: SQLAlchemyDB,
285
- ignore_api_error_code_not_found: bool = True
285
+ ignore_api_error_codes: list[str] | None = None,
286
+ ignore_status_codes: list[int] | None = None
286
287
  ) -> Callable:
287
288
  def func(
288
289
  *,
@@ -292,8 +293,12 @@ def create_story_log_before_response_in_handle_exception(
292
293
  exception: Exception,
293
294
  **kwargs
294
295
  ) -> (int, ErrorSO, dict[str, Any]):
295
- if ignore_api_error_code_not_found and error_so.error_code == BaseAPIErrorCodes.not_found:
296
+ if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
296
297
  return status_code, error_so, kwargs
298
+
299
+ if ignore_status_codes and status_code in ignore_status_codes:
300
+ return status_code, error_so, kwargs
301
+
297
302
  sqlalchemy_db.init()
298
303
  traceback_str = "".join(traceback.format_exception(type(exception), exception, exception.__traceback__))
299
304
  with sqlalchemy_db.new_session() as session:
@@ -391,7 +396,10 @@ def add_needed_api_router_to_app(*, app: FastAPI):
391
396
  async def _():
392
397
  return APIJSONResponse(
393
398
  status_code=starlette.status.HTTP_200_OK,
394
- content=RawDataSO(data={"healthcheck": "healthcheck"})
399
+ content=RawDataSO(data={
400
+ "healthcheck": "healthcheck",
401
+ "is_ok": True
402
+ })
395
403
  )
396
404
 
397
405
  @api_router.get(
@@ -449,8 +457,8 @@ class BaseAPIAuthData(BaseModel):
449
457
  token_string: str | None = None
450
458
  api_key_string: str | None = None
451
459
 
452
- is_token_string_correct: bool | None = None
453
- is_api_key_string_correct: bool | None = None
460
+ is_token_correct: bool | None = None
461
+ is_api_key_correct: bool | None = None
454
462
 
455
463
 
456
464
  def base_api_auth(
@@ -580,7 +588,7 @@ def base_api_auth(
580
588
  )
581
589
  if is_async_object(validate_api_key_func_data):
582
590
  validate_api_key_func_data = await validate_api_key_func_data
583
- api_auth_data.is_api_key_string_correct = validate_api_key_func_data
591
+ api_auth_data.is_api_key_correct = validate_api_key_func_data
584
592
 
585
593
  # token
586
594
 
@@ -595,25 +603,27 @@ def base_api_auth(
595
603
  )
596
604
  if is_async_object(validate_token_func_data):
597
605
  validate_token_func_data_data = await validate_token_func_data
598
- api_auth_data.is_token_string_correct = validate_token_func_data_data
606
+ api_auth_data.is_token_correct = validate_token_func_data_data
599
607
 
600
608
  # api_key
601
609
 
602
610
  if require_correct_api_key:
603
- if not api_auth_data.is_api_key_string_correct:
611
+ if not api_auth_data.is_api_key_correct:
604
612
  raise APIException(
605
613
  status_code=starlette.status.HTTP_401_UNAUTHORIZED,
606
614
  error_code=BaseAPIErrorCodes.cannot_authorize,
607
- error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
615
+ error_description="not api_auth_data.is_api_key_correct",
616
+ error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump()),
608
617
  )
609
618
 
610
619
  # token
611
620
 
612
621
  if require_correct_token:
613
- if not api_auth_data.is_token_string_correct:
622
+ if not api_auth_data.is_token_correct:
614
623
  raise APIException(
615
624
  status_code=starlette.status.HTTP_401_UNAUTHORIZED,
616
625
  error_code=BaseAPIErrorCodes.cannot_authorize,
626
+ error_description="not api_auth_data.is_token_correct",
617
627
  error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
618
628
  )
619
629
 
@@ -26,6 +26,7 @@ def sync_make_http_request(
26
26
  max_tries_: int = 9,
27
27
  proxy_url_: str | None = None,
28
28
  raise_for_status_: bool = False,
29
+ not_raise_for_statuses: list[int] | None = None,
29
30
  timeout_: timedelta | float = timedelta(seconds=15).total_seconds(),
30
31
  enable_logging_: bool = False,
31
32
  **kwargs
@@ -53,21 +54,25 @@ def sync_make_http_request(
53
54
  kwargs["allow_redirects"] = True
54
55
 
55
56
  if enable_logging_:
56
- _logger.info(f"TRY HTTP {method} {url} {params}")
57
+ _logger.info(f"try http {method} {url} {params}")
57
58
 
58
59
  while True:
59
60
  tries_counter += 1
60
61
  try:
61
62
  response = requests.request(**kwargs)
62
63
  if raise_for_status_:
63
- response.raise_for_status()
64
+ if not_raise_for_statuses and response.status_code in not_raise_for_statuses:
65
+ if enable_logging_:
66
+ _logger.info(f"ignored status {response.status_code} for http {method} {url} {params}")
67
+ else:
68
+ response.raise_for_status()
64
69
  if enable_logging_:
65
- _logger.info(f"GOOD TRY HTTP {method} {url} {params}")
70
+ _logger.info(f"good try http {method} {url} {params}")
66
71
  return response
67
72
  except BaseException as exception:
68
73
  if enable_logging_:
69
74
  _logger.warning(
70
- f"{tries_counter}/{max_tries_}. RETRY {method} {url} {params}, exception={exception}"
75
+ f"{tries_counter}/{max_tries_}. retry {method} {url} {params}, exception={exception}"
71
76
  )
72
77
  if tries_counter >= max_tries_:
73
78
  raise exception
@@ -84,6 +89,7 @@ async def async_make_http_request(
84
89
  max_tries_: int = 9,
85
90
  proxy_url_: str | None = None,
86
91
  raise_for_status_: bool = False,
92
+ not_raise_for_statuses: list[int] | None = None,
87
93
  timeout_: timedelta | None = timedelta(seconds=15),
88
94
  enable_logging_: bool = False,
89
95
  **kwargs
@@ -106,7 +112,7 @@ async def async_make_http_request(
106
112
  proxy_connector = ProxyConnector.from_url(proxy_url_)
107
113
 
108
114
  if enable_logging_:
109
- _logger.info(f"TRY HTTP {method} {url} {params}")
115
+ _logger.info(f"try http {method} {url} {params}")
110
116
 
111
117
  while True:
112
118
  tries_counter += 1
@@ -114,15 +120,21 @@ async def async_make_http_request(
114
120
  async with aiohttp.ClientSession(connector=proxy_connector) as session:
115
121
  async with session.request(**kwargs) as response:
116
122
  if raise_for_status_:
117
- response.raise_for_status()
123
+ if not_raise_for_statuses and response.status in not_raise_for_statuses:
124
+ if enable_logging_:
125
+ _logger.info(
126
+ f"ignored status {response.status} for http {method} {url} {params}"
127
+ )
128
+ else:
129
+ response.raise_for_status()
118
130
  await response.read()
119
131
  if enable_logging_:
120
- _logger.info(f"GOOD TRY HTTP {method} {url} {params}")
132
+ _logger.info(f"good try http {method} {url} {params}")
121
133
  return response
122
134
  except BaseException as exception:
123
135
  if enable_logging_:
124
136
  _logger.warning(
125
- f"{tries_counter}/{max_tries_}. RETRY HTTP {method} {url} {params}, exception={exception}"
137
+ f"{tries_counter}/{max_tries_}. retry http {method} {url} {params}, exception={exception}"
126
138
  )
127
139
  if tries_counter >= max_tries_:
128
140
  raise exception
@@ -77,7 +77,8 @@ class ScheduleUUSTAPIClient:
77
77
  *,
78
78
  method: str = "GET",
79
79
  url: str,
80
- params: dict[str, Any] | None = None
80
+ params: dict[str, Any] | None = None,
81
+ **kwargs
81
82
  ) -> ClientResponse:
82
83
  response = await async_make_http_request(
83
84
  method=method,
@@ -85,7 +86,8 @@ class ScheduleUUSTAPIClient:
85
86
  headers=self.headers,
86
87
  params=combine_dicts(params, self.auth_params()),
87
88
  proxy_url_=self.api_proxy_url,
88
- raise_for_status_=True
89
+ raise_for_status_=True,
90
+ **kwargs
89
91
  )
90
92
  json_data = await response.json()
91
93
  if "error" in json_data.keys():
@@ -4,8 +4,7 @@ from datetime import datetime, timedelta
4
4
  from typing import Any
5
5
  from uuid import uuid4
6
6
 
7
- from sqlalchemy import inspect, INTEGER, TEXT, TIMESTAMP, func
8
- from sqlalchemy.dialects.postgresql import JSONB
7
+ from sqlalchemy import inspect, INTEGER, TEXT, TIMESTAMP, func, JSON
9
8
  from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
10
9
 
11
10
  from arpakitlib.ar_datetime_util import now_utc_dt
@@ -103,7 +102,7 @@ class StoryLogDBM(SimpleDBM):
103
102
  )
104
103
  title: Mapped[str | None] = mapped_column(TEXT, index=True, default=None, nullable=True)
105
104
  data: Mapped[dict[str, Any]] = mapped_column(
106
- JSONB, insert_default={}, server_default="{}", index=True, nullable=False
105
+ JSON, insert_default={}, server_default="{}", index=True, nullable=False
107
106
  )
108
107
 
109
108
 
@@ -131,13 +130,13 @@ class OperationDBM(SimpleDBM):
131
130
  execution_start_dt: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True), nullable=True)
132
131
  execution_finish_dt: Mapped[datetime | None] = mapped_column(TIMESTAMP(timezone=True), nullable=True)
133
132
  input_data: Mapped[dict[str, Any]] = mapped_column(
134
- JSONB,
133
+ JSON,
135
134
  insert_default={},
136
135
  server_default="{}",
137
136
  nullable=False
138
137
  )
139
- output_data: Mapped[dict[str, Any]] = mapped_column(JSONB, insert_default={}, server_default="{}", nullable=False)
140
- error_data: Mapped[dict[str, Any]] = mapped_column(JSONB, insert_default={}, server_default="{}", nullable=False)
138
+ output_data: Mapped[dict[str, Any]] = mapped_column(JSON, insert_default={}, server_default="{}", nullable=False)
139
+ error_data: Mapped[dict[str, Any]] = mapped_column(JSON, insert_default={}, server_default="{}", nullable=False)
141
140
 
142
141
  def raise_if_executed_with_error(self):
143
142
  if self.status == self.Statuses.executed_with_error:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arpakitlib
3
- Version: 1.7.212
3
+ Version: 1.7.216
4
4
  Summary: arpakitlib
5
5
  License: Apache-2.0
6
6
  Keywords: arpakitlib,arpakit,arpakit-company,arpakitcompany,arpakit_company
@@ -120,7 +120,7 @@ arpakitlib/ar_aiogram_util.py,sha256=5JPCDZpdBGTE-EIWPRez9amCZAX7XemFIVu5YrQK7Pw
120
120
  arpakitlib/ar_api_key_util.py,sha256=E84JlJXiDHtxLQmV8BNHvqNKu_G8-Dox0XxknYJQ37Q,422
121
121
  arpakitlib/ar_arpakit_lib_module_util.py,sha256=UEPU8wk29R_bBP_RENnhXYzNbj_RF9FWjowrj_yxWLA,5931
122
122
  arpakitlib/ar_arpakit_project_template_util.py,sha256=c7yc8w2IvZGH5hH8eOpL7JuD005hUxZ0GVDcSkJF5iI,3705
123
- arpakitlib/ar_arpakit_schedule_uust_api_client_util.py,sha256=jGbP6egs2yhgfheyqhM0J-SeM2qp2YrW7dV-u9djv4Q,19223
123
+ arpakitlib/ar_arpakit_schedule_uust_api_client_util.py,sha256=006JstWvs6JTuxRCg3toSNQvtKO0KM5pqyPJa1PRDNA,14998
124
124
  arpakitlib/ar_arpakitlib_cli_util.py,sha256=8lhEDxnwMSRX2PGV2xQtQru1AYKSA92SVolol5u7iBk,3154
125
125
  arpakitlib/ar_base64_util.py,sha256=aZkg2cZTuAaP2IWeG_LXJ6RO7qhyskVwec-Lks0iM-k,676
126
126
  arpakitlib/ar_base_worker_util.py,sha256=Qm_C7PFH5W-LPu1AGX1zp29zbqZ04i71Su1U-eeQBkA,5674
@@ -152,12 +152,12 @@ arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css,sha256=jzPZlgJTFwSdSphk9C
152
152
  arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css.map,sha256=5wq8eXMLU6Zxb45orZPL1zAsBFJReFw6GjYqGpUX3hg,262650
153
153
  arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js,sha256=ffrLZHHEQ_g84A-ul3yWa10Kk09waOAxHcQXPuZuavg,339292
154
154
  arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js.map,sha256=9UhIW7MqCOZPAz1Sl1IKfZUuhWU0p-LJqrnjjJD9Xhc,1159454
155
- arpakitlib/ar_fastapi_util.py,sha256=jpjTmUJKyx8pcOv3UcZsM1r6jPnK5Y7NYDHLm61HQK4,26228
155
+ arpakitlib/ar_fastapi_util.py,sha256=Otm6sa-xB3qUZ7ttp0A52FVEaBN3yRHQx2JKWKYDEGg,26567
156
156
  arpakitlib/ar_file_storage_in_dir_util.py,sha256=D3e3rGuHoI6xqAA5mVvEpVVpOWY1jyjNsjj2UhyHRbE,3674
157
157
  arpakitlib/ar_file_util.py,sha256=GUdJYm1tUZnYpY-SIPRHAZBHGra8NKy1eYEI0D5AfhY,489
158
158
  arpakitlib/ar_func_util.py,sha256=bCuWbSMoFXBaMNhb89sevj2oaXRk4Jk6Qjot8OXMDT4,1319
159
159
  arpakitlib/ar_hash_util.py,sha256=Iqy6KBAOLBQMFLWv676boI5sV7atT2B-fb7aCdHOmIQ,340
160
- arpakitlib/ar_http_request_util.py,sha256=Kp2wKU4wGPsJ6m5sLwCEKWMs3Uqa59SUxhBevU6Gk_s,4328
160
+ arpakitlib/ar_http_request_util.py,sha256=tvYtLiTGxJ0tsoQm4SqBsPmlUn2X0qQPDeetVEIZ7Lk,5078
161
161
  arpakitlib/ar_ip_util.py,sha256=aEAa1Hvobh9DWX7cmBAPLqnXSTiKe2hRk-WJaiKMaI8,1009
162
162
  arpakitlib/ar_json_db_util.py,sha256=CEyhIU4WuNmX5mqwBVYxUKSdpFelXvWmf_tJ1fuxMSE,7187
163
163
  arpakitlib/ar_json_util.py,sha256=wJOsN8N7Rs7r8cTgMDXrmFa1GOkcD-LghqFEYXc8zGA,1083
@@ -172,19 +172,19 @@ arpakitlib/ar_operation_execution_util.py,sha256=uWLDJWYbfsYItUQ48qOSWy4qxCTCFMj
172
172
  arpakitlib/ar_parse_command.py,sha256=-s61xcATIsfw1eV_iD3xi-grsitbGzSDoAFc5V0OFy4,3447
173
173
  arpakitlib/ar_postgresql_util.py,sha256=1AuLjEaa1Lg4pzn-ukCVnDi35Eg1k91APRTqZhIJAdo,945
174
174
  arpakitlib/ar_run_cmd_util.py,sha256=D_rPavKMmWkQtwvZFz-Io5Ak8eSODHkcFeLPzNVC68g,1072
175
- arpakitlib/ar_schedule_uust_api_client_util.py,sha256=0Ns0mMEXYEkVmP6YTAXHyNcrhNsvCJ8X-G_5XwILhJ4,6855
175
+ arpakitlib/ar_schedule_uust_api_client_util.py,sha256=93d-bc3S0PddqV65lSvmApcSoDV0gqxPVgIaB7zKXfY,6899
176
176
  arpakitlib/ar_settings_util.py,sha256=rnoTqbRuhiq7294D4crD5kbnU8-gNWJbwGU_Ls2gJoU,2199
177
177
  arpakitlib/ar_sleep_util.py,sha256=OaLtRaJQWMkGjfj_mW1RB2P4RaSWsAIH8LUoXqsH0zM,1061
178
178
  arpakitlib/ar_sqladmin_util.py,sha256=6Nv9VQssk9PB0piyuss__soYKdjVhdbIeXIv4AgUxmQ,2660
179
- arpakitlib/ar_sqlalchemy_model_util.py,sha256=nKJGN32eg3Gn5kmJwHdVJznPT5TydLsfUfwJGdypdUo,6264
179
+ arpakitlib/ar_sqlalchemy_model_util.py,sha256=DGbXcqD2hukzAiO0aQDUkJ5xOvi3yYnMKzI5y0pMXzs,6217
180
180
  arpakitlib/ar_sqlalchemy_util.py,sha256=Hcg1THrDsSR_-8dsY1CG3NWPEv0FqCbkPXFXLtjlSJ0,4207
181
181
  arpakitlib/ar_ssh_runner_util.py,sha256=e9deuUdBW7Eh0Exx2nTBhk57SaOZYaJaSjNk8q6dbJk,6804
182
182
  arpakitlib/ar_str_util.py,sha256=yU5gOwNXUQaH5b_tM5t6fXUn9oUcv5EQbVnq2wXXIpQ,3378
183
183
  arpakitlib/ar_type_util.py,sha256=9C3ErtUVs0tAUqtK-foFzjJOykfBOntfCz2IogDOgfA,4134
184
184
  arpakitlib/ar_yookassa_api_client_util.py,sha256=sh4fcUkAkdOetFn9JYoTvjcSXP-M1wU04KEY-ECLfLg,5137
185
185
  arpakitlib/ar_zabbix_api_client_util.py,sha256=Q-VR4MvoZ9aHwZeYZr9G3LwN-ANx1T5KFmF6pvPM-9M,6402
186
- arpakitlib-1.7.212.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
187
- arpakitlib-1.7.212.dist-info/METADATA,sha256=F1DoUYryVsjSst03zhO0qKSffrGXtmoClJoQt2uC36M,3176
188
- arpakitlib-1.7.212.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
189
- arpakitlib-1.7.212.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
190
- arpakitlib-1.7.212.dist-info/RECORD,,
186
+ arpakitlib-1.7.216.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
187
+ arpakitlib-1.7.216.dist-info/METADATA,sha256=Pm0yzZbxaZf_Iu4ROSrnQVVa4RbswNleQQC4PoIU9b0,3176
188
+ arpakitlib-1.7.216.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
189
+ arpakitlib-1.7.216.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
190
+ arpakitlib-1.7.216.dist-info/RECORD,,