librus-python-api 1.0.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 +243 -0
- librus_python_api/_notification_bootstrap.py +124 -0
- librus_python_api/_notification_codec.py +396 -0
- librus_python_api/_storage.py +403 -0
- librus_python_api/_windows_filesystem.py +390 -0
- librus_python_api/announcements.py +159 -0
- librus_python_api/attachment_routes.py +114 -0
- librus_python_api/attachments.py +297 -0
- librus_python_api/attendance.py +182 -0
- librus_python_api/attendance_frequency.py +112 -0
- librus_python_api/budget.py +79 -0
- librus_python_api/checkpoint.py +61 -0
- librus_python_api/completed_lessons.py +216 -0
- librus_python_api/config.py +1410 -0
- librus_python_api/detail_fields.py +50 -0
- librus_python_api/diagnostics.py +25 -0
- librus_python_api/exceptions.py +172 -0
- librus_python_api/files.py +242 -0
- librus_python_api/grade_parsers.py +169 -0
- librus_python_api/grade_records.py +454 -0
- librus_python_api/homework_range.py +41 -0
- librus_python_api/lifecycle.py +24 -0
- librus_python_api/markup.py +147 -0
- librus_python_api/message_content.py +230 -0
- librus_python_api/messages.py +288 -0
- librus_python_api/models.py +1160 -0
- librus_python_api/modern_body.py +75 -0
- librus_python_api/modern_mailbox.py +459 -0
- librus_python_api/modern_messages.py +276 -0
- librus_python_api/notification_models.py +145 -0
- librus_python_api/notification_persistence.py +1243 -0
- librus_python_api/notification_workflow.py +337 -0
- librus_python_api/notifications.py +216 -0
- librus_python_api/parsers.py +232 -0
- librus_python_api/parsing.py +49 -0
- librus_python_api/persistence.py +419 -0
- librus_python_api/py.typed +0 -0
- librus_python_api/recipients.py +271 -0
- librus_python_api/scheduler.py +287 -0
- librus_python_api/school_reads.py +400 -0
- librus_python_api/sending.py +125 -0
- librus_python_api/service.py +2285 -0
- librus_python_api/timetable.py +261 -0
- librus_python_api/transport.py +956 -0
- librus_python_api-1.0.0.dist-info/METADATA +262 -0
- librus_python_api-1.0.0.dist-info/RECORD +48 -0
- librus_python_api-1.0.0.dist-info/WHEEL +4 -0
- librus_python_api-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""Independent bounded async client. General live compatibility is unqualified."""
|
|
2
|
+
|
|
3
|
+
from librus_python_api.attachments import AttachmentStream, ModernAttachmentStream
|
|
4
|
+
from librus_python_api.budget import RequestBudget
|
|
5
|
+
from librus_python_api.config import (
|
|
6
|
+
AccountCredentials,
|
|
7
|
+
ConnectionSettings,
|
|
8
|
+
OperationLimits,
|
|
9
|
+
SchedulerLimits,
|
|
10
|
+
TransportLimits,
|
|
11
|
+
)
|
|
12
|
+
from librus_python_api.models import (
|
|
13
|
+
AccountContext,
|
|
14
|
+
Agenda,
|
|
15
|
+
AgendaDay,
|
|
16
|
+
AgendaEvent,
|
|
17
|
+
Announcement,
|
|
18
|
+
Announcements,
|
|
19
|
+
AttachmentHeaders,
|
|
20
|
+
AttachmentMetadata,
|
|
21
|
+
Attendance,
|
|
22
|
+
AttendanceDetail,
|
|
23
|
+
AttendanceDetailContent,
|
|
24
|
+
AttendanceFrequency,
|
|
25
|
+
AttendanceKind,
|
|
26
|
+
AttendanceRecord,
|
|
27
|
+
AttendanceRecords,
|
|
28
|
+
AttendanceView,
|
|
29
|
+
AttendanceWindow,
|
|
30
|
+
Availability,
|
|
31
|
+
CompletedLesson,
|
|
32
|
+
CompletedLessons,
|
|
33
|
+
CompletedLessonsCursor,
|
|
34
|
+
CompletedLessonsPage,
|
|
35
|
+
DescriptiveGrade,
|
|
36
|
+
DescriptiveGradeSummary,
|
|
37
|
+
DetailField,
|
|
38
|
+
DetailFieldKey,
|
|
39
|
+
FinalGrades,
|
|
40
|
+
FrequencyMeasure,
|
|
41
|
+
GatewayAttendance,
|
|
42
|
+
GatewayAttendanceRecord,
|
|
43
|
+
GradeKind,
|
|
44
|
+
GradeRecords,
|
|
45
|
+
Grades,
|
|
46
|
+
GradeSummaryValue,
|
|
47
|
+
GradeView,
|
|
48
|
+
GradeWindow,
|
|
49
|
+
Homework,
|
|
50
|
+
HomeworkItem,
|
|
51
|
+
HomeworkRangeRequest,
|
|
52
|
+
Identity,
|
|
53
|
+
LuckyNumber,
|
|
54
|
+
MessageAttachment,
|
|
55
|
+
MessageAttachmentReference,
|
|
56
|
+
MessageContent,
|
|
57
|
+
MessageContentData,
|
|
58
|
+
MessageFolder,
|
|
59
|
+
MessageRecipientReceipt,
|
|
60
|
+
MessageReference,
|
|
61
|
+
Messages,
|
|
62
|
+
MessagesCursor,
|
|
63
|
+
MessagesPage,
|
|
64
|
+
MessageSummary,
|
|
65
|
+
MessageTimestamp,
|
|
66
|
+
MessagingBackend,
|
|
67
|
+
ModernAccountData,
|
|
68
|
+
ModernIdentity,
|
|
69
|
+
ModernMessageAttachment,
|
|
70
|
+
ModernMessageAttachmentReference,
|
|
71
|
+
ModernMessageContent,
|
|
72
|
+
ModernMessageRecipientReceipt,
|
|
73
|
+
ModernMessageReference,
|
|
74
|
+
ModernMessages,
|
|
75
|
+
ModernMessagesCursor,
|
|
76
|
+
ModernMessagesPage,
|
|
77
|
+
ModernMessageSummary,
|
|
78
|
+
ModernRecipient,
|
|
79
|
+
ModernRecipientReference,
|
|
80
|
+
ModernRecipients,
|
|
81
|
+
ModernRecipientType,
|
|
82
|
+
ModernRecipientTypeReference,
|
|
83
|
+
ModernRecipientTypes,
|
|
84
|
+
ModernSendSubmission,
|
|
85
|
+
NotificationCategory,
|
|
86
|
+
NotificationCount,
|
|
87
|
+
NotificationCounts,
|
|
88
|
+
NumericGrade,
|
|
89
|
+
Observation,
|
|
90
|
+
Person,
|
|
91
|
+
RecentScheduleEvent,
|
|
92
|
+
Recipient,
|
|
93
|
+
RecipientGroup,
|
|
94
|
+
RecipientGroupChoice,
|
|
95
|
+
RecipientGroupChoices,
|
|
96
|
+
RecipientGroupReference,
|
|
97
|
+
RecipientGroups,
|
|
98
|
+
RecipientReference,
|
|
99
|
+
Recipients,
|
|
100
|
+
ScheduleEventResponse,
|
|
101
|
+
ScheduleEvents,
|
|
102
|
+
ScheduleEventWire,
|
|
103
|
+
SchoolAverage,
|
|
104
|
+
SchoolDetail,
|
|
105
|
+
SchoolReference,
|
|
106
|
+
SendResult,
|
|
107
|
+
SendStatus,
|
|
108
|
+
SendSubmission,
|
|
109
|
+
StudentInformation,
|
|
110
|
+
SubjectFrequencies,
|
|
111
|
+
SubjectFrequency,
|
|
112
|
+
SubjectGradeSummary,
|
|
113
|
+
Timetable,
|
|
114
|
+
TimetableChange,
|
|
115
|
+
TimetableDay,
|
|
116
|
+
TimetableInterval,
|
|
117
|
+
TimetableLesson,
|
|
118
|
+
TimetablePeriod,
|
|
119
|
+
)
|
|
120
|
+
from librus_python_api.sending import SendAttempt
|
|
121
|
+
from librus_python_api.service import AccountClient, LibrusService
|
|
122
|
+
|
|
123
|
+
__version__ = "1.0.0"
|
|
124
|
+
|
|
125
|
+
__all__ = [
|
|
126
|
+
"AccountContext",
|
|
127
|
+
"MessagingBackend",
|
|
128
|
+
"ModernAccountData",
|
|
129
|
+
"ModernIdentity",
|
|
130
|
+
"ModernMessageAttachment",
|
|
131
|
+
"ModernAttachmentStream",
|
|
132
|
+
"ModernMessageAttachmentReference",
|
|
133
|
+
"ModernMessageContent",
|
|
134
|
+
"ModernMessageReference",
|
|
135
|
+
"ModernMessageRecipientReceipt",
|
|
136
|
+
"ModernMessages",
|
|
137
|
+
"ModernMessagesCursor",
|
|
138
|
+
"ModernMessagesPage",
|
|
139
|
+
"ModernMessageSummary",
|
|
140
|
+
"ModernRecipient",
|
|
141
|
+
"ModernRecipientReference",
|
|
142
|
+
"ModernRecipientType",
|
|
143
|
+
"ModernRecipientTypeReference",
|
|
144
|
+
"ModernRecipientTypes",
|
|
145
|
+
"ModernRecipients",
|
|
146
|
+
"ModernSendSubmission",
|
|
147
|
+
"SendAttempt",
|
|
148
|
+
"SendResult",
|
|
149
|
+
"SendStatus",
|
|
150
|
+
"SendSubmission",
|
|
151
|
+
"RecipientGroupChoice",
|
|
152
|
+
"RecipientGroupChoices",
|
|
153
|
+
"MessageRecipientReceipt",
|
|
154
|
+
"NotificationCategory",
|
|
155
|
+
"NotificationCount",
|
|
156
|
+
"NotificationCounts",
|
|
157
|
+
"ScheduleEventWire",
|
|
158
|
+
"ScheduleEventResponse",
|
|
159
|
+
"RecentScheduleEvent",
|
|
160
|
+
"ScheduleEvents",
|
|
161
|
+
"AttachmentStream",
|
|
162
|
+
"AttachmentMetadata",
|
|
163
|
+
"AttachmentHeaders",
|
|
164
|
+
"MessageAttachment",
|
|
165
|
+
"MessageAttachmentReference",
|
|
166
|
+
"MessageContent",
|
|
167
|
+
"MessageContentData",
|
|
168
|
+
"Recipient",
|
|
169
|
+
"RecipientGroup",
|
|
170
|
+
"RecipientGroupReference",
|
|
171
|
+
"RecipientGroups",
|
|
172
|
+
"RecipientReference",
|
|
173
|
+
"Recipients",
|
|
174
|
+
"MessageFolder",
|
|
175
|
+
"MessageReference",
|
|
176
|
+
"MessageSummary",
|
|
177
|
+
"MessageTimestamp",
|
|
178
|
+
"Messages",
|
|
179
|
+
"MessagesCursor",
|
|
180
|
+
"MessagesPage",
|
|
181
|
+
"CompletedLesson",
|
|
182
|
+
"CompletedLessons",
|
|
183
|
+
"CompletedLessonsCursor",
|
|
184
|
+
"CompletedLessonsPage",
|
|
185
|
+
"AccountClient",
|
|
186
|
+
"AccountCredentials",
|
|
187
|
+
"Availability",
|
|
188
|
+
"Attendance",
|
|
189
|
+
"Announcement",
|
|
190
|
+
"Agenda",
|
|
191
|
+
"AgendaDay",
|
|
192
|
+
"AgendaEvent",
|
|
193
|
+
"Homework",
|
|
194
|
+
"HomeworkItem",
|
|
195
|
+
"HomeworkRangeRequest",
|
|
196
|
+
"SchoolDetail",
|
|
197
|
+
"DetailField",
|
|
198
|
+
"DetailFieldKey",
|
|
199
|
+
"SchoolReference",
|
|
200
|
+
"Announcements",
|
|
201
|
+
"AttendanceDetail",
|
|
202
|
+
"AttendanceDetailContent",
|
|
203
|
+
"AttendanceKind",
|
|
204
|
+
"AttendanceFrequency",
|
|
205
|
+
"FrequencyMeasure",
|
|
206
|
+
"GatewayAttendance",
|
|
207
|
+
"GatewayAttendanceRecord",
|
|
208
|
+
"SubjectFrequency",
|
|
209
|
+
"SubjectFrequencies",
|
|
210
|
+
"Timetable",
|
|
211
|
+
"TimetableChange",
|
|
212
|
+
"TimetableDay",
|
|
213
|
+
"TimetableInterval",
|
|
214
|
+
"TimetableLesson",
|
|
215
|
+
"TimetablePeriod",
|
|
216
|
+
"AttendanceRecord",
|
|
217
|
+
"AttendanceRecords",
|
|
218
|
+
"AttendanceView",
|
|
219
|
+
"AttendanceWindow",
|
|
220
|
+
"ConnectionSettings",
|
|
221
|
+
"DescriptiveGrade",
|
|
222
|
+
"DescriptiveGradeSummary",
|
|
223
|
+
"FinalGrades",
|
|
224
|
+
"GradeKind",
|
|
225
|
+
"GradeRecords",
|
|
226
|
+
"Grades",
|
|
227
|
+
"GradeSummaryValue",
|
|
228
|
+
"GradeView",
|
|
229
|
+
"GradeWindow",
|
|
230
|
+
"Identity",
|
|
231
|
+
"LibrusService",
|
|
232
|
+
"LuckyNumber",
|
|
233
|
+
"NumericGrade",
|
|
234
|
+
"Observation",
|
|
235
|
+
"OperationLimits",
|
|
236
|
+
"Person",
|
|
237
|
+
"RequestBudget",
|
|
238
|
+
"SchedulerLimits",
|
|
239
|
+
"SchoolAverage",
|
|
240
|
+
"StudentInformation",
|
|
241
|
+
"SubjectGradeSummary",
|
|
242
|
+
"TransportLimits",
|
|
243
|
+
]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Original bounded validation of caller-established, neutral offline mappings."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import asdict
|
|
4
|
+
|
|
5
|
+
from librus_python_api._notification_codec import (
|
|
6
|
+
HEX,
|
|
7
|
+
canonical_notification_id,
|
|
8
|
+
dump,
|
|
9
|
+
validate_historical_item,
|
|
10
|
+
)
|
|
11
|
+
from librus_python_api.exceptions import ErrorKind, LibrusError
|
|
12
|
+
from librus_python_api.models import NotificationCategory, RecentScheduleEvent
|
|
13
|
+
from librus_python_api.notification_models import (
|
|
14
|
+
NotificationBaselineMapping,
|
|
15
|
+
NotificationBootstrap,
|
|
16
|
+
NotificationItem,
|
|
17
|
+
NotificationProvenance,
|
|
18
|
+
NotificationSeen,
|
|
19
|
+
NotificationState,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def prepare_bootstrap(
|
|
24
|
+
plan: NotificationBootstrap, per_category: int, batch_items: int, batch_bytes: int
|
|
25
|
+
) -> tuple[
|
|
26
|
+
NotificationState,
|
|
27
|
+
tuple[NotificationItem, ...],
|
|
28
|
+
tuple[NotificationBaselineMapping, ...],
|
|
29
|
+
]:
|
|
30
|
+
if type(plan.mappings) is not tuple or type(plan.pending_events) is not tuple:
|
|
31
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
32
|
+
if (
|
|
33
|
+
len(plan.mappings) > len(NotificationCategory) * per_category
|
|
34
|
+
or len(plan.pending_events) > batch_items
|
|
35
|
+
):
|
|
36
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
37
|
+
seen, unmapped = _mapped_state(plan.mappings, per_category)
|
|
38
|
+
items: list[NotificationItem] = []
|
|
39
|
+
used = 2
|
|
40
|
+
agenda_ids = set(
|
|
41
|
+
next(
|
|
42
|
+
entry.identifiers
|
|
43
|
+
for entry in seen
|
|
44
|
+
if entry.category is NotificationCategory.AGENDA
|
|
45
|
+
)
|
|
46
|
+
)
|
|
47
|
+
for event in plan.pending_events:
|
|
48
|
+
if type(event) is not RecentScheduleEvent:
|
|
49
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
50
|
+
item = NotificationItem(
|
|
51
|
+
NotificationCategory.AGENDA,
|
|
52
|
+
"0" * 64,
|
|
53
|
+
event,
|
|
54
|
+
None,
|
|
55
|
+
None,
|
|
56
|
+
NotificationProvenance.IMPORTED_HISTORY,
|
|
57
|
+
)
|
|
58
|
+
try:
|
|
59
|
+
validate_historical_item(item)
|
|
60
|
+
except LibrusError:
|
|
61
|
+
raise LibrusError(ErrorKind.INVALID_INPUT) from None
|
|
62
|
+
identifier = canonical_notification_id(NotificationCategory.AGENDA, event)
|
|
63
|
+
if identifier in agenda_ids:
|
|
64
|
+
# A conflict cannot silently consume an externally pending event.
|
|
65
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
66
|
+
agenda_ids.add(identifier)
|
|
67
|
+
final = NotificationItem(
|
|
68
|
+
item.category, identifier, event, None, None, item.provenance
|
|
69
|
+
)
|
|
70
|
+
used += len(
|
|
71
|
+
dump({"kind": "RecentScheduleEvent", "item": asdict(final)}, batch_bytes)
|
|
72
|
+
) + bool(items)
|
|
73
|
+
if used > batch_bytes:
|
|
74
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
75
|
+
items.append(final)
|
|
76
|
+
if len(agenda_ids) > per_category:
|
|
77
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
78
|
+
return NotificationState(True, seen), tuple(items), unmapped
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _mapped_state(
|
|
82
|
+
mappings: tuple[NotificationBaselineMapping, ...], per_category: int
|
|
83
|
+
) -> tuple[tuple[NotificationSeen, ...], tuple[NotificationBaselineMapping, ...]]:
|
|
84
|
+
seen: dict[NotificationCategory, list[str]] = {
|
|
85
|
+
category: [] for category in NotificationCategory
|
|
86
|
+
}
|
|
87
|
+
sources: set[tuple[NotificationCategory, str]] = set()
|
|
88
|
+
native: set[tuple[NotificationCategory, str]] = set()
|
|
89
|
+
unmapped = []
|
|
90
|
+
counts = {category: 0 for category in NotificationCategory}
|
|
91
|
+
for mapping in mappings:
|
|
92
|
+
if (
|
|
93
|
+
type(mapping) is not NotificationBaselineMapping
|
|
94
|
+
or not isinstance(mapping.category, NotificationCategory)
|
|
95
|
+
or type(mapping.source_identifier) is not str
|
|
96
|
+
or not 1 <= len(mapping.source_identifier) <= 256
|
|
97
|
+
):
|
|
98
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
99
|
+
try:
|
|
100
|
+
mapping.source_identifier.encode("utf-8")
|
|
101
|
+
except UnicodeError:
|
|
102
|
+
raise LibrusError(ErrorKind.INVALID_INPUT) from None
|
|
103
|
+
source = (mapping.category, mapping.source_identifier)
|
|
104
|
+
if source in sources:
|
|
105
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
106
|
+
sources.add(source)
|
|
107
|
+
counts[mapping.category] += 1
|
|
108
|
+
if counts[mapping.category] > per_category:
|
|
109
|
+
raise LibrusError(ErrorKind.LIMIT)
|
|
110
|
+
identifier = mapping.native_identifier
|
|
111
|
+
if identifier is None:
|
|
112
|
+
unmapped.append(mapping)
|
|
113
|
+
continue
|
|
114
|
+
if type(identifier) is not str or HEX.fullmatch(identifier) is None:
|
|
115
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
116
|
+
key = (mapping.category, identifier)
|
|
117
|
+
if key in native:
|
|
118
|
+
raise LibrusError(ErrorKind.INVALID_INPUT)
|
|
119
|
+
native.add(key)
|
|
120
|
+
seen[mapping.category].append(identifier)
|
|
121
|
+
return tuple(
|
|
122
|
+
NotificationSeen(category, tuple(identifiers))
|
|
123
|
+
for category, identifiers in seen.items()
|
|
124
|
+
), tuple(unmapped)
|