PyHiveLMS 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.
- pyhive/__init__.py +13 -0
- pyhive/cli/__init__.py +0 -0
- pyhive/cli/main.py +30 -0
- pyhive/client.py +570 -0
- pyhive/src/__init__.py +0 -0
- pyhive/src/_generated_versions.py +3 -0
- pyhive/src/api_versions.py +6 -0
- pyhive/src/authenticated_hive_client.py +250 -0
- pyhive/src/types/__init__.py +0 -0
- pyhive/src/types/assignment.py +210 -0
- pyhive/src/types/assignment_response.py +205 -0
- pyhive/src/types/assignment_response_content.py +131 -0
- pyhive/src/types/autocheck_status.py +94 -0
- pyhive/src/types/class_.py +113 -0
- pyhive/src/types/common.py +56 -0
- pyhive/src/types/core_item.py +22 -0
- pyhive/src/types/enums/__init__.py +0 -0
- pyhive/src/types/enums/action_enum.py +18 -0
- pyhive/src/types/enums/assignment_response_type_enum.py +17 -0
- pyhive/src/types/enums/assignment_status_enum.py +17 -0
- pyhive/src/types/enums/class_type_enum.py +13 -0
- pyhive/src/types/enums/clearance_enum.py +16 -0
- pyhive/src/types/enums/event_type_enum.py +14 -0
- pyhive/src/types/enums/exercise_patbas_enum.py +15 -0
- pyhive/src/types/enums/exercise_preview_types.py +15 -0
- pyhive/src/types/enums/form_field_type_enum.py +15 -0
- pyhive/src/types/enums/gender_enum.py +14 -0
- pyhive/src/types/enums/help_response_type_enum.py +14 -0
- pyhive/src/types/enums/help_status_enum.py +13 -0
- pyhive/src/types/enums/help_type_enum.py +18 -0
- pyhive/src/types/enums/queue_rule_enum.py +15 -0
- pyhive/src/types/enums/status_enum.py +21 -0
- pyhive/src/types/enums/sync_status_enum.py +15 -0
- pyhive/src/types/enums/visibility_enum.py +14 -0
- pyhive/src/types/event.py +140 -0
- pyhive/src/types/event_attendees_type_0_item.py +69 -0
- pyhive/src/types/event_color.py +63 -0
- pyhive/src/types/exercise.py +216 -0
- pyhive/src/types/form_field.py +152 -0
- pyhive/src/types/help_.py +275 -0
- pyhive/src/types/help_response.py +113 -0
- pyhive/src/types/help_response_segel_nested.py +129 -0
- pyhive/src/types/module.py +141 -0
- pyhive/src/types/notification_nested.py +80 -0
- pyhive/src/types/program.py +180 -0
- pyhive/src/types/queue.py +150 -0
- pyhive/src/types/queue_item.py +88 -0
- pyhive/src/types/subject.py +156 -0
- pyhive/src/types/tag.py +62 -0
- pyhive/src/types/user.py +450 -0
- pyhive/types.py +23 -0
- pyhivelms-1.0.0.dist-info/METADATA +156 -0
- pyhivelms-1.0.0.dist-info/RECORD +55 -0
- pyhivelms-1.0.0.dist-info/WHEEL +4 -0
- pyhivelms-1.0.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Model for student help requests (auto-generated).
|
|
2
|
+
|
|
3
|
+
This module defines the :class:`Help` model used to represent student
|
|
4
|
+
help requests and provides serialization helpers.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Self, TypeVar, Union, cast
|
|
9
|
+
|
|
10
|
+
from attr import field
|
|
11
|
+
from attrs import define
|
|
12
|
+
from .common import UNSET, Unset
|
|
13
|
+
from .enums.help_status_enum import HelpStatusEnum
|
|
14
|
+
from .enums.help_type_enum import HelpTypeEnum
|
|
15
|
+
from .enums.visibility_enum import VisibilityEnum
|
|
16
|
+
from .core_item import HiveCoreItem
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ...client import HiveClient
|
|
20
|
+
from .user import User
|
|
21
|
+
from .exercise import Exercise
|
|
22
|
+
from .help_response_segel_nested import HelpResponseSegelNested
|
|
23
|
+
from .notification_nested import NotificationNested
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
T = TypeVar("T", bound="Help")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@define
|
|
30
|
+
class Help(HiveCoreItem):
|
|
31
|
+
"""A student's help request.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
id (int):
|
|
35
|
+
user (int):
|
|
36
|
+
checker (Union[None, int]):
|
|
37
|
+
checker_first_name (str):
|
|
38
|
+
checker_last_name (str):
|
|
39
|
+
is_subscribed (bool):
|
|
40
|
+
help_type (HelpTypeEnum):
|
|
41
|
+
* `Exercise` - Exercise
|
|
42
|
+
* `Medical` - Medical
|
|
43
|
+
* `Error` - Error
|
|
44
|
+
* `Music` - Music
|
|
45
|
+
* `Request` - Request
|
|
46
|
+
* `Other` - Other
|
|
47
|
+
* `Chat` - Chat
|
|
48
|
+
help_status (HelpStatusEnum):
|
|
49
|
+
* `Resolved` - Resolved
|
|
50
|
+
* `Open` - Open
|
|
51
|
+
for_exercise (Union['Exercise', None]):
|
|
52
|
+
responses (list['HelpResponseSegelNested']):
|
|
53
|
+
notifications (list['NotificationNested']):
|
|
54
|
+
title (Union[Unset, str]):
|
|
55
|
+
visibility (Union[Unset, VisibilityEnum]):
|
|
56
|
+
* `All Staff` - Allstaff
|
|
57
|
+
* `All Staff And Checkers` - Allstaffandcheckers
|
|
58
|
+
* `Author Only` - Authoronly
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
hive_client: "HiveClient"
|
|
63
|
+
id: int
|
|
64
|
+
user_id: int
|
|
65
|
+
_user: Union["User", None] = field(init=False, default=None)
|
|
66
|
+
checker_id: None | int
|
|
67
|
+
_checker: Union["User", None] = field(init=False, default=None)
|
|
68
|
+
checker_first_name: str
|
|
69
|
+
checker_last_name: str
|
|
70
|
+
is_subscribed: bool
|
|
71
|
+
help_type: HelpTypeEnum
|
|
72
|
+
help_status: HelpStatusEnum
|
|
73
|
+
for_exercise_id: int | None
|
|
74
|
+
_for_exercise: Union["Exercise", None] = field(init=False, default=None)
|
|
75
|
+
responses: list["HelpResponseSegelNested"]
|
|
76
|
+
notifications: list["NotificationNested"]
|
|
77
|
+
title: Unset | str = UNSET
|
|
78
|
+
visibility: Unset | VisibilityEnum = UNSET
|
|
79
|
+
|
|
80
|
+
def to_dict(self) -> dict[str, Any]: # pylint: disable=too-many-locals
|
|
81
|
+
"""Serialize this Help instance to a plain dictionary.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A JSON-serializable mapping representing this help request.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
# Import locally to avoid circular imports at module import time.
|
|
88
|
+
# Keep the import local but silence pylint's import-outside-toplevel.
|
|
89
|
+
from .exercise import ( # pylint: disable=import-outside-toplevel
|
|
90
|
+
Exercise,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Keep the attribute name `id` as generated; silence redefined-builtin warning.
|
|
94
|
+
id = self.id # pylint: disable=redefined-builtin
|
|
95
|
+
|
|
96
|
+
user = self.user
|
|
97
|
+
|
|
98
|
+
checker_id = self.checker_id
|
|
99
|
+
|
|
100
|
+
checker_first_name = self.checker_first_name
|
|
101
|
+
|
|
102
|
+
checker_last_name = self.checker_last_name
|
|
103
|
+
|
|
104
|
+
is_subscribed = self.is_subscribed
|
|
105
|
+
|
|
106
|
+
help_type = self.help_type.value
|
|
107
|
+
|
|
108
|
+
help_status = self.help_status.value
|
|
109
|
+
|
|
110
|
+
for_exercise: None | dict[str, Any]
|
|
111
|
+
for_exercise = (
|
|
112
|
+
self.for_exercise.to_dict()
|
|
113
|
+
if isinstance(self.for_exercise, Exercise)
|
|
114
|
+
else self.for_exercise
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
responses: list[dict[str, Any]] = []
|
|
118
|
+
for responses_item_data in self.responses:
|
|
119
|
+
responses_item = responses_item_data.to_dict()
|
|
120
|
+
responses.append(responses_item)
|
|
121
|
+
|
|
122
|
+
notifications: list[dict[str, Any]] = []
|
|
123
|
+
for notifications_item_data in self.notifications:
|
|
124
|
+
notifications_item = notifications_item_data.to_dict()
|
|
125
|
+
notifications.append(notifications_item)
|
|
126
|
+
|
|
127
|
+
title = self.title
|
|
128
|
+
|
|
129
|
+
visibility: Unset | str = UNSET
|
|
130
|
+
if not isinstance(self.visibility, Unset):
|
|
131
|
+
visibility = self.visibility.value
|
|
132
|
+
|
|
133
|
+
field_dict: dict[str, Any] = {}
|
|
134
|
+
field_dict.update(
|
|
135
|
+
{
|
|
136
|
+
"id": id,
|
|
137
|
+
"user": user,
|
|
138
|
+
"checker": checker_id,
|
|
139
|
+
"checker_first_name": checker_first_name,
|
|
140
|
+
"checker_last_name": checker_last_name,
|
|
141
|
+
"is_subscribed": is_subscribed,
|
|
142
|
+
"help_type": help_type,
|
|
143
|
+
"help_status": help_status,
|
|
144
|
+
"for_exercise": for_exercise,
|
|
145
|
+
"responses": responses,
|
|
146
|
+
"notifications": notifications,
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
if title is not UNSET:
|
|
150
|
+
field_dict["title"] = title
|
|
151
|
+
if visibility is not UNSET:
|
|
152
|
+
field_dict["visibility"] = visibility
|
|
153
|
+
|
|
154
|
+
return field_dict
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_dict( # pylint: disable=too-many-locals
|
|
158
|
+
cls, src_dict: Mapping[str, Any], hive_client: "HiveClient"
|
|
159
|
+
) -> Self:
|
|
160
|
+
"""Deserialize a Help instance from a mapping.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
src_dict: The source mapping (typically parsed JSON).
|
|
164
|
+
hive_client: The HiveClient used for lazy-loading related objects.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
A populated :class:`Help` instance.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
# Local imports to avoid runtime import cycles; keep but silence pylint.
|
|
171
|
+
from .help_response_segel_nested import ( # pylint: disable=import-outside-toplevel
|
|
172
|
+
HelpResponseSegelNested,
|
|
173
|
+
)
|
|
174
|
+
from .notification_nested import ( # pylint: disable=import-outside-toplevel
|
|
175
|
+
NotificationNested,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
d = dict(src_dict)
|
|
179
|
+
id = d.pop("id")
|
|
180
|
+
|
|
181
|
+
user_id = d.pop("user")
|
|
182
|
+
|
|
183
|
+
def _parse_checker(data: object) -> None | int:
|
|
184
|
+
if data is None:
|
|
185
|
+
return data
|
|
186
|
+
return cast("None | int", data)
|
|
187
|
+
|
|
188
|
+
checker_id = _parse_checker(d.pop("checker"))
|
|
189
|
+
|
|
190
|
+
checker_first_name = d.pop("checker_first_name")
|
|
191
|
+
|
|
192
|
+
checker_last_name = d.pop("checker_last_name")
|
|
193
|
+
|
|
194
|
+
is_subscribed = d.pop("is_subscribed")
|
|
195
|
+
|
|
196
|
+
help_type = HelpTypeEnum(d.pop("help_type"))
|
|
197
|
+
|
|
198
|
+
help_status = HelpStatusEnum(d.pop("help_status"))
|
|
199
|
+
|
|
200
|
+
def _parse_for_exercise(data: object) -> int | None:
|
|
201
|
+
if data is None:
|
|
202
|
+
return data
|
|
203
|
+
try:
|
|
204
|
+
if not isinstance(data, dict):
|
|
205
|
+
raise TypeError
|
|
206
|
+
return cast("int", data["id"])
|
|
207
|
+
except Exception: # pylint: disable=broad-except
|
|
208
|
+
# When the structure is unexpected, treat as missing.
|
|
209
|
+
return None
|
|
210
|
+
|
|
211
|
+
for_exercise_id = _parse_for_exercise(d.pop("for_exercise"))
|
|
212
|
+
|
|
213
|
+
responses = [
|
|
214
|
+
HelpResponseSegelNested.from_dict(
|
|
215
|
+
responses_item_data, hive_client=hive_client
|
|
216
|
+
)
|
|
217
|
+
for responses_item_data in d.pop("responses")
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
notifications: list[NotificationNested] = [
|
|
221
|
+
NotificationNested.from_dict(
|
|
222
|
+
notifications_item_data, hive_client=hive_client
|
|
223
|
+
)
|
|
224
|
+
for notifications_item_data in d.pop("notifications")
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
title = d.pop("title", UNSET)
|
|
228
|
+
|
|
229
|
+
_visibility = d.pop("visibility", UNSET)
|
|
230
|
+
visibility: Unset | VisibilityEnum
|
|
231
|
+
visibility = (
|
|
232
|
+
UNSET if isinstance(_visibility, Unset) else VisibilityEnum(_visibility)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return cls(
|
|
236
|
+
id=id,
|
|
237
|
+
user_id=user_id,
|
|
238
|
+
checker_id=checker_id,
|
|
239
|
+
checker_first_name=checker_first_name,
|
|
240
|
+
checker_last_name=checker_last_name,
|
|
241
|
+
is_subscribed=is_subscribed,
|
|
242
|
+
help_type=help_type,
|
|
243
|
+
help_status=help_status,
|
|
244
|
+
for_exercise_id=for_exercise_id,
|
|
245
|
+
responses=responses,
|
|
246
|
+
notifications=notifications,
|
|
247
|
+
title=title,
|
|
248
|
+
visibility=visibility,
|
|
249
|
+
hive_client=hive_client,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def for_exercise(self) -> Union["Exercise", None]:
|
|
254
|
+
"""Lazily load and return the related Exercise, if any.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
The resolved :class:`Exercise` instance or None when not set.
|
|
258
|
+
"""
|
|
259
|
+
if self.for_exercise_id is None:
|
|
260
|
+
return None
|
|
261
|
+
if self._for_exercise is None:
|
|
262
|
+
self._for_exercise = self.hive_client.get_exercise(self.for_exercise_id)
|
|
263
|
+
return self._for_exercise
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def user(self) -> "User":
|
|
267
|
+
"""User which opened this help request.
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
User: The user instance.
|
|
271
|
+
|
|
272
|
+
"""
|
|
273
|
+
if self._user is None:
|
|
274
|
+
self._user = self.hive_client.get_user(self.user_id)
|
|
275
|
+
return self._user
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Model definition for help responses in the Hive system.
|
|
2
|
+
|
|
3
|
+
Represents a reply to a help request, which may include resolution,
|
|
4
|
+
comments, attachments, and display preferences.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import datetime
|
|
8
|
+
from collections.abc import Mapping
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Self, TypeVar, cast
|
|
10
|
+
|
|
11
|
+
from attrs import define
|
|
12
|
+
from attrs import field
|
|
13
|
+
from dateutil.parser import isoparse
|
|
14
|
+
from .common import UNSET, Unset
|
|
15
|
+
from .core_item import HiveCoreItem
|
|
16
|
+
from .enums.help_response_type_enum import HelpResponseTypeEnum
|
|
17
|
+
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from ...client import HiveClient
|
|
20
|
+
from .user import User
|
|
21
|
+
|
|
22
|
+
T = TypeVar("T", bound="HelpResponse")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@define
|
|
26
|
+
class HelpResponse(HiveCoreItem):
|
|
27
|
+
"""A response to a help request.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
id: Unique identifier for the response.
|
|
31
|
+
user: ID of the responding user.
|
|
32
|
+
date: Timestamp of the response.
|
|
33
|
+
response_type: Type of the response (e.g., Resolve, Open, Comment).
|
|
34
|
+
contents: Optional text content of the response.
|
|
35
|
+
file_name: Optional name of an attached file.
|
|
36
|
+
dear_student: Whether to include a "Dear student" greeting. Default is True.
|
|
37
|
+
hide_checker_name: If True, the name of the checker is hidden.
|
|
38
|
+
segel_only: If True, the response is visible only to staff.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
hive_client: "HiveClient"
|
|
43
|
+
id: int
|
|
44
|
+
user_id: int
|
|
45
|
+
date: datetime.datetime
|
|
46
|
+
response_type: HelpResponseTypeEnum
|
|
47
|
+
contents: None | Unset | str = UNSET
|
|
48
|
+
file_name: Unset | str = UNSET
|
|
49
|
+
dear_student: Unset | bool = True
|
|
50
|
+
hide_checker_name: Unset | bool = UNSET
|
|
51
|
+
segel_only: Unset | bool = UNSET
|
|
52
|
+
|
|
53
|
+
# Lazy-loaded objects
|
|
54
|
+
_user: "User | None" = field(init=False, default=None)
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def user(self) -> "User":
|
|
58
|
+
"""Returns the User object associated with this instance.
|
|
59
|
+
|
|
60
|
+
If the User object has not been retrieved yet,
|
|
61
|
+
it fetches the user using the hive_client and caches it for future calls.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
User: The user associated with this instance.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
if self._user is None:
|
|
68
|
+
self._user = self.hive_client.get_user(self.user_id)
|
|
69
|
+
return self._user
|
|
70
|
+
|
|
71
|
+
def to_dict(self) -> dict[str, Any]:
|
|
72
|
+
contents = UNSET if isinstance(self.contents, Unset) else self.contents
|
|
73
|
+
|
|
74
|
+
field_dict: dict[str, Any] = {
|
|
75
|
+
"id": self.id,
|
|
76
|
+
"user": self.user_id,
|
|
77
|
+
"date": self.date.isoformat(),
|
|
78
|
+
"response_type": self.response_type.value,
|
|
79
|
+
}
|
|
80
|
+
if contents is not UNSET:
|
|
81
|
+
field_dict["contents"] = contents
|
|
82
|
+
if self.file_name is not UNSET:
|
|
83
|
+
field_dict["file_name"] = self.file_name
|
|
84
|
+
if self.dear_student is not UNSET:
|
|
85
|
+
field_dict["dear_student"] = self.dear_student
|
|
86
|
+
if self.hide_checker_name is not UNSET:
|
|
87
|
+
field_dict["hide_checker_name"] = self.hide_checker_name
|
|
88
|
+
if self.segel_only is not UNSET:
|
|
89
|
+
field_dict["segel_only"] = self.segel_only
|
|
90
|
+
|
|
91
|
+
return field_dict
|
|
92
|
+
|
|
93
|
+
@classmethod
|
|
94
|
+
def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
|
|
95
|
+
d = dict(src_dict)
|
|
96
|
+
|
|
97
|
+
def _parse_optional_str(data: object) -> None | Unset | str:
|
|
98
|
+
if data is None or isinstance(data, Unset):
|
|
99
|
+
return data
|
|
100
|
+
return cast("str", data)
|
|
101
|
+
|
|
102
|
+
return cls(
|
|
103
|
+
hive_client=hive_client,
|
|
104
|
+
id=d.pop("id"),
|
|
105
|
+
user_id=d.pop("user"),
|
|
106
|
+
date=isoparse(d.pop("date")),
|
|
107
|
+
response_type=HelpResponseTypeEnum(d.pop("response_type")),
|
|
108
|
+
contents=_parse_optional_str(d.pop("contents", UNSET)),
|
|
109
|
+
file_name=d.pop("file_name", UNSET),
|
|
110
|
+
dear_student=d.pop("dear_student", UNSET),
|
|
111
|
+
hide_checker_name=d.pop("hide_checker_name", UNSET),
|
|
112
|
+
segel_only=d.pop("segel_only", UNSET),
|
|
113
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
""" "This module contains the HelpResponseSegelNested class."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Self, TypeVar, cast
|
|
6
|
+
|
|
7
|
+
from attrs import define
|
|
8
|
+
from dateutil.parser import isoparse
|
|
9
|
+
from .common import UNSET, Unset
|
|
10
|
+
from .enums.help_response_type_enum import HelpResponseTypeEnum
|
|
11
|
+
from .core_item import HiveCoreItem
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ...client import HiveClient
|
|
15
|
+
|
|
16
|
+
T = TypeVar("T", bound="HelpResponseSegelNested")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@define
|
|
20
|
+
class HelpResponseSegelNested(HiveCoreItem):
|
|
21
|
+
"""Attributes:
|
|
22
|
+
id (int):
|
|
23
|
+
user (int):
|
|
24
|
+
date (datetime.datetime):
|
|
25
|
+
response_type (HelpResponseTypeEnum):
|
|
26
|
+
* `Resolve` - Resolve
|
|
27
|
+
* `Open` - Open
|
|
28
|
+
* `Comment` - Comment
|
|
29
|
+
contents (Union[None, Unset, str]):
|
|
30
|
+
file_name (Union[Unset, str]):
|
|
31
|
+
dear_student (Union[Unset, bool]): Default: True.
|
|
32
|
+
hide_checker_name (Union[Unset, bool]):
|
|
33
|
+
segel_only (Union[Unset, bool]):
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
hive_client: "HiveClient"
|
|
38
|
+
id: int
|
|
39
|
+
user: int
|
|
40
|
+
date: datetime.datetime
|
|
41
|
+
response_type: HelpResponseTypeEnum
|
|
42
|
+
contents: None | Unset | str = UNSET
|
|
43
|
+
file_name: Unset | str = UNSET
|
|
44
|
+
dear_student: Unset | bool = True
|
|
45
|
+
hide_checker_name: Unset | bool = UNSET
|
|
46
|
+
segel_only: Unset | bool = UNSET
|
|
47
|
+
|
|
48
|
+
def to_dict(self) -> dict[str, Any]:
|
|
49
|
+
id = self.id
|
|
50
|
+
|
|
51
|
+
user = self.user
|
|
52
|
+
|
|
53
|
+
date = self.date.isoformat()
|
|
54
|
+
|
|
55
|
+
response_type = self.response_type.value
|
|
56
|
+
|
|
57
|
+
contents: None | Unset | str
|
|
58
|
+
contents = UNSET if isinstance(self.contents, Unset) else self.contents
|
|
59
|
+
|
|
60
|
+
file_name = self.file_name
|
|
61
|
+
|
|
62
|
+
dear_student = self.dear_student
|
|
63
|
+
|
|
64
|
+
hide_checker_name = self.hide_checker_name
|
|
65
|
+
|
|
66
|
+
segel_only = self.segel_only
|
|
67
|
+
|
|
68
|
+
field_dict: dict[str, Any] = {}
|
|
69
|
+
field_dict.update(
|
|
70
|
+
{
|
|
71
|
+
"id": id,
|
|
72
|
+
"user": user,
|
|
73
|
+
"date": date,
|
|
74
|
+
"response_type": response_type,
|
|
75
|
+
},
|
|
76
|
+
)
|
|
77
|
+
if contents is not UNSET:
|
|
78
|
+
field_dict["contents"] = contents
|
|
79
|
+
if file_name is not UNSET:
|
|
80
|
+
field_dict["file_name"] = file_name
|
|
81
|
+
if dear_student is not UNSET:
|
|
82
|
+
field_dict["dear_student"] = dear_student
|
|
83
|
+
if hide_checker_name is not UNSET:
|
|
84
|
+
field_dict["hide_checker_name"] = hide_checker_name
|
|
85
|
+
if segel_only is not UNSET:
|
|
86
|
+
field_dict["segel_only"] = segel_only
|
|
87
|
+
|
|
88
|
+
return field_dict
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
|
|
92
|
+
d = dict(src_dict)
|
|
93
|
+
id = d.pop("id")
|
|
94
|
+
|
|
95
|
+
user = d.pop("user")
|
|
96
|
+
|
|
97
|
+
date = isoparse(d.pop("date"))
|
|
98
|
+
|
|
99
|
+
response_type = HelpResponseTypeEnum(d.pop("response_type"))
|
|
100
|
+
|
|
101
|
+
def _parse_contents(data: object) -> None | Unset | str:
|
|
102
|
+
if data is None:
|
|
103
|
+
return data
|
|
104
|
+
if isinstance(data, Unset):
|
|
105
|
+
return data
|
|
106
|
+
return cast("None | Unset | str", data)
|
|
107
|
+
|
|
108
|
+
contents = _parse_contents(d.pop("contents", UNSET))
|
|
109
|
+
|
|
110
|
+
file_name = d.pop("file_name", UNSET)
|
|
111
|
+
|
|
112
|
+
dear_student = d.pop("dear_student", UNSET)
|
|
113
|
+
|
|
114
|
+
hide_checker_name = d.pop("hide_checker_name", UNSET)
|
|
115
|
+
|
|
116
|
+
segel_only = d.pop("segel_only", UNSET)
|
|
117
|
+
|
|
118
|
+
return cls(
|
|
119
|
+
id=id,
|
|
120
|
+
user=user,
|
|
121
|
+
date=date,
|
|
122
|
+
response_type=response_type,
|
|
123
|
+
contents=contents,
|
|
124
|
+
file_name=file_name,
|
|
125
|
+
dear_student=dear_student,
|
|
126
|
+
hide_checker_name=hide_checker_name,
|
|
127
|
+
segel_only=segel_only,
|
|
128
|
+
hive_client=hive_client,
|
|
129
|
+
)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Module model definition for the Hive system.
|
|
2
|
+
|
|
3
|
+
Represents a logical course module within a subject, supporting serialization,
|
|
4
|
+
lazy loading of parent subject, and retrieval of exercises.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from collections.abc import Generator, Mapping
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Self, TypeVar, cast
|
|
9
|
+
|
|
10
|
+
from attrs import define
|
|
11
|
+
from attrs import field
|
|
12
|
+
from .enums.sync_status_enum import SyncStatusEnum
|
|
13
|
+
from .exercise import Exercise
|
|
14
|
+
from .program import HiveCoreItem
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ...client import HiveClient
|
|
18
|
+
from .subject import Subject
|
|
19
|
+
|
|
20
|
+
T = TypeVar("T", bound="Module")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@define
|
|
24
|
+
class Module(HiveCoreItem):
|
|
25
|
+
"""Course Subject Module.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
id: Unique identifier.
|
|
29
|
+
name: Name of the module.
|
|
30
|
+
parent_subject_id: ID of the parent subject.
|
|
31
|
+
order: Order of display within the subject.
|
|
32
|
+
sync_status: Synchronization status.
|
|
33
|
+
sync_message: Optional error or status message.
|
|
34
|
+
parent_program_name: Name of the program the subject belongs to.
|
|
35
|
+
parent_subject_name: Name of the parent subject.
|
|
36
|
+
parent_subject_symbol: Symbol of the parent subject.
|
|
37
|
+
segel_path: Network path accessible to staff.
|
|
38
|
+
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
hive_client: "HiveClient"
|
|
42
|
+
id: int
|
|
43
|
+
name: str
|
|
44
|
+
parent_subject_id: int
|
|
45
|
+
order: str
|
|
46
|
+
sync_status: SyncStatusEnum
|
|
47
|
+
sync_message: None | str
|
|
48
|
+
parent_program_name: str
|
|
49
|
+
parent_subject_name: str
|
|
50
|
+
parent_subject_symbol: str
|
|
51
|
+
segel_path: str
|
|
52
|
+
|
|
53
|
+
_parent_subject: "Subject | None" = field(init=False, default=None)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def parent_subject(self) -> "Subject":
|
|
57
|
+
"""Lazily load and return the parent subject."""
|
|
58
|
+
if self._parent_subject is None:
|
|
59
|
+
self._parent_subject = self.hive_client.get_subject(self.parent_subject_id)
|
|
60
|
+
return self._parent_subject
|
|
61
|
+
|
|
62
|
+
def to_dict(self) -> dict[str, Any]:
|
|
63
|
+
"""Serialize the module to a dictionary."""
|
|
64
|
+
return {
|
|
65
|
+
"id": self.id,
|
|
66
|
+
"name": self.name,
|
|
67
|
+
"parent_subject": self.parent_subject_id,
|
|
68
|
+
"order": self.order,
|
|
69
|
+
"sync_status": self.sync_status.value,
|
|
70
|
+
"sync_message": self.sync_message,
|
|
71
|
+
"parent_program_name": self.parent_program_name,
|
|
72
|
+
"parent_subject_name": self.parent_subject_name,
|
|
73
|
+
"parent_subject_symbol": self.parent_subject_symbol,
|
|
74
|
+
"segel_path": self.segel_path,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
|
|
79
|
+
"""Deserialize a module from a dictionary."""
|
|
80
|
+
d = dict(src_dict)
|
|
81
|
+
return cls(
|
|
82
|
+
hive_client=hive_client,
|
|
83
|
+
id=d["id"],
|
|
84
|
+
name=d["name"],
|
|
85
|
+
parent_subject_id=d["parent_subject"],
|
|
86
|
+
order=d["order"],
|
|
87
|
+
sync_status=SyncStatusEnum(d["sync_status"]),
|
|
88
|
+
sync_message=cast("None | str", d["sync_message"]),
|
|
89
|
+
parent_program_name=d["parent_program_name"],
|
|
90
|
+
parent_subject_name=d["parent_subject_name"],
|
|
91
|
+
parent_subject_symbol=d["parent_subject_symbol"],
|
|
92
|
+
segel_path=d["segel_path"],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def __eq__(self, value: object) -> bool:
|
|
96
|
+
if not isinstance(value, Module):
|
|
97
|
+
return False
|
|
98
|
+
return self.id == value.id and self.parent_subject == value.parent_subject
|
|
99
|
+
|
|
100
|
+
def __lt__(self, value: object) -> bool:
|
|
101
|
+
if not isinstance(value, Module):
|
|
102
|
+
return NotImplemented
|
|
103
|
+
return self.order < value.order
|
|
104
|
+
|
|
105
|
+
def get_exercises(self) -> Generator[Exercise]:
|
|
106
|
+
"""Fetch all exercises within this module."""
|
|
107
|
+
return self.hive_client.get_exercises(parent_module__id=self.id)
|
|
108
|
+
|
|
109
|
+
def get_exercise(self, exercise_name: str) -> Exercise:
|
|
110
|
+
"""Fetch a specific exercise by name within this module."""
|
|
111
|
+
exercises = list(
|
|
112
|
+
self.hive_client.get_exercises(
|
|
113
|
+
parent_module__id=self.id,
|
|
114
|
+
exercise_name=exercise_name,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if len(exercises) == 0:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Exercise '{exercise_name}' not found in module '{self.name}'"
|
|
121
|
+
)
|
|
122
|
+
if len(exercises) > 1:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"Multiple exercises named '{exercise_name}' found in module '{self.name}'"
|
|
125
|
+
)
|
|
126
|
+
return exercises[0]
|
|
127
|
+
|
|
128
|
+
def __iter__(self) -> Generator["Exercise", None, None]:
|
|
129
|
+
"""Allow iteration over this Module to yield its exercises."""
|
|
130
|
+
yield from self.get_exercises()
|
|
131
|
+
|
|
132
|
+
def __hash__(self) -> int:
|
|
133
|
+
return hash(
|
|
134
|
+
(
|
|
135
|
+
self.id,
|
|
136
|
+
self.parent_subject_id,
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
ModuleLike = TypeVar("ModuleLike", Module, int)
|