librus-python-api 1.0.0__py3-none-any.whl → 1.0.1__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 +1 -1
- librus_python_api/_storage.py +4 -1
- librus_python_api/attendance_frequency.py +65 -2
- librus_python_api/config.py +19 -0
- librus_python_api/files.py +7 -2
- librus_python_api/models.py +20 -0
- librus_python_api/service.py +53 -3
- {librus_python_api-1.0.0.dist-info → librus_python_api-1.0.1.dist-info}/METADATA +4 -4
- {librus_python_api-1.0.0.dist-info → librus_python_api-1.0.1.dist-info}/RECORD +11 -11
- {librus_python_api-1.0.0.dist-info → librus_python_api-1.0.1.dist-info}/WHEEL +0 -0
- {librus_python_api-1.0.0.dist-info → librus_python_api-1.0.1.dist-info}/licenses/LICENSE +0 -0
librus_python_api/__init__.py
CHANGED
|
@@ -120,7 +120,7 @@ from librus_python_api.models import (
|
|
|
120
120
|
from librus_python_api.sending import SendAttempt
|
|
121
121
|
from librus_python_api.service import AccountClient, LibrusService
|
|
122
122
|
|
|
123
|
-
__version__ = "1.0.
|
|
123
|
+
__version__ = "1.0.1"
|
|
124
124
|
|
|
125
125
|
__all__ = [
|
|
126
126
|
"AccountContext",
|
librus_python_api/_storage.py
CHANGED
|
@@ -26,7 +26,10 @@ if TYPE_CHECKING:
|
|
|
26
26
|
|
|
27
27
|
class _StorageLimits(_ValidatedConfig):
|
|
28
28
|
operations: int = Field(default=8, ge=1, le=64)
|
|
29
|
-
|
|
29
|
+
# Several local processes (one per agent host session) commonly share one
|
|
30
|
+
# store. Each transaction is short, but FULL synchronous commits can exceed
|
|
31
|
+
# 100 ms on slow disks, so ordinary operations wait up to one second.
|
|
32
|
+
busy_timeout_seconds: float = Field(default=1.0, gt=0, le=5, allow_inf_nan=False)
|
|
30
33
|
# Final saves follow upstream side effects (a dispatched send or a consumed
|
|
31
34
|
# read-once page), so they wait longer for other contexts' write locks.
|
|
32
35
|
final_busy_timeout_seconds: float = Field(
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"""Original validated gateway parsing and explicit attendance ratio policies."""
|
|
2
2
|
|
|
3
|
-
from pydantic import ValidationError
|
|
3
|
+
from pydantic import BaseModel, ValidationError
|
|
4
4
|
|
|
5
5
|
from librus_python_api import markup
|
|
6
|
-
from librus_python_api.config import
|
|
6
|
+
from librus_python_api.config import (
|
|
7
|
+
ATTENDANCE_MAX_LESSONS,
|
|
8
|
+
ATTENDANCE_MAX_RECORDS,
|
|
9
|
+
ATTENDANCE_MAX_SUBJECTS,
|
|
10
|
+
ATTENDANCE_TYPE_KINDS,
|
|
11
|
+
)
|
|
7
12
|
from librus_python_api.exceptions import ErrorKind, LibrusError
|
|
8
13
|
from librus_python_api.models import (
|
|
9
14
|
AttendanceKind,
|
|
@@ -11,7 +16,9 @@ from librus_python_api.models import (
|
|
|
11
16
|
GatewayAttendanceRecord,
|
|
12
17
|
_AttendanceEnvelopeWire,
|
|
13
18
|
_LessonEnvelopeWire,
|
|
19
|
+
_LessonsEnvelopeWire,
|
|
14
20
|
_SubjectEnvelopeWire,
|
|
21
|
+
_SubjectsEnvelopeWire,
|
|
15
22
|
)
|
|
16
23
|
from librus_python_api.parsers import decode_json
|
|
17
24
|
|
|
@@ -84,6 +91,62 @@ def parse_subject_name(body: bytes, identifier: str) -> str:
|
|
|
84
91
|
return subject.Name.strip()
|
|
85
92
|
|
|
86
93
|
|
|
94
|
+
def _collection[M: BaseModel](data: object, key: str, maximum: int, wire: type[M]) -> M:
|
|
95
|
+
if (
|
|
96
|
+
isinstance(data, dict)
|
|
97
|
+
and isinstance(data.get(key), list)
|
|
98
|
+
and len(data[key]) > maximum
|
|
99
|
+
):
|
|
100
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
101
|
+
failed = False
|
|
102
|
+
envelope = None
|
|
103
|
+
try:
|
|
104
|
+
envelope = wire.model_validate(data)
|
|
105
|
+
except ValidationError:
|
|
106
|
+
failed = True
|
|
107
|
+
if failed or envelope is None:
|
|
108
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
109
|
+
return envelope
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _unique(pairs: list[tuple[str, str]]) -> dict[str, str]:
|
|
113
|
+
result: dict[str, str] = {}
|
|
114
|
+
for identifier, value in pairs:
|
|
115
|
+
# Conflicting rows are never resolved by choosing one of them.
|
|
116
|
+
if identifier in result and result[identifier] != value:
|
|
117
|
+
raise LibrusError(ErrorKind.PARSE)
|
|
118
|
+
result[identifier] = value
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_lesson_subjects(body: bytes) -> dict[str, str]:
|
|
123
|
+
"""Map lesson IDs to subject IDs; rows without a subject are omitted."""
|
|
124
|
+
envelope = _collection(
|
|
125
|
+
decode_json(body), "Lessons", ATTENDANCE_MAX_LESSONS, _LessonsEnvelopeWire
|
|
126
|
+
)
|
|
127
|
+
return _unique(
|
|
128
|
+
[
|
|
129
|
+
(lesson.Id, lesson.Subject.Id)
|
|
130
|
+
for lesson in envelope.Lessons
|
|
131
|
+
if lesson.Subject is not None
|
|
132
|
+
]
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def parse_subject_names(body: bytes) -> dict[str, str]:
|
|
137
|
+
"""Map subject IDs to nonblank names; blank names are omitted."""
|
|
138
|
+
envelope = _collection(
|
|
139
|
+
decode_json(body), "Subjects", ATTENDANCE_MAX_SUBJECTS, _SubjectsEnvelopeWire
|
|
140
|
+
)
|
|
141
|
+
return _unique(
|
|
142
|
+
[
|
|
143
|
+
(subject.Id, subject.Name.strip())
|
|
144
|
+
for subject in envelope.Subjects
|
|
145
|
+
if subject.Name is not None and subject.Name.strip()
|
|
146
|
+
]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
87
150
|
def summarize_frequency(
|
|
88
151
|
rows: tuple[GatewayAttendanceRecord, ...],
|
|
89
152
|
*,
|
librus_python_api/config.py
CHANGED
|
@@ -401,6 +401,22 @@ ENDPOINTS: Mapping[str, Endpoint] = MappingProxyType(
|
|
|
401
401
|
True,
|
|
402
402
|
Evidence.INDEPENDENTLY_OBSERVED,
|
|
403
403
|
),
|
|
404
|
+
Endpoint(
|
|
405
|
+
"attendance_lessons",
|
|
406
|
+
"GET",
|
|
407
|
+
"/gateway/api/2.0/Lessons",
|
|
408
|
+
SideEffect.NONE,
|
|
409
|
+
True,
|
|
410
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
411
|
+
),
|
|
412
|
+
Endpoint(
|
|
413
|
+
"attendance_subjects",
|
|
414
|
+
"GET",
|
|
415
|
+
"/gateway/api/2.0/Subjects",
|
|
416
|
+
SideEffect.NONE,
|
|
417
|
+
True,
|
|
418
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
419
|
+
),
|
|
404
420
|
Endpoint(
|
|
405
421
|
"attendance_lesson",
|
|
406
422
|
"GET",
|
|
@@ -636,6 +652,9 @@ ATTENDANCE_SEMESTER_LABELS = MappingProxyType(
|
|
|
636
652
|
ATTENDANCE_EMPTY_MARKERS = frozenset({"", "-", "Brak nieobecności"})
|
|
637
653
|
ATTENDANCE_MAX_RECORDS = 2048
|
|
638
654
|
ATTENDANCE_MAX_WINDOW_DAYS = 371
|
|
655
|
+
# Whole-school gateway collections resolve attendance metadata in two requests.
|
|
656
|
+
ATTENDANCE_MAX_LESSONS = 4096
|
|
657
|
+
ATTENDANCE_MAX_SUBJECTS = 1024
|
|
639
658
|
# Collection recognition and detail retrieval share this central path family.
|
|
640
659
|
ATTENDANCE_DETAIL_PATH_PREFIX = "/przegladaj_nb/szczegoly/"
|
|
641
660
|
ATTENDANCE_DETAIL_MAX_FIELDS = 32
|
librus_python_api/files.py
CHANGED
|
@@ -166,6 +166,7 @@ async def publish_attachment(
|
|
|
166
166
|
size = 0
|
|
167
167
|
digest = hashlib.sha256()
|
|
168
168
|
failed = False
|
|
169
|
+
published: PublishedAttachment | None = None
|
|
169
170
|
try:
|
|
170
171
|
if sys.platform == "win32":
|
|
171
172
|
from librus_python_api._windows_filesystem import (
|
|
@@ -206,9 +207,10 @@ async def publish_attachment(
|
|
|
206
207
|
final = await _disk(
|
|
207
208
|
lambda: _publish(directory_fd, descriptor, temporary, name)
|
|
208
209
|
)
|
|
209
|
-
|
|
210
|
+
published = PublishedAttachment(
|
|
210
211
|
directory / final, size, digest.hexdigest(), content_type
|
|
211
212
|
)
|
|
213
|
+
return published
|
|
212
214
|
except OSError:
|
|
213
215
|
failed = True
|
|
214
216
|
finally:
|
|
@@ -237,6 +239,9 @@ async def publish_attachment(
|
|
|
237
239
|
windows_directory.close()
|
|
238
240
|
except OSError:
|
|
239
241
|
failed = True
|
|
240
|
-
|
|
242
|
+
# The atomic link is the commit point. Failing to remove our random
|
|
243
|
+
# temporary name afterwards leaves an inert private file, but must not
|
|
244
|
+
# report a complete, published attachment as a failed transfer.
|
|
245
|
+
if failed and published is None:
|
|
241
246
|
raise LibrusError(ErrorKind.STORAGE) from None
|
|
242
247
|
raise LibrusError(ErrorKind.STORAGE)
|
librus_python_api/models.py
CHANGED
|
@@ -1104,6 +1104,26 @@ class _SubjectEnvelopeWire(BaseModel):
|
|
|
1104
1104
|
Subject: _SubjectWire
|
|
1105
1105
|
|
|
1106
1106
|
|
|
1107
|
+
class _CollectionLessonWire(_NumericReferenceWire):
|
|
1108
|
+
# Collection rows may omit a subject; such lessons resolve individually.
|
|
1109
|
+
Subject: _NumericReferenceWire | None = None
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
class _LessonsEnvelopeWire(BaseModel):
|
|
1113
|
+
model_config = ConfigDict(strict=True, extra="ignore", hide_input_in_errors=True)
|
|
1114
|
+
Lessons: list[_CollectionLessonWire]
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
class _CollectionSubjectWire(_NumericReferenceWire):
|
|
1118
|
+
# Blank names resolve individually, where the strict subject contract applies.
|
|
1119
|
+
Name: Annotated[str | None, Field(max_length=1024)] = None
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
class _SubjectsEnvelopeWire(BaseModel):
|
|
1123
|
+
model_config = ConfigDict(strict=True, extra="ignore", hide_input_in_errors=True)
|
|
1124
|
+
Subjects: list[_CollectionSubjectWire]
|
|
1125
|
+
|
|
1126
|
+
|
|
1107
1127
|
@dataclass(frozen=True, slots=True)
|
|
1108
1128
|
class ProfileFields:
|
|
1109
1129
|
name: str = field(repr=False)
|
librus_python_api/service.py
CHANGED
|
@@ -10,7 +10,7 @@ import time
|
|
|
10
10
|
from collections.abc import Awaitable, Callable, Hashable, Mapping
|
|
11
11
|
from dataclasses import dataclass
|
|
12
12
|
from datetime import UTC, date, datetime
|
|
13
|
-
from types import TracebackType
|
|
13
|
+
from types import MappingProxyType, TracebackType
|
|
14
14
|
from typing import Any, Literal, Self, cast
|
|
15
15
|
from urllib.parse import urljoin, urlsplit
|
|
16
16
|
|
|
@@ -20,7 +20,9 @@ from librus_python_api.attendance import parse_attendance, parse_attendance_deta
|
|
|
20
20
|
from librus_python_api.attendance_frequency import (
|
|
21
21
|
parse_gateway_attendance,
|
|
22
22
|
parse_lesson_subject,
|
|
23
|
+
parse_lesson_subjects,
|
|
23
24
|
parse_subject_name,
|
|
25
|
+
parse_subject_names,
|
|
24
26
|
summarize_frequency,
|
|
25
27
|
)
|
|
26
28
|
from librus_python_api.budget import RequestBudget
|
|
@@ -345,6 +347,11 @@ def _require_dates(*days: date | None, max_days: int | None = None) -> None:
|
|
|
345
347
|
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
346
348
|
|
|
347
349
|
|
|
350
|
+
_METADATA_MAP_FALLBACK = frozenset(
|
|
351
|
+
{ErrorKind.ACCESS_DENIED, ErrorKind.CONNECTION, ErrorKind.LIMIT, ErrorKind.PARSE}
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
|
|
348
355
|
class AccountClient:
|
|
349
356
|
"""Service-owned account context. Fresh reads are the default.
|
|
350
357
|
|
|
@@ -388,6 +395,7 @@ class AccountClient:
|
|
|
388
395
|
self._identity: Identity | None = None
|
|
389
396
|
self._cache: dict[Hashable, tuple[float, Any]] = {}
|
|
390
397
|
self._metadata: dict[tuple[str, str], tuple[float, str]] = {}
|
|
398
|
+
self._metadata_maps: dict[str, tuple[float, Mapping[str, str]]] = {}
|
|
391
399
|
self._cooldowns: dict[str, tuple[float, ErrorKind]] = {}
|
|
392
400
|
self._consuming_schedule = False
|
|
393
401
|
self._modern_account: ModernAccountData | None = None
|
|
@@ -1910,6 +1918,7 @@ class AccountClient:
|
|
|
1910
1918
|
self._modern_account = None
|
|
1911
1919
|
self._cache.clear()
|
|
1912
1920
|
self._metadata.clear()
|
|
1921
|
+
self._metadata_maps.clear()
|
|
1913
1922
|
self._transport.clear_auth()
|
|
1914
1923
|
|
|
1915
1924
|
def _invalidate_modern(self) -> None:
|
|
@@ -2132,6 +2141,36 @@ class AccountClient:
|
|
|
2132
2141
|
del self._metadata[next(iter(self._metadata))]
|
|
2133
2142
|
return value
|
|
2134
2143
|
|
|
2144
|
+
async def _metadata_map(
|
|
2145
|
+
self, endpoint: str, budget: RequestBudget
|
|
2146
|
+
) -> Mapping[str, str]:
|
|
2147
|
+
"""One login-scoped collection read replaces per-reference lookups."""
|
|
2148
|
+
cached = self._metadata_maps.get(endpoint)
|
|
2149
|
+
if cached and time.monotonic() - cached[0] <= ATTENDANCE_METADATA_TTL_SECONDS:
|
|
2150
|
+
return cached[1]
|
|
2151
|
+
parser = (
|
|
2152
|
+
parse_lesson_subjects
|
|
2153
|
+
if endpoint == "attendance_lessons"
|
|
2154
|
+
else parse_subject_names
|
|
2155
|
+
)
|
|
2156
|
+
try:
|
|
2157
|
+
value: Mapping[str, str] = MappingProxyType(
|
|
2158
|
+
await self._page(endpoint, budget, parser, content_type=JSON)
|
|
2159
|
+
)
|
|
2160
|
+
except LibrusError as error:
|
|
2161
|
+
# An unavailable or unexpected collection degrades to the original
|
|
2162
|
+
# per-reference lookups. Deadlines, budgets, cancellation and session
|
|
2163
|
+
# failures still propagate. Only deterministic outcomes are cached.
|
|
2164
|
+
if error.kind not in _METADATA_MAP_FALLBACK:
|
|
2165
|
+
raise
|
|
2166
|
+
if error.kind is ErrorKind.LIMIT:
|
|
2167
|
+
budget.check() # An exhausted caller budget is not a large collection.
|
|
2168
|
+
if error.kind is ErrorKind.CONNECTION:
|
|
2169
|
+
return MappingProxyType({})
|
|
2170
|
+
value = MappingProxyType({})
|
|
2171
|
+
self._metadata_maps[endpoint] = time.monotonic(), value
|
|
2172
|
+
return value
|
|
2173
|
+
|
|
2135
2174
|
async def _subject_frequencies(
|
|
2136
2175
|
self, budget: RequestBudget, start: date | None, end: date | None
|
|
2137
2176
|
) -> SubjectFrequencies:
|
|
@@ -2147,8 +2186,15 @@ class AccountClient:
|
|
|
2147
2186
|
lessons = dict.fromkeys(row.lesson_id for row in rows)
|
|
2148
2187
|
if len(lessons) > ATTENDANCE_METADATA_CACHE_SIZE:
|
|
2149
2188
|
raise LibrusError(ErrorKind.LIMIT)
|
|
2189
|
+
# Two collection reads keep the request count independent of how many
|
|
2190
|
+
# lessons and subjects a student has. References absent from a
|
|
2191
|
+
# collection keep the strict per-reference contract.
|
|
2192
|
+
lesson_map = (
|
|
2193
|
+
await self._metadata_map("attendance_lessons", budget) if lessons else {}
|
|
2194
|
+
)
|
|
2150
2195
|
lesson_subjects = {
|
|
2151
|
-
lesson:
|
|
2196
|
+
lesson: lesson_map.get(lesson)
|
|
2197
|
+
or await self._metadata_value("attendance_lesson", lesson, budget)
|
|
2152
2198
|
for lesson in lessons
|
|
2153
2199
|
}
|
|
2154
2200
|
subjects: dict[str, list[GatewayAttendanceRecord]] = {}
|
|
@@ -2156,10 +2202,14 @@ class AccountClient:
|
|
|
2156
2202
|
subjects.setdefault(lesson_subjects[row.lesson_id], []).append(row)
|
|
2157
2203
|
if len(subjects) > ATTENDANCE_METADATA_CACHE_SIZE:
|
|
2158
2204
|
raise LibrusError(ErrorKind.LIMIT)
|
|
2205
|
+
subject_map = (
|
|
2206
|
+
await self._metadata_map("attendance_subjects", budget) if subjects else {}
|
|
2207
|
+
)
|
|
2159
2208
|
items = [
|
|
2160
2209
|
SubjectFrequency(
|
|
2161
2210
|
subject,
|
|
2162
|
-
|
|
2211
|
+
subject_map.get(subject)
|
|
2212
|
+
or await self._metadata_value("attendance_subject", subject, budget),
|
|
2163
2213
|
summarize_frequency(tuple(records), subject_policy=True),
|
|
2164
2214
|
)
|
|
2165
2215
|
for subject, records in subjects.items()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: librus-python-api
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
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
|
|
@@ -40,7 +40,7 @@ private ACLs. Windows installations include `pywin32` and `tzdata` automatically
|
|
|
40
40
|
Network paths, reparse-point paths and unsafe storage permissions fail closed.
|
|
41
41
|
See [platform requirements](contracts/persistence.md#windows-disk-boundary-25).
|
|
42
42
|
|
|
43
|
-
**Status:** `1.0.
|
|
43
|
+
**Status:** `1.0.x` freezes the documented native library API. It does not claim
|
|
44
44
|
completed MCP cutover, production-state migration or universal live-school
|
|
45
45
|
compatibility. Those consumer/live gates remain separate. School features depend
|
|
46
46
|
on what each account can access. See the [compatibility policy](API.md#compatibility-policy).
|
|
@@ -51,7 +51,7 @@ See [limitations](#supported-features-and-limitations) below.
|
|
|
51
51
|
Install the exact release from PyPI after its gated publication completes:
|
|
52
52
|
|
|
53
53
|
```sh
|
|
54
|
-
python -m pip install librus-python-api==1.0.
|
|
54
|
+
python -m pip install librus-python-api==1.0.1
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Pin the exact version qualified by your application. Before publication, use a
|
|
@@ -67,7 +67,7 @@ python -m pip install .
|
|
|
67
67
|
To install a locally built wheel instead:
|
|
68
68
|
|
|
69
69
|
```sh
|
|
70
|
-
python -m pip install ./dist/librus_python_api-1.0.
|
|
70
|
+
python -m pip install ./dist/librus_python_api-1.0.1-py3-none-any.whl
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
No CLI or background process is installed: import the library in your own program.
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
librus_python_api/__init__.py,sha256=
|
|
1
|
+
librus_python_api/__init__.py,sha256=YAo4-5FnnoG8Rx29PXJFTCxiQ0Rb6biHI52JCXsdQ0A,5657
|
|
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
|
-
librus_python_api/_storage.py,sha256=
|
|
4
|
+
librus_python_api/_storage.py,sha256=M_6ZAzN4zs7laOdKJBRH1I2rvy3pgqYgC_MZtsd2xtM,15893
|
|
5
5
|
librus_python_api/_windows_filesystem.py,sha256=pQANIGkYrb4Q75gz5mdr5mqYhM_PFblWwTxPGM0bQ78,14063
|
|
6
6
|
librus_python_api/announcements.py,sha256=13FoOR1sq7tt9w6aLf0vclcFKDqLUiIYaO5iYD2QVLA,5928
|
|
7
7
|
librus_python_api/attachment_routes.py,sha256=uSg5wCQqHJiphF0KdI0GatQxZr5SLUx6xWQRsl_U4_U,4123
|
|
8
8
|
librus_python_api/attachments.py,sha256=kyIBpFxeK34Al5_jCkxYKpZnsSLa5DYaGS6vHADpy_g,10921
|
|
9
9
|
librus_python_api/attendance.py,sha256=HeFAfnIUn4N__yp1zr635Dve55S2jmwtkeIy_6mV5B4,6924
|
|
10
|
-
librus_python_api/attendance_frequency.py,sha256=
|
|
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
13
|
librus_python_api/completed_lessons.py,sha256=sR2oLSGb2q1580l-2upCBv9sQfIcHi7CQfWr1K0EFXI,7635
|
|
14
|
-
librus_python_api/config.py,sha256=
|
|
14
|
+
librus_python_api/config.py,sha256=E3rMMc5qsw_imdpODI09Y6EOv0fcfbuWLPcIkY-ChwQ,49057
|
|
15
15
|
librus_python_api/detail_fields.py,sha256=K10Stx_ehDTttkUI7-G7a2Ko9exYEu5NJpR-3sBnQ60,1789
|
|
16
16
|
librus_python_api/diagnostics.py,sha256=2l7wq6RA3it-Gwj-JRLcw5qbI37fl3xII6GZA2mZj0o,856
|
|
17
17
|
librus_python_api/exceptions.py,sha256=yWWoo0xyrQAk1d13HOJZnQBsq4ulh_P0hRSI6ZiNQOE,4622
|
|
18
|
-
librus_python_api/files.py,sha256=
|
|
18
|
+
librus_python_api/files.py,sha256=pQBsIJyfTOZDz83ZB-5fcd-nPMcxR1HJ155h4zTHmsA,9223
|
|
19
19
|
librus_python_api/grade_parsers.py,sha256=91Kr1-TArzqO-b94ipeIR0a98OV7tRCZQdBWsd9QLZ0,6141
|
|
20
20
|
librus_python_api/grade_records.py,sha256=HvGHQ8Fyx_1Ne5k6N8cBeTqrDCzM5P6saj8eijDbRA4,16793
|
|
21
21
|
librus_python_api/homework_range.py,sha256=erYfvS6PhHU5XhnR6LkiLNy677hGVflY3UAuHOs-YUw,1663
|
|
@@ -23,7 +23,7 @@ librus_python_api/lifecycle.py,sha256=XzA5CEdZFo7iL5IMUrawnRvf2m9BFe0OseP-fpQFaA
|
|
|
23
23
|
librus_python_api/markup.py,sha256=GZk7MmBBGB5SIVY_VngmQxQErI3eGHOpJs3mFVlMR3M,5298
|
|
24
24
|
librus_python_api/message_content.py,sha256=sUPuqpjuavqDyn6QGM5aPH4mpoQV62bDLepSSQ2AsUE,8686
|
|
25
25
|
librus_python_api/messages.py,sha256=K9F8XVs1SojfFpdxxLV8gx9E0g81YxEHjigDzLHmDCg,10230
|
|
26
|
-
librus_python_api/models.py,sha256=
|
|
26
|
+
librus_python_api/models.py,sha256=aGPst3eV44w4YB-0HbmskumDz-KAHekTf6J6_CHaRbE,33826
|
|
27
27
|
librus_python_api/modern_body.py,sha256=3L4P_cIrHdIxZqiinFSQrDMhG6iklT1GoA-VEWMJUiI,2682
|
|
28
28
|
librus_python_api/modern_mailbox.py,sha256=nRrmIHb7RfV49xjUTBlDcgtxOdmXEpp4F1L0IvcGQxw,16118
|
|
29
29
|
librus_python_api/modern_messages.py,sha256=9gysqjUkQGtMKmnCL7boRzNDnAhtL5ibAJ2rkNZWCec,9694
|
|
@@ -39,10 +39,10 @@ librus_python_api/recipients.py,sha256=6GQCKo-Zjg3VFSg1J8YXcDsy_U3r6-MUS4VCvz0xd
|
|
|
39
39
|
librus_python_api/scheduler.py,sha256=3JplYxqvQdFz3aKXcoQUJHE9s3FPw_xRhQ2CPNLuQmA,10729
|
|
40
40
|
librus_python_api/school_reads.py,sha256=PCnZ4lv44j-68FgJpQiMteaPSyfhbX8_3CUyE6zhFcQ,14760
|
|
41
41
|
librus_python_api/sending.py,sha256=Nvvk2r-6D6Us53a2fH5GW9PcA6bWEfuOvy6c3M5QVcE,4549
|
|
42
|
-
librus_python_api/service.py,sha256=
|
|
42
|
+
librus_python_api/service.py,sha256=tPDO0MlbhD6j0PHIZAHGpAVPvCMq2bLLK1HXGR7QQdc,86428
|
|
43
43
|
librus_python_api/timetable.py,sha256=tX1QK9fQABxzfLaHHqTe8e6rAgdOkCoTOfAHdPwNmLE,9836
|
|
44
44
|
librus_python_api/transport.py,sha256=6S5wwBiRkUfKmi6w2t8msxlTSzCo-IbIBE13FeBc7Jw,35357
|
|
45
|
-
librus_python_api-1.0.
|
|
46
|
-
librus_python_api-1.0.
|
|
47
|
-
librus_python_api-1.0.
|
|
48
|
-
librus_python_api-1.0.
|
|
45
|
+
librus_python_api-1.0.1.dist-info/METADATA,sha256=rwPPhwhdUftJky5wfqstlurtWaiXT93gAPEKi_1oQtY,11514
|
|
46
|
+
librus_python_api-1.0.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
47
|
+
librus_python_api-1.0.1.dist-info/licenses/LICENSE,sha256=IuerLb3cnZCsaB23znydjjvKqP-LmSJd3syKa9NGUts,1071
|
|
48
|
+
librus_python_api-1.0.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|