librus-python-api 1.4.0__py3-none-any.whl → 1.6.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.
- librus_python_api/__init__.py +5 -1
- librus_python_api/class_free_days.py +73 -0
- librus_python_api/config.py +14 -0
- librus_python_api/exceptions.py +7 -0
- librus_python_api/message_content.py +18 -11
- librus_python_api/models.py +21 -0
- librus_python_api/service.py +42 -0
- {librus_python_api-1.4.0.dist-info → librus_python_api-1.6.0.dist-info}/METADATA +6 -6
- {librus_python_api-1.4.0.dist-info → librus_python_api-1.6.0.dist-info}/RECORD +11 -10
- {librus_python_api-1.4.0.dist-info → librus_python_api-1.6.0.dist-info}/WHEEL +0 -0
- {librus_python_api-1.4.0.dist-info → librus_python_api-1.6.0.dist-info}/licenses/LICENSE +0 -0
librus_python_api/__init__.py
CHANGED
|
@@ -35,6 +35,8 @@ from librus_python_api.models import (
|
|
|
35
35
|
AttendanceView,
|
|
36
36
|
AttendanceWindow,
|
|
37
37
|
Availability,
|
|
38
|
+
ClassFreeDay,
|
|
39
|
+
ClassFreeDays,
|
|
38
40
|
CompletedLesson,
|
|
39
41
|
CompletedLessons,
|
|
40
42
|
CompletedLessonsCursor,
|
|
@@ -136,7 +138,7 @@ from librus_python_api.models import (
|
|
|
136
138
|
from librus_python_api.sending import SendAttempt
|
|
137
139
|
from librus_python_api.service import AccountClient, LibrusService
|
|
138
140
|
|
|
139
|
-
__version__ = "1.
|
|
141
|
+
__version__ = "1.6.0"
|
|
140
142
|
|
|
141
143
|
__all__ = [
|
|
142
144
|
"ArchiveAbsences",
|
|
@@ -216,6 +218,8 @@ __all__ = [
|
|
|
216
218
|
"AccountClient",
|
|
217
219
|
"AccountCredentials",
|
|
218
220
|
"Availability",
|
|
221
|
+
"ClassFreeDay",
|
|
222
|
+
"ClassFreeDays",
|
|
219
223
|
"Attendance",
|
|
220
224
|
"Announcement",
|
|
221
225
|
"Agenda",
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Bounded gateway class free days from independently observed JSON structure."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import date
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from librus_python_api.config import CLASS_FREE_DAYS_MAX_RECORDS
|
|
8
|
+
from librus_python_api.exceptions import ErrorKind, LibrusError
|
|
9
|
+
from librus_python_api.models import ClassFreeDay
|
|
10
|
+
from librus_python_api.parsers import decode_json
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _identifier(value: Any) -> str:
|
|
14
|
+
if type(value) is not int or value < 0 or len(str(value)) > 64:
|
|
15
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
16
|
+
return str(value)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _reference(value: Any) -> str:
|
|
20
|
+
if not isinstance(value, dict):
|
|
21
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
22
|
+
return _identifier(value.get("Id"))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _day(value: Any) -> date:
|
|
26
|
+
result = None
|
|
27
|
+
if type(value) is str and re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", value):
|
|
28
|
+
try:
|
|
29
|
+
result = date.fromisoformat(value)
|
|
30
|
+
except ValueError:
|
|
31
|
+
pass
|
|
32
|
+
if result is None:
|
|
33
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
34
|
+
return result
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def parse_class_free_days(body: bytes) -> tuple[ClassFreeDay, ...]:
|
|
38
|
+
data = decode_json(body)
|
|
39
|
+
if not isinstance(data, dict) or not isinstance(data.get("ClassFreeDays"), list):
|
|
40
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
41
|
+
rows = data["ClassFreeDays"]
|
|
42
|
+
if len(rows) > CLASS_FREE_DAYS_MAX_RECORDS:
|
|
43
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
44
|
+
items: list[ClassFreeDay] = []
|
|
45
|
+
seen: set[str] = set()
|
|
46
|
+
for row in rows:
|
|
47
|
+
if not isinstance(row, dict):
|
|
48
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
49
|
+
identifier = _identifier(row.get("Id"))
|
|
50
|
+
start, end = _day(row.get("DateFrom")), _day(row.get("DateTo"))
|
|
51
|
+
first, last = row.get("LessonNoFrom"), row.get("LessonNoTo")
|
|
52
|
+
if "LessonNoFrom" in row or "LessonNoTo" in row:
|
|
53
|
+
if (
|
|
54
|
+
type(first) is not int
|
|
55
|
+
or type(last) is not int
|
|
56
|
+
or not 0 <= first <= last <= 99
|
|
57
|
+
):
|
|
58
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
59
|
+
if identifier in seen or start > end:
|
|
60
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
61
|
+
seen.add(identifier)
|
|
62
|
+
items.append(
|
|
63
|
+
ClassFreeDay(
|
|
64
|
+
identifier,
|
|
65
|
+
_reference(row.get("Class")),
|
|
66
|
+
_reference(row.get("Type")),
|
|
67
|
+
start,
|
|
68
|
+
end,
|
|
69
|
+
first,
|
|
70
|
+
last,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
return tuple(items)
|
librus_python_api/config.py
CHANGED
|
@@ -449,6 +449,14 @@ ENDPOINTS: Mapping[str, Endpoint] = MappingProxyType(
|
|
|
449
449
|
True,
|
|
450
450
|
Evidence.INDEPENDENTLY_OBSERVED,
|
|
451
451
|
),
|
|
452
|
+
Endpoint(
|
|
453
|
+
"class_free_days",
|
|
454
|
+
"GET",
|
|
455
|
+
"/gateway/api/2.0/ClassFreeDays",
|
|
456
|
+
SideEffect.NONE,
|
|
457
|
+
True,
|
|
458
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
459
|
+
),
|
|
452
460
|
Endpoint(
|
|
453
461
|
"gateway_attendance",
|
|
454
462
|
"GET",
|
|
@@ -855,12 +863,18 @@ WEEKDAY_LABELS = ("pon.", "wt.", "śr.", "czw.", "pt.", "sob.", "ndz.")
|
|
|
855
863
|
# Observed page-level notices shown instead of the requested content.
|
|
856
864
|
PAGE_NOTICES = MappingProxyType(
|
|
857
865
|
{
|
|
866
|
+
(
|
|
867
|
+
"Ten moduł nie jest dostępny w wykorzystywanym przez szkołę wariancie "
|
|
868
|
+
"rozwiązania LIBRUS Synergia."
|
|
869
|
+
): ErrorKind.MODULE_UNAVAILABLE,
|
|
858
870
|
"Ten widok został wyłączony przez administratora szkoły.": (
|
|
859
871
|
ErrorKind.VIEW_DISABLED
|
|
860
872
|
),
|
|
861
873
|
"Wybrano nieprawidłowy zakres daty.": ErrorKind.INVALID_INPUT,
|
|
862
874
|
}
|
|
863
875
|
)
|
|
876
|
+
MODULE_UNAVAILABLE_PATH = "/modul_niedostepny"
|
|
877
|
+
CLASS_FREE_DAYS_MAX_RECORDS = 4096
|
|
864
878
|
COMPLETED_LESSONS_MAX_WINDOW_DAYS = 371
|
|
865
879
|
COMPLETED_LESSONS_MAX_PAGE_COUNT = 1000
|
|
866
880
|
COMPLETED_LESSONS_MAX_PAGE_ITEMS = 256
|
librus_python_api/exceptions.py
CHANGED
|
@@ -14,6 +14,7 @@ __all__ = [
|
|
|
14
14
|
"LibrusError",
|
|
15
15
|
"LimitError",
|
|
16
16
|
"MaintenanceError",
|
|
17
|
+
"ModuleUnavailableError",
|
|
17
18
|
"OperationTimeoutError",
|
|
18
19
|
"ParseError",
|
|
19
20
|
"SessionExpiredError",
|
|
@@ -36,6 +37,7 @@ class ErrorKind(StrEnum):
|
|
|
36
37
|
ACCESS_DENIED = "access_denied"
|
|
37
38
|
UNSUPPORTED_CAPABILITY = "unsupported_capability"
|
|
38
39
|
VIEW_DISABLED = "view_disabled"
|
|
40
|
+
MODULE_UNAVAILABLE = "module_unavailable"
|
|
39
41
|
THROTTLED = "throttled"
|
|
40
42
|
MAINTENANCE = "maintenance"
|
|
41
43
|
CONNECTION = "connection"
|
|
@@ -101,6 +103,10 @@ class ViewDisabledError(LibrusError):
|
|
|
101
103
|
"""The school administrator has switched this Synergia view off."""
|
|
102
104
|
|
|
103
105
|
|
|
106
|
+
class ModuleUnavailableError(LibrusError):
|
|
107
|
+
"""The school's Synergia product variant does not include this module."""
|
|
108
|
+
|
|
109
|
+
|
|
104
110
|
class ThrottledError(LibrusError):
|
|
105
111
|
pass
|
|
106
112
|
|
|
@@ -154,6 +160,7 @@ _ERROR_TYPES: dict[ErrorKind, type[LibrusError]] = {
|
|
|
154
160
|
ErrorKind.ACCESS_DENIED: AccessDeniedError,
|
|
155
161
|
ErrorKind.UNSUPPORTED_CAPABILITY: UnsupportedCapabilityError,
|
|
156
162
|
ErrorKind.VIEW_DISABLED: ViewDisabledError,
|
|
163
|
+
ErrorKind.MODULE_UNAVAILABLE: ModuleUnavailableError,
|
|
157
164
|
ErrorKind.THROTTLED: ThrottledError,
|
|
158
165
|
ErrorKind.MAINTENANCE: MaintenanceError,
|
|
159
166
|
ErrorKind.CONNECTION: ConnectionError,
|
|
@@ -57,26 +57,33 @@ def _recipient_receipts(
|
|
|
57
57
|
raise LibrusError(ErrorKind.UNSUPPORTED_CAPABILITY)
|
|
58
58
|
result = []
|
|
59
59
|
total = 0
|
|
60
|
+
# Rows are recipient/status, or recipient/class/status when the page adds
|
|
61
|
+
# the class-label column; one table never mixes the two.
|
|
62
|
+
width = len(cells(table_rows[1]))
|
|
60
63
|
for row in table_rows[1:]:
|
|
61
64
|
columns = cells(row)
|
|
62
|
-
if
|
|
63
|
-
|
|
64
|
-
or
|
|
65
|
-
or
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
if (
|
|
66
|
+
len(columns) != width
|
|
67
|
+
or width not in (2, 3)
|
|
68
|
+
or any(
|
|
69
|
+
c.tag != "td"
|
|
70
|
+
or c.get("colspan", "1") != "1"
|
|
71
|
+
or c.get("rowspan", "1") != "1"
|
|
72
|
+
or next(c.iterdescendants("table"), None) is not None
|
|
73
|
+
for c in columns
|
|
74
|
+
)
|
|
68
75
|
):
|
|
69
76
|
raise LibrusError(ErrorKind.UNSUPPORTED_CAPABILITY)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
]
|
|
77
|
+
values = [text(c, MESSAGE_MAX_FIELD_LENGTH, multiline=True) for c in columns]
|
|
78
|
+
recipient, status = values[0], values[-1]
|
|
73
79
|
if not recipient or not status:
|
|
74
80
|
raise LibrusError(ErrorKind.PARSE)
|
|
75
|
-
total += len
|
|
81
|
+
total += sum(map(len, values))
|
|
76
82
|
if total > MESSAGE_MAX_RECEIPT_TEXT_LENGTH:
|
|
77
83
|
raise LibrusError(ErrorKind.LIMIT)
|
|
78
84
|
stamp = None if status == "NIE" else _timestamp(status)
|
|
79
|
-
|
|
85
|
+
label = values[1] if width == 3 and values[1] else None
|
|
86
|
+
result.append(MessageRecipientReceipt(recipient, status, stamp, label))
|
|
80
87
|
return tuple(result)
|
|
81
88
|
|
|
82
89
|
|
librus_python_api/models.py
CHANGED
|
@@ -16,6 +16,7 @@ from librus_python_api.exceptions import ErrorKind
|
|
|
16
16
|
|
|
17
17
|
type OperationName = Literal[
|
|
18
18
|
"identity",
|
|
19
|
+
"class_free_days",
|
|
19
20
|
"student_information",
|
|
20
21
|
"final_grades",
|
|
21
22
|
"grades",
|
|
@@ -210,6 +211,8 @@ class MessageRecipientReceipt:
|
|
|
210
211
|
recipient: str = field(repr=False)
|
|
211
212
|
raw_status: str = field(repr=False)
|
|
212
213
|
read_timestamp: MessageTimestamp | None = field(repr=False)
|
|
214
|
+
# Displayed class label of the optional middle column; None without it.
|
|
215
|
+
recipient_class: str | None = field(default=None, repr=False)
|
|
213
216
|
|
|
214
217
|
|
|
215
218
|
@dataclass(frozen=True, slots=True)
|
|
@@ -630,6 +633,24 @@ class CompletedLessons:
|
|
|
630
633
|
observation: "Observation"
|
|
631
634
|
|
|
632
635
|
|
|
636
|
+
@dataclass(frozen=True, slots=True)
|
|
637
|
+
class ClassFreeDay:
|
|
638
|
+
identifier: str = field(repr=False)
|
|
639
|
+
class_id: str = field(repr=False)
|
|
640
|
+
type_id: str = field(repr=False)
|
|
641
|
+
date_from: date = field(repr=False)
|
|
642
|
+
date_to: date = field(repr=False)
|
|
643
|
+
lesson_no_from: int | None = None
|
|
644
|
+
lesson_no_to: int | None = None
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
@dataclass(frozen=True, slots=True)
|
|
648
|
+
class ClassFreeDays:
|
|
649
|
+
identity: "Identity" = field(repr=False)
|
|
650
|
+
items: tuple[ClassFreeDay, ...] = field(repr=False)
|
|
651
|
+
observation: "Observation"
|
|
652
|
+
|
|
653
|
+
|
|
633
654
|
class GradeKind(StrEnum):
|
|
634
655
|
CURRENT = "current"
|
|
635
656
|
PERIOD = "period"
|
librus_python_api/service.py
CHANGED
|
@@ -27,6 +27,7 @@ from librus_python_api.attendance_frequency import (
|
|
|
27
27
|
)
|
|
28
28
|
from librus_python_api.budget import RequestBudget
|
|
29
29
|
from librus_python_api.checkpoint import CHECKPOINT_SERVICE, validate_checkpoint
|
|
30
|
+
from librus_python_api.class_free_days import parse_class_free_days
|
|
30
31
|
from librus_python_api.completed_lessons import (
|
|
31
32
|
parse_completed_lessons,
|
|
32
33
|
validate_selection,
|
|
@@ -42,6 +43,7 @@ from librus_python_api.config import (
|
|
|
42
43
|
GRADE_MAX_WINDOW_DAYS,
|
|
43
44
|
MESSAGE_MAX_CURSOR_IDS,
|
|
44
45
|
MODERN_RECIPIENT_OPERATIONS,
|
|
46
|
+
MODULE_UNAVAILABLE_PATH,
|
|
45
47
|
NOTIFICATION_COUNTS_ENDPOINT,
|
|
46
48
|
SCHEDULE_RESPONSE_VERSION,
|
|
47
49
|
SESSION_COOKIE,
|
|
@@ -83,6 +85,7 @@ from librus_python_api.models import (
|
|
|
83
85
|
AttendanceFrequency,
|
|
84
86
|
AttendanceView,
|
|
85
87
|
AttendanceWindow,
|
|
88
|
+
ClassFreeDays,
|
|
86
89
|
CompletedLesson,
|
|
87
90
|
CompletedLessons,
|
|
88
91
|
CompletedLessonsCursor,
|
|
@@ -1314,6 +1317,24 @@ class AccountClient:
|
|
|
1314
1317
|
),
|
|
1315
1318
|
)
|
|
1316
1319
|
|
|
1320
|
+
async def class_free_days(
|
|
1321
|
+
self,
|
|
1322
|
+
*,
|
|
1323
|
+
budget: RequestBudget | None = None,
|
|
1324
|
+
max_age_seconds: float = 0.0,
|
|
1325
|
+
) -> ClassFreeDays:
|
|
1326
|
+
"""Read one bounded collection; dates are civil and references stay opaque."""
|
|
1327
|
+
|
|
1328
|
+
async def fetch(budget: RequestBudget, _: bool) -> ClassFreeDays:
|
|
1329
|
+
items = await self._page(
|
|
1330
|
+
"class_free_days", budget, parse_class_free_days, content_type=JSON
|
|
1331
|
+
)
|
|
1332
|
+
return ClassFreeDays(
|
|
1333
|
+
self._session_identity(), items, self._observation("class_free_days")
|
|
1334
|
+
)
|
|
1335
|
+
|
|
1336
|
+
return await self._read(("class_free_days",), fetch, budget, max_age_seconds)
|
|
1337
|
+
|
|
1317
1338
|
async def attendance(
|
|
1318
1339
|
self,
|
|
1319
1340
|
*,
|
|
@@ -2438,6 +2459,8 @@ class AccountClient:
|
|
|
2438
2459
|
self, response: TransportResponse, content_type: str
|
|
2439
2460
|
) -> None:
|
|
2440
2461
|
if 300 <= response.status < 400:
|
|
2462
|
+
if self._is_module_unavailable_redirect(response):
|
|
2463
|
+
raise LibrusError(ErrorKind.MODULE_UNAVAILABLE)
|
|
2441
2464
|
# A redirect proves expiry only when it targets a login route.
|
|
2442
2465
|
# Other redirects must not trigger a new credential submission.
|
|
2443
2466
|
if self._is_login_redirect(response):
|
|
@@ -2446,6 +2469,25 @@ class AccountClient:
|
|
|
2446
2469
|
if response.status != 200 or _media_type(response) != content_type:
|
|
2447
2470
|
raise LibrusError(ErrorKind.PARSE)
|
|
2448
2471
|
|
|
2472
|
+
def _is_module_unavailable_redirect(self, response: TransportResponse) -> bool:
|
|
2473
|
+
location = response.headers.get("location", "")
|
|
2474
|
+
if not location or len(location) > 4096:
|
|
2475
|
+
return False
|
|
2476
|
+
try:
|
|
2477
|
+
target = urlsplit(urljoin(response.url, location))
|
|
2478
|
+
source = urlsplit(response.url)
|
|
2479
|
+
except ValueError:
|
|
2480
|
+
return False
|
|
2481
|
+
expected = urlsplit(self._service._connection.origin(ENDPOINTS["identity"]))
|
|
2482
|
+
return (
|
|
2483
|
+
(source.scheme, source.netloc) == (expected.scheme, expected.netloc)
|
|
2484
|
+
and (target.scheme, target.netloc) == (expected.scheme, expected.netloc)
|
|
2485
|
+
and not (
|
|
2486
|
+
target.username or target.password or target.query or target.fragment
|
|
2487
|
+
)
|
|
2488
|
+
and target.path == MODULE_UNAVAILABLE_PATH
|
|
2489
|
+
)
|
|
2490
|
+
|
|
2449
2491
|
def _is_login_redirect(self, response: TransportResponse) -> bool:
|
|
2450
2492
|
location = response.headers.get("location", "")
|
|
2451
2493
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: librus-python-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Independent bounded async Librus Synergia client
|
|
5
5
|
Project-URL: Repository, https://github.com/krzysztofbury/librus-python-api
|
|
6
6
|
Project-URL: Issues, https://github.com/krzysztofbury/librus-python-api/issues
|
|
@@ -42,9 +42,9 @@ private ACLs. Windows installations include `pywin32` and `tzdata` automatically
|
|
|
42
42
|
Network paths, reparse-point paths and unsafe storage permissions fail closed.
|
|
43
43
|
See [platform requirements](contracts/persistence.md#windows-disk-boundary-25).
|
|
44
44
|
|
|
45
|
-
**Status:** `1.
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
**Status:** `1.6.0` adds class free days and a typed module-unavailable outcome,
|
|
46
|
+
plus an independent live-check freshness watchdog and targeted mutation/load
|
|
47
|
+
evidence. See [verification](VERIFICATION.md).
|
|
48
48
|
The stable 1.x API does not guarantee every school layout. School features depend
|
|
49
49
|
on what each account can access. See the [compatibility policy](API.md#compatibility-policy).
|
|
50
50
|
See [limitations](#supported-features-and-limitations) below.
|
|
@@ -54,7 +54,7 @@ See [limitations](#supported-features-and-limitations) below.
|
|
|
54
54
|
Install the exact release from PyPI after its gated publication completes:
|
|
55
55
|
|
|
56
56
|
```sh
|
|
57
|
-
python -m pip install librus-python-api==1.
|
|
57
|
+
python -m pip install librus-python-api==1.6.0
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Pin the exact version qualified by your application. Before publication, use a
|
|
@@ -70,7 +70,7 @@ python -m pip install .
|
|
|
70
70
|
To install a locally built wheel instead:
|
|
71
71
|
|
|
72
72
|
```sh
|
|
73
|
-
python -m pip install ./dist/librus_python_api-1.
|
|
73
|
+
python -m pip install ./dist/librus_python_api-1.6.0-py3-none-any.whl
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
No CLI or background process is installed: import the library in your own program.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
librus_python_api/__init__.py,sha256=
|
|
1
|
+
librus_python_api/__init__.py,sha256=DhWaUkE_sGZgqB1aWZh5ZUtyjfIeMJl1KhUPV5WpzLA,6513
|
|
2
2
|
librus_python_api/_notification_bootstrap.py,sha256=18nzH0doXBNaZXCz-_ckIvRrlHTWV5Mm1t5m2-5eYGI,4669
|
|
3
3
|
librus_python_api/_notification_codec.py,sha256=hU6G3zGJ5fR8wfPC6fV2OqBKs-7XpT9eRnKO3FGsTzY,13451
|
|
4
4
|
librus_python_api/_storage.py,sha256=M_6ZAzN4zs7laOdKJBRH1I2rvy3pgqYgC_MZtsd2xtM,15893
|
|
@@ -10,11 +10,12 @@ librus_python_api/attendance.py,sha256=HeFAfnIUn4N__yp1zr635Dve55S2jmwtkeIy_6mV5
|
|
|
10
10
|
librus_python_api/attendance_frequency.py,sha256=SBhED5zuyRIHWdw4urFyTJYykb6EYB7eL4dU3mif1EY,5309
|
|
11
11
|
librus_python_api/budget.py,sha256=8fvqT7eIxyVX2P2OBG27XsYHk_HbtSS4lzuecYpHtgs,2878
|
|
12
12
|
librus_python_api/checkpoint.py,sha256=QBmG9GtpyaGRaFq-FXuGA3yR1BjuMFN9V3RTvvi_FNQ,1930
|
|
13
|
+
librus_python_api/class_free_days.py,sha256=IwoCgr-9ZgEYYQ6717s4cDpyFzbosHMzESMabXJLGqA,2422
|
|
13
14
|
librus_python_api/completed_lessons.py,sha256=sR2oLSGb2q1580l-2upCBv9sQfIcHi7CQfWr1K0EFXI,7635
|
|
14
|
-
librus_python_api/config.py,sha256=
|
|
15
|
+
librus_python_api/config.py,sha256=A5UNZ0oc8Oxt13y0To1REdQLWPgLrH-VRRpcuwodElc,55482
|
|
15
16
|
librus_python_api/detail_fields.py,sha256=K10Stx_ehDTttkUI7-G7a2Ko9exYEu5NJpR-3sBnQ60,1789
|
|
16
17
|
librus_python_api/diagnostics.py,sha256=2l7wq6RA3it-Gwj-JRLcw5qbI37fl3xII6GZA2mZj0o,856
|
|
17
|
-
librus_python_api/exceptions.py,sha256=
|
|
18
|
+
librus_python_api/exceptions.py,sha256=adVcJuzfB8wGrUlF2SfiyIlE9ZZyTAvGdWRtQIjsA3I,4879
|
|
18
19
|
librus_python_api/files.py,sha256=pQBsIJyfTOZDz83ZB-5fcd-nPMcxR1HJ155h4zTHmsA,9223
|
|
19
20
|
librus_python_api/formative_grades.py,sha256=-F-d1rnFVAIGwg1mN0cryCIBR76OW3v2L4mCWUZ3M7Q,6227
|
|
20
21
|
librus_python_api/grade_parsers.py,sha256=91Kr1-TArzqO-b94ipeIR0a98OV7tRCZQdBWsd9QLZ0,6141
|
|
@@ -22,9 +23,9 @@ librus_python_api/grade_records.py,sha256=K0ZzmU1KsA8Jo1MXh30mStApgrigaOKk5ldoBn
|
|
|
22
23
|
librus_python_api/homework_range.py,sha256=erYfvS6PhHU5XhnR6LkiLNy677hGVflY3UAuHOs-YUw,1663
|
|
23
24
|
librus_python_api/lifecycle.py,sha256=XzA5CEdZFo7iL5IMUrawnRvf2m9BFe0OseP-fpQFaAE,856
|
|
24
25
|
librus_python_api/markup.py,sha256=mjCZf6KJTWUz2vjfXbJ4zM17hnev7a5AFIOW3ip3gsE,5677
|
|
25
|
-
librus_python_api/message_content.py,sha256=
|
|
26
|
+
librus_python_api/message_content.py,sha256=v_qoEAoaYS0_9MQBaj57i5XnC7pzv_HHWefgTt4CNyM,9043
|
|
26
27
|
librus_python_api/messages.py,sha256=K9F8XVs1SojfFpdxxLV8gx9E0g81YxEHjigDzLHmDCg,10230
|
|
27
|
-
librus_python_api/models.py,sha256=
|
|
28
|
+
librus_python_api/models.py,sha256=Z6eV-ftjaN8YGUW8SntVTLjGCqe1EWRpd0P1Va8EwDs,40991
|
|
28
29
|
librus_python_api/modern_body.py,sha256=3L4P_cIrHdIxZqiinFSQrDMhG6iklT1GoA-VEWMJUiI,2682
|
|
29
30
|
librus_python_api/modern_mailbox.py,sha256=2jF9UnJZ5AF6dip-GrMcv_z2kSLpp87dIpVeXk9jMzA,20600
|
|
30
31
|
librus_python_api/modern_messages.py,sha256=qt2CXtrxCZQ-jTt0aQCD6ZaPAJQZXf0BxmvacPo9B0w,10653
|
|
@@ -41,10 +42,10 @@ librus_python_api/scheduler.py,sha256=3JplYxqvQdFz3aKXcoQUJHE9s3FPw_xRhQ2CPNLuQm
|
|
|
41
42
|
librus_python_api/school_reads.py,sha256=PCnZ4lv44j-68FgJpQiMteaPSyfhbX8_3CUyE6zhFcQ,14760
|
|
42
43
|
librus_python_api/school_year_archive.py,sha256=D0A1ywMKgoYgvi-d70__SFreMGvMb_72870Akkbb_m8,15492
|
|
43
44
|
librus_python_api/sending.py,sha256=Nvvk2r-6D6Us53a2fH5GW9PcA6bWEfuOvy6c3M5QVcE,4549
|
|
44
|
-
librus_python_api/service.py,sha256=
|
|
45
|
+
librus_python_api/service.py,sha256=7ioLx-8C-I0Qla8YUyu5UqhjBPlcUvEFpQSYH-9yjzs,96232
|
|
45
46
|
librus_python_api/timetable.py,sha256=tX1QK9fQABxzfLaHHqTe8e6rAgdOkCoTOfAHdPwNmLE,9836
|
|
46
47
|
librus_python_api/transport.py,sha256=QnYXnrv1lW4qToYoTv6HyZw-Lt4rDc-_SMTJpMp6_bc,35297
|
|
47
|
-
librus_python_api-1.
|
|
48
|
-
librus_python_api-1.
|
|
49
|
-
librus_python_api-1.
|
|
50
|
-
librus_python_api-1.
|
|
48
|
+
librus_python_api-1.6.0.dist-info/METADATA,sha256=O_HfTBq306xfvGOiGLwpWeWdHb2G9m_2c2PN9HmY4VY,11994
|
|
49
|
+
librus_python_api-1.6.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
50
|
+
librus_python_api-1.6.0.dist-info/licenses/LICENSE,sha256=IuerLb3cnZCsaB23znydjjvKqP-LmSJd3syKa9NGUts,1071
|
|
51
|
+
librus_python_api-1.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|