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.
Files changed (48) hide show
  1. librus_python_api/__init__.py +243 -0
  2. librus_python_api/_notification_bootstrap.py +124 -0
  3. librus_python_api/_notification_codec.py +396 -0
  4. librus_python_api/_storage.py +403 -0
  5. librus_python_api/_windows_filesystem.py +390 -0
  6. librus_python_api/announcements.py +159 -0
  7. librus_python_api/attachment_routes.py +114 -0
  8. librus_python_api/attachments.py +297 -0
  9. librus_python_api/attendance.py +182 -0
  10. librus_python_api/attendance_frequency.py +112 -0
  11. librus_python_api/budget.py +79 -0
  12. librus_python_api/checkpoint.py +61 -0
  13. librus_python_api/completed_lessons.py +216 -0
  14. librus_python_api/config.py +1410 -0
  15. librus_python_api/detail_fields.py +50 -0
  16. librus_python_api/diagnostics.py +25 -0
  17. librus_python_api/exceptions.py +172 -0
  18. librus_python_api/files.py +242 -0
  19. librus_python_api/grade_parsers.py +169 -0
  20. librus_python_api/grade_records.py +454 -0
  21. librus_python_api/homework_range.py +41 -0
  22. librus_python_api/lifecycle.py +24 -0
  23. librus_python_api/markup.py +147 -0
  24. librus_python_api/message_content.py +230 -0
  25. librus_python_api/messages.py +288 -0
  26. librus_python_api/models.py +1160 -0
  27. librus_python_api/modern_body.py +75 -0
  28. librus_python_api/modern_mailbox.py +459 -0
  29. librus_python_api/modern_messages.py +276 -0
  30. librus_python_api/notification_models.py +145 -0
  31. librus_python_api/notification_persistence.py +1243 -0
  32. librus_python_api/notification_workflow.py +337 -0
  33. librus_python_api/notifications.py +216 -0
  34. librus_python_api/parsers.py +232 -0
  35. librus_python_api/parsing.py +49 -0
  36. librus_python_api/persistence.py +419 -0
  37. librus_python_api/py.typed +0 -0
  38. librus_python_api/recipients.py +271 -0
  39. librus_python_api/scheduler.py +287 -0
  40. librus_python_api/school_reads.py +400 -0
  41. librus_python_api/sending.py +125 -0
  42. librus_python_api/service.py +2285 -0
  43. librus_python_api/timetable.py +261 -0
  44. librus_python_api/transport.py +956 -0
  45. librus_python_api-1.0.0.dist-info/METADATA +262 -0
  46. librus_python_api-1.0.0.dist-info/RECORD +48 -0
  47. librus_python_api-1.0.0.dist-info/WHEEL +4 -0
  48. 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)