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.
Files changed (55) hide show
  1. pyhive/__init__.py +13 -0
  2. pyhive/cli/__init__.py +0 -0
  3. pyhive/cli/main.py +30 -0
  4. pyhive/client.py +570 -0
  5. pyhive/src/__init__.py +0 -0
  6. pyhive/src/_generated_versions.py +3 -0
  7. pyhive/src/api_versions.py +6 -0
  8. pyhive/src/authenticated_hive_client.py +250 -0
  9. pyhive/src/types/__init__.py +0 -0
  10. pyhive/src/types/assignment.py +210 -0
  11. pyhive/src/types/assignment_response.py +205 -0
  12. pyhive/src/types/assignment_response_content.py +131 -0
  13. pyhive/src/types/autocheck_status.py +94 -0
  14. pyhive/src/types/class_.py +113 -0
  15. pyhive/src/types/common.py +56 -0
  16. pyhive/src/types/core_item.py +22 -0
  17. pyhive/src/types/enums/__init__.py +0 -0
  18. pyhive/src/types/enums/action_enum.py +18 -0
  19. pyhive/src/types/enums/assignment_response_type_enum.py +17 -0
  20. pyhive/src/types/enums/assignment_status_enum.py +17 -0
  21. pyhive/src/types/enums/class_type_enum.py +13 -0
  22. pyhive/src/types/enums/clearance_enum.py +16 -0
  23. pyhive/src/types/enums/event_type_enum.py +14 -0
  24. pyhive/src/types/enums/exercise_patbas_enum.py +15 -0
  25. pyhive/src/types/enums/exercise_preview_types.py +15 -0
  26. pyhive/src/types/enums/form_field_type_enum.py +15 -0
  27. pyhive/src/types/enums/gender_enum.py +14 -0
  28. pyhive/src/types/enums/help_response_type_enum.py +14 -0
  29. pyhive/src/types/enums/help_status_enum.py +13 -0
  30. pyhive/src/types/enums/help_type_enum.py +18 -0
  31. pyhive/src/types/enums/queue_rule_enum.py +15 -0
  32. pyhive/src/types/enums/status_enum.py +21 -0
  33. pyhive/src/types/enums/sync_status_enum.py +15 -0
  34. pyhive/src/types/enums/visibility_enum.py +14 -0
  35. pyhive/src/types/event.py +140 -0
  36. pyhive/src/types/event_attendees_type_0_item.py +69 -0
  37. pyhive/src/types/event_color.py +63 -0
  38. pyhive/src/types/exercise.py +216 -0
  39. pyhive/src/types/form_field.py +152 -0
  40. pyhive/src/types/help_.py +275 -0
  41. pyhive/src/types/help_response.py +113 -0
  42. pyhive/src/types/help_response_segel_nested.py +129 -0
  43. pyhive/src/types/module.py +141 -0
  44. pyhive/src/types/notification_nested.py +80 -0
  45. pyhive/src/types/program.py +180 -0
  46. pyhive/src/types/queue.py +150 -0
  47. pyhive/src/types/queue_item.py +88 -0
  48. pyhive/src/types/subject.py +156 -0
  49. pyhive/src/types/tag.py +62 -0
  50. pyhive/src/types/user.py +450 -0
  51. pyhive/types.py +23 -0
  52. pyhivelms-1.0.0.dist-info/METADATA +156 -0
  53. pyhivelms-1.0.0.dist-info/RECORD +55 -0
  54. pyhivelms-1.0.0.dist-info/WHEEL +4 -0
  55. pyhivelms-1.0.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,80 @@
1
+ """Model definition for a nested notification in the Hive system.
2
+
3
+ Represents a lightweight notification object, optionally referencing a user and comment.
4
+ """
5
+
6
+ from collections.abc import Mapping
7
+ from typing import TYPE_CHECKING, Any, Self, TypeVar, cast
8
+
9
+ from attrs import define
10
+ from attrs import field
11
+ from .common import UNSET, Unset
12
+ from .core_item import HiveCoreItem
13
+
14
+ if TYPE_CHECKING:
15
+ from ...client import HiveClient
16
+ from .user import User
17
+
18
+ T = TypeVar("T", bound="NotificationNested")
19
+
20
+
21
+ @define
22
+ class NotificationNested(HiveCoreItem):
23
+ """A lightweight notification model.
24
+
25
+ Attributes:
26
+ id: Unique identifier of the notification.
27
+ from_user_id: Optional user ID that sent the notification.
28
+ comment: Optional comment text.
29
+
30
+ """
31
+
32
+ hive_client: "HiveClient"
33
+ id: int
34
+ from_user_id: None | Unset | int = UNSET
35
+ comment: Unset | str = UNSET
36
+
37
+ _from_user: "User | None" = field(init=False, default=None)
38
+
39
+ @property
40
+ def from_user(self) -> "User | None":
41
+ """Lazily loads and returns the `User` who sent the notification.
42
+
43
+ Returns:
44
+ A `User` instance or `None` if not available.
45
+
46
+ """
47
+ if self._from_user is None and not isinstance(self.from_user_id, Unset) and self.from_user_id is not None:
48
+ self._from_user = self.hive_client.get_user(self.from_user_id)
49
+ return self._from_user
50
+
51
+ def to_dict(self) -> dict[str, Any]:
52
+ """Serialize the notification to a dictionary."""
53
+ field_dict: dict[str, Any] = {
54
+ "id": self.id,
55
+ }
56
+
57
+ if not isinstance(self.from_user_id, Unset):
58
+ field_dict["from_user"] = self.from_user_id
59
+
60
+ if not isinstance(self.comment, Unset):
61
+ field_dict["comment"] = self.comment
62
+
63
+ return field_dict
64
+
65
+ @classmethod
66
+ def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
67
+ """Deserialize the notification from a dictionary."""
68
+ d = dict(src_dict)
69
+
70
+ def _parse_from_user(data: object) -> None | Unset | int:
71
+ if data is None or isinstance(data, Unset):
72
+ return data
73
+ return cast("None | Unset | int", data)
74
+
75
+ return cls(
76
+ hive_client=hive_client,
77
+ id=d.pop("id"),
78
+ from_user_id=_parse_from_user(d.pop("from_user", UNSET)),
79
+ comment=d.pop("comment", UNSET),
80
+ )
@@ -0,0 +1,180 @@
1
+ """Model definition for the Hive Program entity.
2
+
3
+ Represents an educational program, including checker configuration,
4
+ sync status, and automatic handling flags.
5
+ """
6
+
7
+ from collections.abc import Generator, Mapping
8
+ from typing import TYPE_CHECKING, Any, Self, TypeVar
9
+
10
+ from attrs import define
11
+ from attrs import field
12
+ from .common import UNSET, Unset
13
+ from .core_item import HiveCoreItem
14
+ from .enums.sync_status_enum import SyncStatusEnum
15
+ from .subject import Subject
16
+
17
+ if TYPE_CHECKING:
18
+ from ...client import HiveClient
19
+ from .class_ import Class
20
+ from .user import User
21
+
22
+ T = TypeVar("T", bound="Program")
23
+
24
+
25
+ @define
26
+ class Program(HiveCoreItem):
27
+ """Course Program entity.
28
+
29
+ Attributes:
30
+ id: Unique identifier.
31
+ name: Display name of the program.
32
+ checker_id: User ID of the assigned checker.
33
+ sync_status: Sync status (e.g., Normal, Creating).
34
+ sync_message: Optional sync diagnostic message.
35
+ default_class_id: Optional class ID used as default.
36
+ auto_toilet: Auto-toilet generation enabled.
37
+ hanich_raise_hand: Whether hanich can raise hand.
38
+ auto_schedule: Enable auto-scheduling.
39
+ auto_room: Enable automatic room assignments.
40
+ hanich_day_only: Restrict hanich to day-only usage.
41
+ hanich_work_name: Enable work name customization.
42
+ auto_toilet_count: Number of auto toilets to assign.
43
+ hanich_classes_only: Restrict hanich to classes only.
44
+ hanich_schedule: Whether hanich gets scheduled.
45
+
46
+ """
47
+
48
+ hive_client: "HiveClient"
49
+ id: int
50
+ name: str
51
+ checker_id: int
52
+ sync_status: SyncStatusEnum
53
+ sync_message: None | str
54
+ default_class_id: None | Unset | int
55
+
56
+ auto_toilet: Unset | bool = UNSET
57
+ hanich_raise_hand: Unset | bool = UNSET
58
+ auto_schedule: Unset | bool = UNSET
59
+ auto_room: Unset | bool = UNSET
60
+ hanich_day_only: Unset | bool = UNSET
61
+ hanich_work_name: Unset | bool = UNSET
62
+ auto_toilet_count: Unset | int = UNSET
63
+ hanich_classes_only: Unset | bool = UNSET
64
+ hanich_schedule: Unset | bool = UNSET
65
+
66
+ _checker: "User | None" = field(init=False, default=None)
67
+ _default_class: "Class | None" = field(init=False, default=None)
68
+
69
+ def __str__(self) -> str:
70
+ return f"<Program[{self.id}] {self.name}>"
71
+
72
+ @property
73
+ def checker(self) -> "User":
74
+ """Lazily loads and returns the checker (staff member)."""
75
+ if self._checker is None:
76
+ self._checker = self.hive_client.get_user(self.checker_id)
77
+ return self._checker
78
+
79
+ @property
80
+ def default_class(self) -> "Class | None":
81
+ """Lazily loads the default class, if set."""
82
+ if (
83
+ self._default_class is None
84
+ and not isinstance(self.default_class_id, Unset)
85
+ and self.default_class_id is not None
86
+ ):
87
+ self._default_class = self.hive_client.get_class(self.default_class_id)
88
+ return self._default_class
89
+
90
+ def get_subjects(self) -> Generator[Subject]:
91
+ """Returns all subjects belonging to this program."""
92
+ return self.hive_client.get_subjects(parent_program__id__in=[self.id])
93
+
94
+ def to_dict(self) -> dict[str, Any]:
95
+ field_dict: dict[str, Any] = {
96
+ "id": self.id,
97
+ "name": self.name,
98
+ "checker": self.checker_id,
99
+ "sync_status": self.sync_status.value,
100
+ "sync_message": self.sync_message,
101
+ }
102
+
103
+ if not isinstance(self.default_class_id, Unset):
104
+ field_dict["default_class"] = self.default_class_id
105
+ if not isinstance(self.auto_toilet, Unset):
106
+ field_dict["auto_toilet"] = self.auto_toilet
107
+ if not isinstance(self.hanich_raise_hand, Unset):
108
+ field_dict["hanich_raise_hand"] = self.hanich_raise_hand
109
+ if not isinstance(self.auto_schedule, Unset):
110
+ field_dict["auto_schedule"] = self.auto_schedule
111
+ if not isinstance(self.auto_room, Unset):
112
+ field_dict["auto_room"] = self.auto_room
113
+ if not isinstance(self.hanich_day_only, Unset):
114
+ field_dict["hanich_day_only"] = self.hanich_day_only
115
+ if not isinstance(self.hanich_work_name, Unset):
116
+ field_dict["hanich_work_name"] = self.hanich_work_name
117
+ if not isinstance(self.auto_toilet_count, Unset):
118
+ field_dict["auto_toilet_count"] = self.auto_toilet_count
119
+ if not isinstance(self.hanich_classes_only, Unset):
120
+ field_dict["hanich_classes_only"] = self.hanich_classes_only
121
+ if not isinstance(self.hanich_schedule, Unset):
122
+ field_dict["hanich_schedule"] = self.hanich_schedule
123
+
124
+ return field_dict
125
+
126
+ @classmethod
127
+ def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
128
+ """Create an instance of the class from a dictionary representation.
129
+
130
+ Args:
131
+ src_dict (Mapping[str, Any]): The source dictionary containing the data to populate the instance.
132
+ hive_client (HiveClient): An instance of HiveClient to associate with the created object.
133
+
134
+ Returns:
135
+ Self: An instance of the class populated with data from src_dict.
136
+
137
+ Notes:
138
+ - Handles optional and unset fields using the _parse_optional helper.
139
+ - Converts the 'sync_status' field to a SyncStatusEnum.
140
+ - Pops fields from the dictionary to avoid duplication.
141
+
142
+ """
143
+ d = dict(src_dict)
144
+
145
+ def _parse_optional(data: object) -> Any:
146
+ if data is None or isinstance(data, Unset):
147
+ return data
148
+ return data
149
+
150
+ return cls(
151
+ id=d.pop("id"),
152
+ name=d.pop("name"),
153
+ checker_id=d.pop("checker"),
154
+ sync_status=SyncStatusEnum(d.pop("sync_status")),
155
+ sync_message=_parse_optional(d.pop("sync_message")),
156
+ default_class_id=_parse_optional(d.pop("default_class", UNSET)),
157
+ auto_toilet=d.pop("auto_toilet", UNSET),
158
+ hanich_raise_hand=d.pop("hanich_raise_hand", UNSET),
159
+ auto_schedule=d.pop("auto_schedule", UNSET),
160
+ auto_room=d.pop("auto_room", UNSET),
161
+ hanich_day_only=d.pop("hanich_day_only", UNSET),
162
+ hanich_work_name=d.pop("hanich_work_name", UNSET),
163
+ auto_toilet_count=d.pop("auto_toilet_count", UNSET),
164
+ hanich_classes_only=d.pop("hanich_classes_only", UNSET),
165
+ hanich_schedule=d.pop("hanich_schedule", UNSET),
166
+ hive_client=hive_client,
167
+ )
168
+
169
+ def __eq__(self, value: object) -> bool:
170
+ if not isinstance(value, Program):
171
+ return False
172
+ return (
173
+ self.id == value.id
174
+ and self.checker_id == value.checker_id
175
+ and self.name == value.name
176
+ )
177
+
178
+ def __iter__(self) -> Generator["Subject", None, None]:
179
+ """Allow iteration over this Program to yield its subjects."""
180
+ yield from self.get_subjects()
@@ -0,0 +1,150 @@
1
+ """Queue model for the Hive API (auto-generated).
2
+
3
+ This module contains the :class:`Queue` dataclass which represents a
4
+ queue entry returned by the Hive API. The class provides simple
5
+ serialization helpers and lazily-resolved relationship properties.
6
+ """
7
+
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 .common import UNSET, Unset
14
+ from .core_item import HiveCoreItem
15
+
16
+ if TYPE_CHECKING:
17
+ from ...client import HiveClient
18
+ from .module import Module
19
+ from .program import Program
20
+ from .subject import Subject
21
+ from .user import User
22
+
23
+ T = TypeVar("T", bound="Queue")
24
+
25
+
26
+ @define
27
+ class Queue(HiveCoreItem):
28
+ """Queue model representing a student/program/module queue entry.
29
+
30
+ Attributes mirror the API JSON keys; relationship properties (``user``,
31
+ ``module``, ``subject``, ``program``) lazily load the referenced
32
+ objects using the supplied ``hive_client``.
33
+ """
34
+
35
+ hive_client: "HiveClient"
36
+ id: int
37
+ name: str
38
+ user_name: None | str
39
+ subject_id: None | int
40
+ subject_name: None | str
41
+ subject_color: None | str
42
+ subject_symbol: None | str
43
+ module_name: None | str
44
+ module_order: None | str
45
+ program_id: int
46
+ program_name: str
47
+ description: None | Unset | str = UNSET
48
+
49
+ module_id: None | Unset | int = UNSET
50
+ user_id: None | Unset | int = UNSET
51
+
52
+ _user: "User | None" = field(init=False, default=None)
53
+ _module: "Module | None" = field(init=False, default=None)
54
+ _subject: "Subject | None" = field(init=False, default=None)
55
+ _program: "Program | None" = field(init=False, default=None)
56
+
57
+ def to_dict(self) -> dict[str, Any]:
58
+ """Return a JSON-serializable dictionary of this Queue.
59
+
60
+ The returned mapping only includes optional keys when they are not
61
+ :data:`UNSET`.
62
+ """
63
+ return {
64
+ "id": self.id,
65
+ "name": self.name,
66
+ "user_id": self.user_id,
67
+ "user_name": self.user_name,
68
+ "subject_id": self.subject_id,
69
+ "subject_name": self.subject_name,
70
+ "subject_color": self.subject_color,
71
+ "subject_symbol": self.subject_symbol,
72
+ "module_id": self.module_id,
73
+ "module_name": self.module_name,
74
+ "module_order": self.module_order,
75
+ "program_id": self.program_id,
76
+ "program_name": self.program_name,
77
+ **(
78
+ {"description": self.description}
79
+ if self.description is not UNSET
80
+ else {}
81
+ ),
82
+ **({"module": self.module} if self.module is not UNSET else {}),
83
+ **({"user": self.user} if self.user_id is not UNSET else {}),
84
+ }
85
+
86
+ @classmethod
87
+ def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
88
+ """Create a :class:`Queue` instance from a mapping (typically parsed JSON).
89
+
90
+ Args:
91
+ src_dict: Mapping with keys matching the API response.
92
+ hive_client: Hive client used to lazily resolve relationships.
93
+
94
+ Returns:
95
+ A populated :class:`Queue` instance.
96
+ """
97
+ d = dict(src_dict)
98
+
99
+ def _optional(data: object) -> Any:
100
+ return data if not isinstance(data, Unset) else UNSET
101
+
102
+ return cls(
103
+ hive_client=hive_client,
104
+ id=d.pop("id"),
105
+ name=d.pop("name"),
106
+ user_id=cast("None | int", d.pop("user_id")),
107
+ user_name=cast("None | str", d.pop("user_name")),
108
+ subject_id=cast("None | int", d.pop("subject_id")),
109
+ subject_name=cast("None | str", d.pop("subject_name")),
110
+ subject_color=cast("None | str", d.pop("subject_color")),
111
+ subject_symbol=cast("None | str", d.pop("subject_symbol")),
112
+ module_id=cast("None | int", d.pop("module_id")),
113
+ module_name=cast("None | str", d.pop("module_name")),
114
+ module_order=cast("None | str", d.pop("module_order")),
115
+ program_id=d.pop("program_id"),
116
+ program_name=d.pop("program_name"),
117
+ description=_optional(d.pop("description", UNSET)),
118
+ )
119
+
120
+ @property
121
+ def user(self) -> "User | None":
122
+ """Lazily return the :class:`User` associated with this queue entry.
123
+
124
+ Returns None when no user_id is set.
125
+ """
126
+ if self._user is None and isinstance(self.user_id, int):
127
+ self._user = self.hive_client.get_user(self.user_id)
128
+ return self._user
129
+
130
+ @property
131
+ def module(self) -> "Module | None":
132
+ """Lazily return the :class:`Module` referenced by this queue entry.
133
+
134
+ Returns None when no module_id is present.
135
+ """
136
+ if self._module is None and isinstance(self.module_id, int):
137
+ self._module = self.hive_client.get_module(self.module_id)
138
+ return self._module
139
+
140
+ @property
141
+ def subject(self) -> "Subject | None":
142
+ """Return the resolved :class:`Subject` or None if not available."""
143
+ if isinstance(self.subject_id, int):
144
+ return self.hive_client.get_subject(self.subject_id)
145
+ return None
146
+
147
+ @property
148
+ def program(self) -> "Program":
149
+ """Return the resolved :class:`Program` for this queue entry."""
150
+ return self.hive_client.get_program(self.program_id)
@@ -0,0 +1,88 @@
1
+ """A single item in a learning queue, either referencing an exercise or another queue."""
2
+
3
+ from collections.abc import Mapping
4
+ from typing import TYPE_CHECKING, Any, Self, TypeVar, Union
5
+
6
+ from attrs import define
7
+ from .common import UNSET, Unset
8
+ from .enums.queue_rule_enum import QueueRuleEnum
9
+ from .core_item import HiveCoreItem
10
+
11
+ if TYPE_CHECKING:
12
+ from ...client import HiveClient
13
+ from .exercise import Exercise
14
+ from .queue import Queue
15
+
16
+ T = TypeVar("T", bound="QueueItem")
17
+
18
+
19
+ @define
20
+ class QueueItem(HiveCoreItem):
21
+ """A single item in a learning queue, either referencing an exercise or another queue."""
22
+
23
+ hive_client: "HiveClient"
24
+ id: int
25
+ order: int
26
+ exercise: Union["Exercise", None]
27
+ nested_queue: Union["Queue", None]
28
+ queue_rule: QueueRuleEnum
29
+ enabled: bool
30
+ continue_on_redo: Unset | bool = UNSET
31
+
32
+ def to_dict(self) -> dict[str, Any]:
33
+ from .exercise import ( # pylint: disable=import-outside-toplevel
34
+ Exercise,
35
+ )
36
+ from .queue import Queue # pylint: disable=import-outside-toplevel
37
+
38
+ return {
39
+ "id": self.id,
40
+ "order": self.order,
41
+ "exercise": (
42
+ self.exercise.to_dict()
43
+ if isinstance(self.exercise, Exercise)
44
+ else self.exercise
45
+ ),
46
+ "nested_queue": (
47
+ self.nested_queue.to_dict()
48
+ if isinstance(self.nested_queue, Queue)
49
+ else self.nested_queue
50
+ ),
51
+ "queue_rule": self.queue_rule.value,
52
+ "enabled": self.enabled,
53
+ **(
54
+ {"continue_on_redo": self.continue_on_redo}
55
+ if self.continue_on_redo is not UNSET
56
+ else {}
57
+ ),
58
+ }
59
+
60
+ @classmethod
61
+ def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
62
+ from .exercise import ( # pylint: disable=import-outside-toplevel
63
+ Exercise,
64
+ )
65
+ from .queue import Queue # pylint: disable=import-outside-toplevel
66
+
67
+ d = dict(src_dict)
68
+
69
+ def _parse_exercise(data: dict[str, Any] | None) -> Union["Exercise", None]:
70
+ if data is None:
71
+ return data
72
+ return Exercise.from_dict(data, hive_client=hive_client)
73
+
74
+ def _parse_nested_queue(data: dict[str, Any] | None) -> Union["Queue", None]:
75
+ if data is None:
76
+ return data
77
+ return Queue.from_dict(data, hive_client=hive_client)
78
+
79
+ return cls(
80
+ hive_client=hive_client,
81
+ id=d.pop("id"),
82
+ order=d.pop("order"),
83
+ exercise=_parse_exercise(d.pop("exercise")),
84
+ nested_queue=_parse_nested_queue(d.pop("nested_queue")),
85
+ queue_rule=QueueRuleEnum(d.pop("queue_rule")),
86
+ enabled=d.pop("enabled"),
87
+ continue_on_redo=d.pop("continue_on_redo", UNSET),
88
+ )
@@ -0,0 +1,156 @@
1
+ """Defines the Subject type and related functionality for the Hive API Python bindings."""
2
+
3
+ from collections.abc import Generator, Mapping
4
+ from typing import TYPE_CHECKING, Any, Self, TypeVar, cast
5
+
6
+ from attrs import define, field
7
+ from .core_item import HiveCoreItem
8
+ from .enums.sync_status_enum import SyncStatusEnum
9
+
10
+ if TYPE_CHECKING:
11
+ from ...client import HiveClient
12
+ from .module import Module
13
+ from .program import Program
14
+
15
+ T = TypeVar("T", bound="Subject")
16
+
17
+
18
+ @define
19
+ class Subject(HiveCoreItem):
20
+ """Represents a Subject in the Hive system.
21
+
22
+ Attributes:
23
+ hive_client (HiveClient): Reference to the Hive API client.
24
+ id (int): Subject ID.
25
+ symbol (str): Subject symbol.
26
+ parent_program_id (int): ID of the parent Program.
27
+ color (str): Subject color (hex code or name).
28
+ name (str): Display name.
29
+ parent_program_name (str): Name of the parent Program.
30
+ sync_status (SyncStatusEnum): Status of subject synchronization.
31
+ sync_message (str | None): Optional sync error or status message.
32
+ segel_path (str): Staff-accessible path on shared drive.
33
+
34
+ """
35
+
36
+ hive_client: "HiveClient"
37
+ id: int
38
+ symbol: str
39
+ parent_program_id: int
40
+ color: str
41
+ name: str
42
+ parent_program_name: str
43
+ sync_status: SyncStatusEnum
44
+ sync_message: str | None
45
+ segel_path: str
46
+ _parent_program: "Program | None" = field(init=False, default=None)
47
+
48
+ def to_dict(self) -> dict[str, Any]:
49
+ """Serialize the Subject to a dictionary."""
50
+ return {
51
+ "id": self.id,
52
+ "symbol": self.symbol,
53
+ "parent_program": self.parent_program_id,
54
+ "color": self.color,
55
+ "name": self.name,
56
+ "parent_program_name": self.parent_program_name,
57
+ "sync_status": self.sync_status.value,
58
+ "sync_message": self.sync_message,
59
+ "segel_path": self.segel_path,
60
+ }
61
+
62
+ @classmethod
63
+ def from_dict(cls, src_dict: Mapping[str, Any], hive_client: "HiveClient") -> Self:
64
+ """Deserialize a Subject from a dictionary.
65
+
66
+ Args:
67
+ src_dict: A dictionary containing Subject data.
68
+ hive_client: An instance of HiveClient.
69
+
70
+ Returns:
71
+ A Subject instance.
72
+
73
+ """
74
+ return cls(
75
+ hive_client=hive_client,
76
+ id=src_dict["id"],
77
+ symbol=src_dict["symbol"],
78
+ parent_program_id=src_dict["parent_program"],
79
+ color=src_dict["color"],
80
+ name=src_dict["name"],
81
+ parent_program_name=src_dict["parent_program_name"],
82
+ sync_status=SyncStatusEnum(src_dict["sync_status"]),
83
+ sync_message=cast("str | None", src_dict.get("sync_message")),
84
+ segel_path=src_dict["segel_path"],
85
+ )
86
+
87
+ @property
88
+ def parent_program(self) -> "Program":
89
+ """Lazily load and return the parent Program.
90
+
91
+ Returns:
92
+ Program: The parent program instance.
93
+
94
+ """
95
+ if self._parent_program is None:
96
+ self._parent_program = self.hive_client.get_program(self.parent_program_id)
97
+ return self._parent_program
98
+
99
+ def get_modules(self) -> Generator["Module"]:
100
+ """Retrieve all modules associated with this subject.
101
+
102
+ Returns:
103
+ Generator[Module]: Generator of Module instances.
104
+
105
+ """
106
+ return self.hive_client.get_modules(parent_subject__id=self.id)
107
+
108
+ def get_module(self, module_name: str) -> "Module":
109
+ """Retrieve a specific module by name within this subject.
110
+
111
+ Args:
112
+ module_name (str): The name of the module to retrieve.
113
+
114
+ Returns:
115
+ Module: The Module instance if found.
116
+
117
+ """
118
+ modules = list(
119
+ self.hive_client.get_modules(
120
+ parent_subject__id=self.id, module_name=module_name
121
+ )
122
+ )
123
+ if len(modules) == 0:
124
+ raise ValueError(
125
+ f"Module '{module_name}' not found in subject '{self.name}'"
126
+ )
127
+ if len(modules) > 1:
128
+ raise ValueError(
129
+ f"Multiple modules named '{module_name}' found in subject '{self.name}'"
130
+ )
131
+ return modules[0]
132
+
133
+ def __eq__(self, value: object) -> bool:
134
+ if not isinstance(value, Subject):
135
+ return False
136
+ return self.id == value.id and self.parent_program == value.parent_program
137
+
138
+ def __lt__(self, value: object) -> bool:
139
+ if not isinstance(value, Subject):
140
+ return NotImplemented
141
+ return self.symbol < value.symbol
142
+
143
+ def __iter__(self) -> Generator["Module", None, None]:
144
+ """Allow iteration over this Subject to yield its modules."""
145
+ yield from self.get_modules()
146
+
147
+ def __hash__(self) -> int:
148
+ return hash(
149
+ (
150
+ self.id,
151
+ self.parent_program_id,
152
+ )
153
+ )
154
+
155
+
156
+ SubjectLike = TypeVar("SubjectLike", Subject, int)