librus-python-api 1.0.2__tar.gz → 1.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/API.md +37 -5
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/CHANGELOG.md +59 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/PKG-INFO +3 -3
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/README.md +2 -2
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/__init__.py +15 -1
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/config.py +130 -6
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/models.py +80 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/modern_mailbox.py +137 -9
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/modern_messages.py +23 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/service.py +225 -27
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/transport.py +3 -6
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/.gitignore +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/LICENSE +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/SECURITY.md +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/pyproject.toml +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/_notification_bootstrap.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/_notification_codec.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/_storage.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/_windows_filesystem.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/announcements.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/attachment_routes.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/attachments.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/attendance.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/attendance_frequency.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/budget.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/checkpoint.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/completed_lessons.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/detail_fields.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/diagnostics.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/exceptions.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/files.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/grade_parsers.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/grade_records.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/homework_range.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/lifecycle.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/markup.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/message_content.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/messages.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/modern_body.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/notification_models.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/notification_persistence.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/notification_workflow.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/notifications.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/parsers.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/parsing.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/persistence.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/py.typed +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/recipients.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/scheduler.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/school_reads.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/sending.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/src/librus_python_api/timetable.py +0 -0
- {librus_python_api-1.0.2 → librus_python_api-1.2.1}/uv.lock +0 -0
|
@@ -399,13 +399,45 @@ redirects, fallback, post-send lookup or implicit reauthentication. Cancel/shutd
|
|
|
399
399
|
propagate normally with an inspectable outcome after joined cleanup, as below.
|
|
400
400
|
Modern list/content reads do not reconcile durable UNKNOWN send history.
|
|
401
401
|
|
|
402
|
-
- `modern_messages_page(folder=RECEIVED, *, page=1, page_size=10,
|
|
403
|
-
max_age_seconds=0)` returns `ModernMessagesPage`. Pages are
|
|
404
|
-
returns summaries only; sent read status can be unknown.
|
|
402
|
+
- `modern_messages_page(folder=RECEIVED, *, page=1, page_size=10, archived=False,
|
|
403
|
+
budget=None, max_age_seconds=0)` returns `ModernMessagesPage`. Pages are
|
|
404
|
+
one-based. This GET returns summaries only; sent read status can be unknown.
|
|
405
405
|
- `modern_messages(folder=RECEIVED, *, cursor=None, page_size=50, max_pages=4,
|
|
406
|
-
limit=128, budget=None, max_age_seconds=0)` returns
|
|
407
|
-
truncation, duplicate counts and
|
|
406
|
+
limit=128, archived=False, budget=None, max_age_seconds=0)` returns
|
|
407
|
+
`ModernMessages` with explicit truncation, duplicate counts and
|
|
408
|
+
account/folder/mailbox/page-size-bound continuation.
|
|
408
409
|
Cursor drift and later-page errors never return partial output.
|
|
410
|
+
- `archived=True` (1.1.0) lists the archive mailbox instead of the current one.
|
|
411
|
+
Pages, collections, cursors and references carry `archived`; a cursor never
|
|
412
|
+
continues the other mailbox. Archived content opens raise
|
|
413
|
+
`UNSUPPORTED_CAPABILITY` because no archived content route or mark-read effect
|
|
414
|
+
is established. Archive pages and collections report the upstream
|
|
415
|
+
`archivingInProgress` status as `archiving_in_progress` (`None` for the
|
|
416
|
+
current mailbox). Live archives report `true` while listing normally, so it
|
|
417
|
+
is informational; before 1.2.1 it wrongly made every archive read
|
|
418
|
+
`UNSUPPORTED_CAPABILITY`.
|
|
419
|
+
- `modern_correspondents(folder=RECEIVED, *, budget=None, max_age_seconds=0)`
|
|
420
|
+
(1.2.0) returns `ModernCorrespondents`: the senders (received) or receivers
|
|
421
|
+
(sent) the web app offers in its filter panel. Each `ModernCorrespondent` has
|
|
422
|
+
`first_name`, `last_name` (one may be empty, never both) and a login-bound
|
|
423
|
+
`ModernCorrespondentReference`.
|
|
424
|
+
- `modern_messages_page()` and `modern_messages()` accept `correspondent=` (a
|
|
425
|
+
reference for the same folder and login) and, for the received folder,
|
|
426
|
+
`unread_only=True` (1.2.0). They send the web app's own `senderId`,
|
|
427
|
+
`receiverId` and `unreadOnly` filters. Results and cursors record the filter,
|
|
428
|
+
and a cursor never continues a differently filtered list. Filters on the
|
|
429
|
+
archive mailbox raise `UNSUPPORTED_CAPABILITY`; they were not observed there.
|
|
430
|
+
- `modern_teacher_subjects(*, budget=None, max_age_seconds=0)` (1.2.0) returns
|
|
431
|
+
`ModernTeacherSubjects`: `ModernTeacherSubject(teacher_identifier, subject)`
|
|
432
|
+
pairs, one per subject a teacher teaches the student. Identical repeated rows
|
|
433
|
+
collapse to one (1.2.1). The identifier is the
|
|
434
|
+
teacher's modern account ID; it is informational and not a recipient reference.
|
|
435
|
+
- `modern_unread_counts(*, budget=None, max_age_seconds=0)` (1.1.0) returns
|
|
436
|
+
`ModernUnreadCounts` with `current` and `archive` `ModernUnreadFolders`:
|
|
437
|
+
`inbox`, `notes`, `alerts`, `substitutions`, `absences`, `justifications` and
|
|
438
|
+
`trash`. It opens no message. A missing or invalid counter fails with `PARSE`;
|
|
439
|
+
unknown extra counters are ignored. Populated counter semantics beyond the
|
|
440
|
+
observed field names are not independently established.
|
|
409
441
|
Page size is 1-50, page number 1-1,000, `max_pages` 1-8, `limit` 1-256 and
|
|
410
442
|
cursor history at most 2,000 IDs. These are library bounds, not upstream maxima.
|
|
411
443
|
- `modern_message_content(reference, *, allow_mark_read=False, budget=None,
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.1 (2026-10-06) - Live fixes for archive listing and teacher subjects
|
|
4
|
+
|
|
5
|
+
Compatible patch release found by the new pre-publication live check. 1.1.0 and
|
|
6
|
+
1.2.0 were tagged but not published to PyPI because that check failed.
|
|
7
|
+
|
|
8
|
+
- List the modern archive mailbox. Live archive pages report
|
|
9
|
+
`archivingInProgress: true` while listing normally, and 1.1.0/1.2.0 treated
|
|
10
|
+
that as unsupported, so every archive read failed. Archive pages and
|
|
11
|
+
collections now report it as `archiving_in_progress`; the current mailbox
|
|
12
|
+
still refuses the flag.
|
|
13
|
+
- Collapse identical repeated rows in `modern_teacher_subjects()` instead of
|
|
14
|
+
failing with `PARSE`.
|
|
15
|
+
|
|
16
|
+
## 1.2.0 (2026-10-06) - Modern correspondent filters and teacher subjects
|
|
17
|
+
|
|
18
|
+
Compatible minor release. Existing calls and results are unchanged; new fields
|
|
19
|
+
have defaults.
|
|
20
|
+
|
|
21
|
+
- Add `modern_correspondents(folder)`, listing the senders (received) or
|
|
22
|
+
receivers (sent) the modern mailbox can be filtered by.
|
|
23
|
+
- Filter `modern_messages_page()` and `modern_messages()` by `correspondent=`
|
|
24
|
+
and, for received messages, `unread_only=True`, using the web app's own
|
|
25
|
+
query filters. Pages, collections and cursors record the filter; a cursor
|
|
26
|
+
never continues a differently filtered list. The archive mailbox does not
|
|
27
|
+
accept filters.
|
|
28
|
+
- Add `modern_teacher_subjects()`, pairing teachers' modern account IDs with
|
|
29
|
+
the subjects they teach the student.
|
|
30
|
+
|
|
31
|
+
## 1.1.0 (2026-10-06) - Modern archive mailbox and unread counts
|
|
32
|
+
|
|
33
|
+
Compatible minor release. Existing calls and results are unchanged; new fields
|
|
34
|
+
have defaults.
|
|
35
|
+
|
|
36
|
+
- List the modern archive mailbox (earlier school years) with
|
|
37
|
+
`modern_messages_page(..., archived=True)` and `modern_messages(...,
|
|
38
|
+
archived=True)`. Pages, collections, cursors and message references gain an
|
|
39
|
+
`archived` field (default `False`), and a cursor never continues the other
|
|
40
|
+
mailbox. Opening an archived message raises `UNSUPPORTED_CAPABILITY` until an
|
|
41
|
+
archived content route is established.
|
|
42
|
+
- Add `modern_unread_counts()`, returning `ModernUnreadCounts` with current and
|
|
43
|
+
archive `ModernUnreadFolders` counters (inbox, notes, alerts, substitutions,
|
|
44
|
+
absences, justifications, trash) from the modern mailbox's own counter route.
|
|
45
|
+
It opens no message.
|
|
46
|
+
|
|
47
|
+
## 1.0.3 (2026-10-06) - Notification counts for parent logins
|
|
48
|
+
|
|
49
|
+
Compatible patch release. The public library API is unchanged.
|
|
50
|
+
|
|
51
|
+
- Read `notification_counts()` from the information page (`/informacja`), which
|
|
52
|
+
every login can open, instead of the student landing route `/uczen/index`.
|
|
53
|
+
That route returned 403 for a parent login in a live check, and the session
|
|
54
|
+
was unusable afterwards. The counters come from the menu shared by every
|
|
55
|
+
page, so the result is unchanged for student logins. The read is now
|
|
56
|
+
retry-safe: a proven session expiry is recovered once.
|
|
57
|
+
- Count the modern messages launcher link (`/wiadomosci3`) as the messages
|
|
58
|
+
category. Before, schools linking it showed no messages counter.
|
|
59
|
+
- Keep `/uczen/index` only as an accepted login redirect target. It is never
|
|
60
|
+
requested directly.
|
|
61
|
+
|
|
3
62
|
## 1.0.2 (2026-10-06) - Faster shared request policy
|
|
4
63
|
|
|
5
64
|
Compatible patch release. The public library API is unchanged.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: librus-python-api
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -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.
|
|
54
|
+
python -m pip install librus-python-api==1.2.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.
|
|
70
|
+
python -m pip install ./dist/librus_python_api-1.2.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.
|
|
@@ -25,7 +25,7 @@ See [limitations](#supported-features-and-limitations) below.
|
|
|
25
25
|
Install the exact release from PyPI after its gated publication completes:
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
|
-
python -m pip install librus-python-api==1.
|
|
28
|
+
python -m pip install librus-python-api==1.2.1
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Pin the exact version qualified by your application. Before publication, use a
|
|
@@ -41,7 +41,7 @@ python -m pip install .
|
|
|
41
41
|
To install a locally built wheel instead:
|
|
42
42
|
|
|
43
43
|
```sh
|
|
44
|
-
python -m pip install ./dist/librus_python_api-1.
|
|
44
|
+
python -m pip install ./dist/librus_python_api-1.2.1-py3-none-any.whl
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
No CLI or background process is installed: import the library in your own program.
|
|
@@ -65,6 +65,9 @@ from librus_python_api.models import (
|
|
|
65
65
|
MessageTimestamp,
|
|
66
66
|
MessagingBackend,
|
|
67
67
|
ModernAccountData,
|
|
68
|
+
ModernCorrespondent,
|
|
69
|
+
ModernCorrespondentReference,
|
|
70
|
+
ModernCorrespondents,
|
|
68
71
|
ModernIdentity,
|
|
69
72
|
ModernMessageAttachment,
|
|
70
73
|
ModernMessageAttachmentReference,
|
|
@@ -82,6 +85,10 @@ from librus_python_api.models import (
|
|
|
82
85
|
ModernRecipientTypeReference,
|
|
83
86
|
ModernRecipientTypes,
|
|
84
87
|
ModernSendSubmission,
|
|
88
|
+
ModernTeacherSubject,
|
|
89
|
+
ModernTeacherSubjects,
|
|
90
|
+
ModernUnreadCounts,
|
|
91
|
+
ModernUnreadFolders,
|
|
85
92
|
NotificationCategory,
|
|
86
93
|
NotificationCount,
|
|
87
94
|
NotificationCounts,
|
|
@@ -120,7 +127,7 @@ from librus_python_api.models import (
|
|
|
120
127
|
from librus_python_api.sending import SendAttempt
|
|
121
128
|
from librus_python_api.service import AccountClient, LibrusService
|
|
122
129
|
|
|
123
|
-
__version__ = "1.
|
|
130
|
+
__version__ = "1.2.1"
|
|
124
131
|
|
|
125
132
|
__all__ = [
|
|
126
133
|
"AccountContext",
|
|
@@ -135,6 +142,13 @@ __all__ = [
|
|
|
135
142
|
"ModernMessageRecipientReceipt",
|
|
136
143
|
"ModernMessages",
|
|
137
144
|
"ModernMessagesCursor",
|
|
145
|
+
"ModernUnreadCounts",
|
|
146
|
+
"ModernCorrespondent",
|
|
147
|
+
"ModernCorrespondentReference",
|
|
148
|
+
"ModernCorrespondents",
|
|
149
|
+
"ModernTeacherSubject",
|
|
150
|
+
"ModernTeacherSubjects",
|
|
151
|
+
"ModernUnreadFolders",
|
|
138
152
|
"ModernMessagesPage",
|
|
139
153
|
"ModernMessageSummary",
|
|
140
154
|
"ModernRecipient",
|
|
@@ -207,6 +207,60 @@ ENDPOINTS: Mapping[str, Endpoint] = MappingProxyType(
|
|
|
207
207
|
Evidence.INDEPENDENTLY_OBSERVED,
|
|
208
208
|
"messages",
|
|
209
209
|
),
|
|
210
|
+
Endpoint(
|
|
211
|
+
"modern_archive_messages_received",
|
|
212
|
+
"GET",
|
|
213
|
+
"/api/archive/inbox/messages",
|
|
214
|
+
SideEffect.NONE,
|
|
215
|
+
False,
|
|
216
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
217
|
+
"messages",
|
|
218
|
+
),
|
|
219
|
+
Endpoint(
|
|
220
|
+
"modern_archive_messages_sent",
|
|
221
|
+
"GET",
|
|
222
|
+
"/api/archive/outbox/messages",
|
|
223
|
+
SideEffect.NONE,
|
|
224
|
+
False,
|
|
225
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
226
|
+
"messages",
|
|
227
|
+
),
|
|
228
|
+
Endpoint(
|
|
229
|
+
"modern_unread_counts",
|
|
230
|
+
"GET",
|
|
231
|
+
"/api/inbox/unreadMessagesCount",
|
|
232
|
+
SideEffect.NONE,
|
|
233
|
+
False,
|
|
234
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
235
|
+
"messages",
|
|
236
|
+
),
|
|
237
|
+
Endpoint(
|
|
238
|
+
"modern_senders",
|
|
239
|
+
"GET",
|
|
240
|
+
"/api/inbox/messages/senders",
|
|
241
|
+
SideEffect.NONE,
|
|
242
|
+
False,
|
|
243
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
244
|
+
"messages",
|
|
245
|
+
),
|
|
246
|
+
Endpoint(
|
|
247
|
+
"modern_receivers",
|
|
248
|
+
"GET",
|
|
249
|
+
"/api/outbox/messages/receivers",
|
|
250
|
+
SideEffect.NONE,
|
|
251
|
+
False,
|
|
252
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
253
|
+
"messages",
|
|
254
|
+
),
|
|
255
|
+
Endpoint(
|
|
256
|
+
"modern_teacher_subjects",
|
|
257
|
+
"GET",
|
|
258
|
+
"/api/receivers/student-subjects",
|
|
259
|
+
SideEffect.NONE,
|
|
260
|
+
False,
|
|
261
|
+
Evidence.INDEPENDENTLY_OBSERVED,
|
|
262
|
+
"messages",
|
|
263
|
+
),
|
|
210
264
|
Endpoint(
|
|
211
265
|
"modern_content_received",
|
|
212
266
|
"GET",
|
|
@@ -337,8 +391,10 @@ ENDPOINTS: Mapping[str, Endpoint] = MappingProxyType(
|
|
|
337
391
|
False,
|
|
338
392
|
Evidence.SYNTHETIC_ONLY,
|
|
339
393
|
),
|
|
394
|
+
# Accepted only as a redirect target during login. Never requested
|
|
395
|
+
# directly: it answers parent logins with 403.
|
|
340
396
|
Endpoint(
|
|
341
|
-
"
|
|
397
|
+
"login_student_landing",
|
|
342
398
|
"GET",
|
|
343
399
|
"/uczen/index",
|
|
344
400
|
SideEffect.AUTHENTICATION,
|
|
@@ -695,6 +751,10 @@ CHECKPOINT_TIMEOUT_SECONDS = 5.0
|
|
|
695
751
|
CHECKPOINT_MAX_TIMEOUT_SECONDS = 30.0
|
|
696
752
|
SCHEDULE_RESPONSE_VERSION = 1
|
|
697
753
|
SCHEDULE_MAX_EVENTS = 1024
|
|
754
|
+
# Every Synergia page carries the counter menu. The information page is
|
|
755
|
+
# readable by student and parent logins alike; the student landing route
|
|
756
|
+
# (/uczen/index) answers parent logins with 403 and is not used.
|
|
757
|
+
NOTIFICATION_COUNTS_ENDPOINT = "student_information"
|
|
698
758
|
NOTIFICATION_MAX_MENU_ITEMS = 64
|
|
699
759
|
NOTIFICATION_MAX_COUNT = 1000000
|
|
700
760
|
NOTIFICATION_DESTINATIONS = MappingProxyType(
|
|
@@ -702,6 +762,7 @@ NOTIFICATION_DESTINATIONS = MappingProxyType(
|
|
|
702
762
|
"/przegladaj_oceny/uczen": "grades",
|
|
703
763
|
"/przegladaj_nb/uczen": "attendance",
|
|
704
764
|
"/wiadomosci": "messages",
|
|
765
|
+
"/wiadomosci3": "messages",
|
|
705
766
|
"/ogloszenia": "announcements",
|
|
706
767
|
"/terminarz": "agenda",
|
|
707
768
|
"/moje_zadania": "homework",
|
|
@@ -871,17 +932,67 @@ def modern_directory_query(
|
|
|
871
932
|
|
|
872
933
|
|
|
873
934
|
def modern_mailbox_query(
|
|
874
|
-
folder: MessageFolder,
|
|
935
|
+
folder: MessageFolder,
|
|
936
|
+
page: int,
|
|
937
|
+
page_size: int,
|
|
938
|
+
*,
|
|
939
|
+
correspondent: str | None = None,
|
|
940
|
+
unread_only: bool = False,
|
|
875
941
|
) -> dict[str, str]:
|
|
942
|
+
"""Page selection plus the web app's own sender/receiver/unread filters."""
|
|
876
943
|
if (
|
|
877
944
|
not isinstance(folder, MessageFolder)
|
|
878
945
|
or type(page) is not int
|
|
879
946
|
or not 1 <= page <= 1000
|
|
880
947
|
or type(page_size) is not int
|
|
881
948
|
or not 1 <= page_size <= 50
|
|
949
|
+
or type(unread_only) is not bool
|
|
950
|
+
or (unread_only and folder is not MessageFolder.RECEIVED)
|
|
951
|
+
or (
|
|
952
|
+
correspondent is not None
|
|
953
|
+
and (
|
|
954
|
+
type(correspondent) is not str
|
|
955
|
+
or re.fullmatch(r"[0-9]{1,64}", correspondent) is None
|
|
956
|
+
)
|
|
957
|
+
)
|
|
882
958
|
):
|
|
883
959
|
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
884
|
-
|
|
960
|
+
query = {}
|
|
961
|
+
if correspondent is not None:
|
|
962
|
+
query[MODERN_CORRESPONDENT_FILTERS[folder]] = correspondent
|
|
963
|
+
if unread_only:
|
|
964
|
+
query["unreadOnly"] = "1"
|
|
965
|
+
return query | {"page": str(page), "limit": str(page_size)}
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
MODERN_MAILBOX_OPERATIONS = frozenset(
|
|
969
|
+
{
|
|
970
|
+
"modern_messages_received",
|
|
971
|
+
"modern_messages_sent",
|
|
972
|
+
"modern_archive_messages_received",
|
|
973
|
+
"modern_archive_messages_sent",
|
|
974
|
+
}
|
|
975
|
+
)
|
|
976
|
+
MODERN_CORRESPONDENT_FILTERS = MappingProxyType(
|
|
977
|
+
{MessageFolder.RECEIVED: "senderId", MessageFolder.SENT: "receiverId"}
|
|
978
|
+
)
|
|
979
|
+
# Filters observed in the web app for the current mailbox only.
|
|
980
|
+
MODERN_MAILBOX_FILTERS = MappingProxyType(
|
|
981
|
+
{
|
|
982
|
+
"modern_messages_received": frozenset({"senderId", "unreadOnly"}),
|
|
983
|
+
"modern_messages_sent": frozenset({"receiverId"}),
|
|
984
|
+
}
|
|
985
|
+
)
|
|
986
|
+
MODERN_UNREAD_COUNT_FIELDS = (
|
|
987
|
+
"inbox",
|
|
988
|
+
"notes",
|
|
989
|
+
"alerts",
|
|
990
|
+
"substitutions",
|
|
991
|
+
"absences",
|
|
992
|
+
"justifications",
|
|
993
|
+
"trash",
|
|
994
|
+
)
|
|
995
|
+
MODERN_MAX_UNREAD_COUNT = 1000000
|
|
885
996
|
|
|
886
997
|
|
|
887
998
|
def validate_modern_query(operation: str, query: Mapping[str, str]) -> None:
|
|
@@ -890,14 +1001,27 @@ def validate_modern_query(operation: str, query: Mapping[str, str]) -> None:
|
|
|
890
1001
|
type(v) is not str for v in query.values()
|
|
891
1002
|
):
|
|
892
1003
|
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
893
|
-
if operation in
|
|
894
|
-
|
|
1004
|
+
if operation in MODERN_MAILBOX_OPERATIONS:
|
|
1005
|
+
filters = MODERN_MAILBOX_FILTERS.get(operation, frozenset())
|
|
1006
|
+
if not {"page", "limit"} <= set(query) <= {"page", "limit"} | filters or any(
|
|
895
1007
|
re.fullmatch(r"[1-9][0-9]{0,3}", query[name]) is None
|
|
896
1008
|
for name in ("page", "limit")
|
|
897
1009
|
):
|
|
898
1010
|
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
1011
|
+
folder = (
|
|
1012
|
+
MessageFolder.SENT
|
|
1013
|
+
if operation == "modern_messages_sent"
|
|
1014
|
+
else MessageFolder.RECEIVED
|
|
1015
|
+
)
|
|
1016
|
+
unread = query.get("unreadOnly")
|
|
1017
|
+
if unread not in (None, "1"):
|
|
1018
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
899
1019
|
modern_mailbox_query(
|
|
900
|
-
|
|
1020
|
+
folder,
|
|
1021
|
+
int(query["page"]),
|
|
1022
|
+
int(query["limit"]),
|
|
1023
|
+
correspondent=query.get(MODERN_CORRESPONDENT_FILTERS[folder]),
|
|
1024
|
+
unread_only=unread == "1",
|
|
901
1025
|
)
|
|
902
1026
|
return
|
|
903
1027
|
allowed = [
|
|
@@ -48,6 +48,12 @@ type OperationName = Literal[
|
|
|
48
48
|
"modern_send_message",
|
|
49
49
|
"modern_school_recipients",
|
|
50
50
|
"modern_class_parents",
|
|
51
|
+
"modern_archive_messages_received",
|
|
52
|
+
"modern_archive_messages_sent",
|
|
53
|
+
"modern_unread_counts",
|
|
54
|
+
"modern_senders",
|
|
55
|
+
"modern_receivers",
|
|
56
|
+
"modern_teacher_subjects",
|
|
51
57
|
"modern_messages_received",
|
|
52
58
|
"modern_messages_sent",
|
|
53
59
|
"modern_content_received",
|
|
@@ -393,6 +399,46 @@ class ModernMessageReference:
|
|
|
393
399
|
folder: MessageFolder
|
|
394
400
|
identifier: str = field(repr=False)
|
|
395
401
|
account: str = field(repr=False)
|
|
402
|
+
# Listed from the archive mailbox. Archived content opens are not supported.
|
|
403
|
+
archived: bool = False
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@dataclass(frozen=True, slots=True)
|
|
407
|
+
class ModernCorrespondentReference:
|
|
408
|
+
"""A sender (received folder) or receiver (sent folder) to filter a list by."""
|
|
409
|
+
|
|
410
|
+
folder: MessageFolder
|
|
411
|
+
identifier: str = field(repr=False)
|
|
412
|
+
account: str = field(repr=False)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@dataclass(frozen=True, slots=True)
|
|
416
|
+
class ModernCorrespondent:
|
|
417
|
+
reference: ModernCorrespondentReference = field(repr=False)
|
|
418
|
+
first_name: str = field(repr=False)
|
|
419
|
+
last_name: str = field(repr=False)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
@dataclass(frozen=True, slots=True)
|
|
423
|
+
class ModernCorrespondents:
|
|
424
|
+
identity: "Identity" = field(repr=False)
|
|
425
|
+
folder: MessageFolder
|
|
426
|
+
items: tuple[ModernCorrespondent, ...] = field(repr=False)
|
|
427
|
+
observation: "Observation"
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
@dataclass(frozen=True, slots=True)
|
|
431
|
+
class ModernTeacherSubject:
|
|
432
|
+
# The teacher's modern account ID, as a decimal string.
|
|
433
|
+
teacher_identifier: str = field(repr=False)
|
|
434
|
+
subject: str = field(repr=False)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
@dataclass(frozen=True, slots=True)
|
|
438
|
+
class ModernTeacherSubjects:
|
|
439
|
+
identity: "Identity" = field(repr=False)
|
|
440
|
+
items: tuple[ModernTeacherSubject, ...] = field(repr=False)
|
|
441
|
+
observation: "Observation"
|
|
396
442
|
|
|
397
443
|
|
|
398
444
|
@dataclass(frozen=True, slots=True)
|
|
@@ -417,6 +463,11 @@ class ModernMessagesPage:
|
|
|
417
463
|
items: tuple[ModernMessageSummary, ...] = field(repr=False)
|
|
418
464
|
fingerprint: str = field(repr=False)
|
|
419
465
|
observation: "Observation"
|
|
466
|
+
archived: bool = False
|
|
467
|
+
correspondent: ModernCorrespondentReference | None = field(default=None, repr=False)
|
|
468
|
+
unread_only: bool = False
|
|
469
|
+
# Archive listings only: the upstream archivingInProgress status flag.
|
|
470
|
+
archiving_in_progress: bool | None = None
|
|
420
471
|
|
|
421
472
|
|
|
422
473
|
@dataclass(frozen=True, slots=True)
|
|
@@ -429,6 +480,9 @@ class ModernMessagesCursor:
|
|
|
429
480
|
total_count: int
|
|
430
481
|
fingerprint: str = field(repr=False)
|
|
431
482
|
seen_ids: tuple[str, ...] = field(repr=False)
|
|
483
|
+
archived: bool = False
|
|
484
|
+
correspondent: str | None = field(default=None, repr=False)
|
|
485
|
+
unread_only: bool = False
|
|
432
486
|
|
|
433
487
|
|
|
434
488
|
@dataclass(frozen=True, slots=True)
|
|
@@ -441,6 +495,32 @@ class ModernMessages:
|
|
|
441
495
|
next_cursor: ModernMessagesCursor | None = field(repr=False)
|
|
442
496
|
truncation_reason: Literal["item_limit", "page_limit"] | None
|
|
443
497
|
observation: "Observation"
|
|
498
|
+
archived: bool = False
|
|
499
|
+
correspondent: ModernCorrespondentReference | None = field(default=None, repr=False)
|
|
500
|
+
unread_only: bool = False
|
|
501
|
+
# Archive listings only: the upstream archivingInProgress status flag.
|
|
502
|
+
archiving_in_progress: bool | None = None
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
@dataclass(frozen=True, slots=True)
|
|
506
|
+
class ModernUnreadFolders:
|
|
507
|
+
"""Unread counters the modern mailbox shows for one mailbox section."""
|
|
508
|
+
|
|
509
|
+
inbox: int
|
|
510
|
+
notes: int
|
|
511
|
+
alerts: int
|
|
512
|
+
substitutions: int
|
|
513
|
+
absences: int
|
|
514
|
+
justifications: int
|
|
515
|
+
trash: int
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
@dataclass(frozen=True, slots=True)
|
|
519
|
+
class ModernUnreadCounts:
|
|
520
|
+
identity: "Identity" = field(repr=False)
|
|
521
|
+
current: ModernUnreadFolders
|
|
522
|
+
archive: ModernUnreadFolders
|
|
523
|
+
observation: "Observation"
|
|
444
524
|
|
|
445
525
|
|
|
446
526
|
@dataclass(frozen=True, slots=True)
|