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
pyhive/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Public package for the pyhive distribution.
|
|
2
|
+
|
|
3
|
+
Expose the public convenience symbol `HiveClient` at package level so users
|
|
4
|
+
can do `from pyhive import HiveClient`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
# Import the implementation from the `pyhive` package (implementation
|
|
10
|
+
# lives there) and expose the client at package level.
|
|
11
|
+
from pyhive.client import HiveClient # re-export
|
|
12
|
+
|
|
13
|
+
__all__ = ["HiveClient"]
|
pyhive/cli/__init__.py
ADDED
|
File without changes
|
pyhive/cli/main.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""PyHive CLI entry point"""
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
from pyhive.src._generated_versions import SUPPORTED_API_VERSIONS
|
|
5
|
+
|
|
6
|
+
app = typer.Typer(help="PyHive CLI")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.command()
|
|
10
|
+
def versions():
|
|
11
|
+
"""Show supported Hive versions"""
|
|
12
|
+
print("Supported Hive Versions:")
|
|
13
|
+
for v in SUPPORTED_API_VERSIONS:
|
|
14
|
+
print(v)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command()
|
|
18
|
+
def versions2():
|
|
19
|
+
"""Show supported API versions"""
|
|
20
|
+
for v in SUPPORTED_API_VERSIONS:
|
|
21
|
+
print(v)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def main():
|
|
25
|
+
"""PyHive CLI main entry point"""
|
|
26
|
+
app()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
main()
|
pyhive/client.py
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
"""High-level Hive API client.
|
|
2
|
+
|
|
3
|
+
This module provides ``HiveClient``, a small, synchronous authenticated
|
|
4
|
+
client for the Hive service. It exposes convenience methods that return
|
|
5
|
+
typed model objects from :mod:`src.types` and generator-based list
|
|
6
|
+
endpoints for memory-efficient iteration.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import re
|
|
10
|
+
from functools import lru_cache
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Generator, Optional, Sequence, TypeVar
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
from .src.authenticated_hive_client import _AuthenticatedHiveClient
|
|
16
|
+
from .src.types.assignment import Assignment
|
|
17
|
+
from .src.types.assignment_response import AssignmentResponse
|
|
18
|
+
from .src.types.class_ import Class
|
|
19
|
+
from .src.types.enums.class_type_enum import ClassTypeEnum
|
|
20
|
+
from .src.types.exercise import Exercise
|
|
21
|
+
from .src.types.form_field import FormField
|
|
22
|
+
from .src.types.module import Module
|
|
23
|
+
from .src.types.program import Program
|
|
24
|
+
from .src.types.queue import Queue
|
|
25
|
+
from .src.types.subject import Subject
|
|
26
|
+
from .src.types.user import User
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from .src.types.assignment import AssignmentLike
|
|
30
|
+
from .src.types.core_item import HiveCoreItem
|
|
31
|
+
from .src.types.exercise import ExerciseLike
|
|
32
|
+
from .src.types.module import ModuleLike
|
|
33
|
+
from .src.types.subject import SubjectLike
|
|
34
|
+
|
|
35
|
+
CoreItemTypeT = TypeVar("CoreItemTypeT", bound="HiveCoreItem")
|
|
36
|
+
|
|
37
|
+
ItemOrIdT = TypeVar("ItemOrIdT", bound="HiveCoreItem | int")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def resolve_item_or_id(
|
|
41
|
+
item_or_id: ItemOrIdT | None,
|
|
42
|
+
) -> int | None:
|
|
43
|
+
"""Resolve a HiveCoreItem or int to an int ID."""
|
|
44
|
+
from .src.types.core_item import (
|
|
45
|
+
HiveCoreItem,
|
|
46
|
+
) # pylint: disable=import-outside-toplevel
|
|
47
|
+
|
|
48
|
+
if item_or_id is None:
|
|
49
|
+
return None
|
|
50
|
+
if not isinstance(item_or_id, (HiveCoreItem, int)):
|
|
51
|
+
raise TypeError(
|
|
52
|
+
f"Expected HiveCoreItem or int, got {type(item_or_id).__name__}"
|
|
53
|
+
)
|
|
54
|
+
return item_or_id.id if isinstance(item_or_id, HiveCoreItem) else item_or_id
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class HiveClient(_AuthenticatedHiveClient): # pylint: disable=too-many-public-methods
|
|
58
|
+
"""HTTP client for accessing Hive API.
|
|
59
|
+
|
|
60
|
+
The client is used as a context manager and provides typed helpers for
|
|
61
|
+
common Hive resources (programs, subjects, modules, exercises, users,
|
|
62
|
+
classes, and form fields).
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __enter__(self) -> "HiveClient":
|
|
66
|
+
"""Enter context manager and return this client instance.
|
|
67
|
+
|
|
68
|
+
This delegates to the base class context manager which manages the
|
|
69
|
+
underlying :class:`httpx.Client` session.
|
|
70
|
+
"""
|
|
71
|
+
super().__enter__()
|
|
72
|
+
return self
|
|
73
|
+
|
|
74
|
+
def get_programs(
|
|
75
|
+
self,
|
|
76
|
+
id__in: Optional[list[int]] = None,
|
|
77
|
+
program_name: Optional[str] = None,
|
|
78
|
+
) -> Generator[Program, None, None]:
|
|
79
|
+
"""Yield :class:`Program` objects.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
id__in: Optional list of program ids to filter the results.
|
|
83
|
+
|
|
84
|
+
Yields:
|
|
85
|
+
Program instances parsed from the API response.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
query_params = httpx.QueryParams()
|
|
89
|
+
if id__in is not None:
|
|
90
|
+
query_params.set("id__in", id__in)
|
|
91
|
+
programs = (
|
|
92
|
+
Program.from_dict(program_dict, hive_client=self)
|
|
93
|
+
for program_dict in super().get(
|
|
94
|
+
"/api/core/course/programs/",
|
|
95
|
+
params=query_params,
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
if program_name is not None:
|
|
99
|
+
programs = filter(lambda p: p.name == program_name, programs)
|
|
100
|
+
return programs
|
|
101
|
+
|
|
102
|
+
def get_program(self, program_id: int) -> Program:
|
|
103
|
+
"""Return a single :class:`Program` by id.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
program_id: The program identifier.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
A populated :class:`Program` object.
|
|
110
|
+
"""
|
|
111
|
+
return Program.from_dict(
|
|
112
|
+
super().get(f"/api/core/course/programs/{program_id}/"),
|
|
113
|
+
hive_client=self,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def get_subjects(
|
|
117
|
+
self,
|
|
118
|
+
parent_program__id__in: Optional[list[int]] = None,
|
|
119
|
+
subject_name: Optional[str] = None,
|
|
120
|
+
) -> Generator[Subject, None, None]:
|
|
121
|
+
"""Yield :class:`Subject` objects for course subjects.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
parent_program__id__in: Optional list of parent program ids to
|
|
125
|
+
filter subjects.
|
|
126
|
+
|
|
127
|
+
Yields:
|
|
128
|
+
Subject instances.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
query_params = httpx.QueryParams()
|
|
132
|
+
if parent_program__id__in is not None:
|
|
133
|
+
query_params.set("parent_program__id__in", parent_program__id__in)
|
|
134
|
+
subjects = (
|
|
135
|
+
Subject.from_dict(subject_dict, hive_client=self)
|
|
136
|
+
for subject_dict in super().get(
|
|
137
|
+
"/api/core/course/subjects/",
|
|
138
|
+
params=query_params,
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if subject_name is not None:
|
|
143
|
+
subjects = filter(lambda s: s.name == subject_name, subjects)
|
|
144
|
+
|
|
145
|
+
return subjects
|
|
146
|
+
|
|
147
|
+
def get_subject(self, subject_id: int) -> Subject:
|
|
148
|
+
"""Return a single :class:`Subject` by id.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
subject_id: The subject identifier.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A populated :class:`Subject` object.
|
|
155
|
+
"""
|
|
156
|
+
return Subject.from_dict(
|
|
157
|
+
super().get(f"/api/core/course/subjects/{subject_id}/"),
|
|
158
|
+
hive_client=self,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
def get_modules(
|
|
162
|
+
self,
|
|
163
|
+
/,
|
|
164
|
+
parent_subject__id: Optional[int] = None,
|
|
165
|
+
parent_subject__parent_program__id__in: Optional[list[int]] = None,
|
|
166
|
+
parent_subject: Optional["SubjectLike"] = None,
|
|
167
|
+
module_name: Optional[str] = None,
|
|
168
|
+
) -> Generator[Module, None, None]:
|
|
169
|
+
"""Yield :class:`Module` objects for course modules.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
parent_subject__id: Optional subject id to restrict modules.
|
|
173
|
+
parent_subject__parent_program__id__in: Optional list of program
|
|
174
|
+
ids to restrict modules.
|
|
175
|
+
|
|
176
|
+
Yields:
|
|
177
|
+
Module instances.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
query_params = httpx.QueryParams()
|
|
181
|
+
if parent_subject__parent_program__id__in is not None:
|
|
182
|
+
query_params.set(
|
|
183
|
+
"parent_subject__parent_program__id__in",
|
|
184
|
+
parent_subject__parent_program__id__in,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
parent_subject__id = (
|
|
188
|
+
parent_subject__id
|
|
189
|
+
if parent_subject__id is not None
|
|
190
|
+
else resolve_item_or_id(parent_subject)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if parent_subject__id is not None:
|
|
194
|
+
query_params.set("parent_subject__id", parent_subject__id)
|
|
195
|
+
|
|
196
|
+
modules = (
|
|
197
|
+
Module.from_dict(subject_dict, hive_client=self)
|
|
198
|
+
for subject_dict in super().get(
|
|
199
|
+
"/api/core/course/modules/",
|
|
200
|
+
params=query_params,
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
if module_name is not None:
|
|
204
|
+
modules = filter(lambda m: m.name == module_name, modules)
|
|
205
|
+
return modules
|
|
206
|
+
|
|
207
|
+
def get_module(self, module_id: int) -> Module:
|
|
208
|
+
"""Return a single :class:`Module` by id.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
module_id: The module identifier.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
A populated :class:`Module` object.
|
|
215
|
+
"""
|
|
216
|
+
return Module.from_dict(
|
|
217
|
+
super().get(f"/api/core/course/modules/{module_id}/"),
|
|
218
|
+
hive_client=self,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def get_exercises( # pylint: disable=too-many-arguments
|
|
222
|
+
self,
|
|
223
|
+
*,
|
|
224
|
+
parent_module__id: Optional[int] = None,
|
|
225
|
+
parent_module__parent_subject__id: Optional[int] = None,
|
|
226
|
+
parent_module__parent_subject__parent_program__id__in: Optional[
|
|
227
|
+
list[int]
|
|
228
|
+
] = None,
|
|
229
|
+
queue__id: Optional[int] = None,
|
|
230
|
+
tags__id__in: Optional[list[int]] = None,
|
|
231
|
+
parent_module: Optional["ModuleLike"] = None,
|
|
232
|
+
parent_subject: Optional["SubjectLike"] = None,
|
|
233
|
+
exercise_name: Optional[str] = None,
|
|
234
|
+
) -> Generator[Exercise, None, None]:
|
|
235
|
+
"""Yield :class:`Exercise` objects.
|
|
236
|
+
|
|
237
|
+
Accepts common filtering keyword args which are forwarded to the
|
|
238
|
+
underlying list endpoint.
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
if parent_module is not None and parent_module__id is not None:
|
|
242
|
+
assert parent_module__id == resolve_item_or_id(parent_module)
|
|
243
|
+
parent_module__id = (
|
|
244
|
+
parent_module__id
|
|
245
|
+
if parent_module__id is not None
|
|
246
|
+
else resolve_item_or_id(parent_module)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if parent_subject is not None and parent_module__parent_subject__id is not None:
|
|
250
|
+
assert parent_module__parent_subject__id == resolve_item_or_id(
|
|
251
|
+
parent_subject
|
|
252
|
+
)
|
|
253
|
+
parent_module__parent_subject__id = (
|
|
254
|
+
parent_module__parent_subject__id
|
|
255
|
+
if parent_module__parent_subject__id is not None
|
|
256
|
+
else resolve_item_or_id(parent_subject)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
exercises = self._get_core_items(
|
|
260
|
+
"/api/core/course/exercises/",
|
|
261
|
+
Exercise,
|
|
262
|
+
parent_module__id=parent_module__id,
|
|
263
|
+
parent_module__parent_subject__id=parent_module__parent_subject__id,
|
|
264
|
+
parent_module__parent_subject__parent_program__id__in=parent_module__parent_subject__parent_program__id__in,
|
|
265
|
+
queue__id=queue__id,
|
|
266
|
+
tags__id__in=tags__id__in,
|
|
267
|
+
)
|
|
268
|
+
if exercise_name is not None:
|
|
269
|
+
exercises = filter(lambda e: e.name == exercise_name, exercises)
|
|
270
|
+
return exercises
|
|
271
|
+
|
|
272
|
+
def get_exercise(self, exercise_id: int) -> Exercise:
|
|
273
|
+
"""Return a single :class:`Exercise` by id.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
exercise_id: The exercise identifier.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
A populated :class:`Exercise` object.
|
|
280
|
+
"""
|
|
281
|
+
return Exercise.from_dict(
|
|
282
|
+
super().get(f"/api/core/course/exercises/{exercise_id}/"),
|
|
283
|
+
hive_client=self,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
def get_assignments( # pylint: disable=too-many-arguments
|
|
287
|
+
self,
|
|
288
|
+
*,
|
|
289
|
+
exercise__id: Optional[int] = None,
|
|
290
|
+
exercise__parent_module__id: Optional[int] = None,
|
|
291
|
+
exercise__parent_module__parent_subject__id: Optional[int] = None,
|
|
292
|
+
exercise__tags__id__in: Optional[Sequence[int]] = None,
|
|
293
|
+
queue__id: Optional[int] = None,
|
|
294
|
+
user__classes__id: Optional[int] = None,
|
|
295
|
+
user__classes__id__in: Optional[Sequence[int]] = None,
|
|
296
|
+
user__id__in: Optional[Sequence[int]] = None,
|
|
297
|
+
user__mentor__id: Optional[int] = None,
|
|
298
|
+
user__mentor__id__in: Optional[Sequence[int]] = None,
|
|
299
|
+
user__program__id__in: Optional[Sequence[int]] = None,
|
|
300
|
+
parent_module: Optional["ModuleLike"] = None,
|
|
301
|
+
parent_subject: Optional["SubjectLike"] = None,
|
|
302
|
+
) -> Generator[Assignment, None, None]:
|
|
303
|
+
"""Fetch assignments filtered by various optional parameters."""
|
|
304
|
+
|
|
305
|
+
if parent_module is not None and exercise__parent_module__id is not None:
|
|
306
|
+
assert exercise__parent_module__id == resolve_item_or_id(parent_module)
|
|
307
|
+
exercise__parent_module__id = (
|
|
308
|
+
exercise__parent_module__id
|
|
309
|
+
if exercise__parent_module__id is not None
|
|
310
|
+
else resolve_item_or_id(parent_module)
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
if (
|
|
314
|
+
parent_subject is not None
|
|
315
|
+
and exercise__parent_module__parent_subject__id is not None
|
|
316
|
+
):
|
|
317
|
+
assert exercise__parent_module__parent_subject__id == resolve_item_or_id(
|
|
318
|
+
parent_subject
|
|
319
|
+
)
|
|
320
|
+
exercise__parent_module__parent_subject__id = (
|
|
321
|
+
exercise__parent_module__parent_subject__id
|
|
322
|
+
if exercise__parent_module__parent_subject__id is not None
|
|
323
|
+
else resolve_item_or_id(parent_subject)
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
return self._get_core_items(
|
|
327
|
+
"/api/core/assignments/",
|
|
328
|
+
Assignment,
|
|
329
|
+
exercise__id=exercise__id,
|
|
330
|
+
exercise__parent_module__id=exercise__parent_module__id,
|
|
331
|
+
exercise__parent_module__parent_subject__id=exercise__parent_module__parent_subject__id,
|
|
332
|
+
exercise__tags__id__in=exercise__tags__id__in,
|
|
333
|
+
queue__id=queue__id,
|
|
334
|
+
user__classes__id=user__classes__id,
|
|
335
|
+
user__classes__id__in=user__classes__id__in,
|
|
336
|
+
user__id__in=user__id__in,
|
|
337
|
+
user__mentor__id=user__mentor__id,
|
|
338
|
+
user__mentor__id__in=user__mentor__id__in,
|
|
339
|
+
user__program__id__in=user__program__id__in,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
def get_assignment(self, assignment_id: int) -> Assignment:
|
|
343
|
+
"""Return a single :class:`Assignment` by id.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
assignment_id: The assignment identifier.
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
A populated :class:`Assignment` object.
|
|
350
|
+
"""
|
|
351
|
+
return Assignment.from_dict(
|
|
352
|
+
super().get(f"/api/core/assignments/{assignment_id}/"),
|
|
353
|
+
hive_client=self,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
def get_users( # pylint: disable=too-many-arguments
|
|
357
|
+
self,
|
|
358
|
+
*,
|
|
359
|
+
classes__id__in: Optional[list[int]] = None,
|
|
360
|
+
clearance__in: Optional[list[int]] = None,
|
|
361
|
+
id__in: Optional[list[int]] = None,
|
|
362
|
+
mentor__id: Optional[int] = None,
|
|
363
|
+
mentor__id__in: Optional[list[int]] = None,
|
|
364
|
+
program__id__in: Optional[list[int]] = None,
|
|
365
|
+
program_checker__id__in: Optional[list[int]] = None,
|
|
366
|
+
) -> Generator[User, None, None]:
|
|
367
|
+
"""Yield :class:`User` objects from the management users endpoint.
|
|
368
|
+
|
|
369
|
+
All kwargs are optional filters forwarded to the API.
|
|
370
|
+
"""
|
|
371
|
+
|
|
372
|
+
return self._get_core_items(
|
|
373
|
+
"/api/core/management/users/",
|
|
374
|
+
User,
|
|
375
|
+
classes__id__in=classes__id__in,
|
|
376
|
+
clearance__in=clearance__in,
|
|
377
|
+
id__in=id__in,
|
|
378
|
+
mentor__id=mentor__id,
|
|
379
|
+
mentor__id__in=mentor__id__in,
|
|
380
|
+
program__id__in=program__id__in,
|
|
381
|
+
program_checker__id__in=program_checker__id__in,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
def get_user(self, user_id: int) -> User:
|
|
385
|
+
"""Return a single :class:`User` by id.
|
|
386
|
+
|
|
387
|
+
Args:
|
|
388
|
+
user_id: The user identifier.
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
A populated :class:`User` object.
|
|
392
|
+
"""
|
|
393
|
+
return User.from_dict(
|
|
394
|
+
super().get(f"/api/core/management/users/{user_id}/"),
|
|
395
|
+
hive_client=self,
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
def get_user_me(self) -> User:
|
|
399
|
+
"""Return the currently authenticated user.
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
A populated :class:`User` object.
|
|
403
|
+
"""
|
|
404
|
+
raise NotImplementedError("get_user_me() is not implemented")
|
|
405
|
+
# For some reason this endpoint does not return the same data as /users/{id}/
|
|
406
|
+
return User.from_dict( # pylint: disable=unreachable
|
|
407
|
+
super().get("/api/core/management/users/me/"),
|
|
408
|
+
hive_client=self,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
def get_classes(
|
|
412
|
+
self,
|
|
413
|
+
*,
|
|
414
|
+
id__in: Optional[list[int]] = None,
|
|
415
|
+
name: Optional[str] = None,
|
|
416
|
+
program__id__in: Optional[list[int]] = None,
|
|
417
|
+
type_: Optional[ClassTypeEnum] = None,
|
|
418
|
+
) -> Generator[Class, None, None]:
|
|
419
|
+
"""Yield :class:`Class` objects from the management classes endpoint.
|
|
420
|
+
|
|
421
|
+
Filters may be provided as keyword arguments.
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
return self._get_core_items(
|
|
425
|
+
"/api/core/management/classes/",
|
|
426
|
+
Class,
|
|
427
|
+
id__in=id__in,
|
|
428
|
+
name=name,
|
|
429
|
+
program__id__in=program__id__in,
|
|
430
|
+
type_=type_,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def get_class(
|
|
434
|
+
self,
|
|
435
|
+
class_id: int,
|
|
436
|
+
) -> Class:
|
|
437
|
+
"""Return a single :class:`Class` by id.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
class_id: The class identifier.
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
A populated :class:`Class` object.
|
|
444
|
+
"""
|
|
445
|
+
return Class.from_dict(
|
|
446
|
+
super().get(f"/api/core/management/classes/{class_id}/"),
|
|
447
|
+
hive_client=self,
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
@lru_cache(maxsize=256)
|
|
451
|
+
def get_exercise_fields(
|
|
452
|
+
self,
|
|
453
|
+
exercise: "ExerciseLike",
|
|
454
|
+
) -> Generator[FormField, None, None]:
|
|
455
|
+
"""Yield :class:`FormField` objects for an exercise.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
exercise: The exercise identifier.
|
|
459
|
+
"""
|
|
460
|
+
|
|
461
|
+
exercise_id = resolve_item_or_id(exercise)
|
|
462
|
+
|
|
463
|
+
return self._get_core_items(
|
|
464
|
+
f"/api/core/course/exercises/{exercise_id}/fields/",
|
|
465
|
+
FormField,
|
|
466
|
+
exercise_id=exercise_id,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
@lru_cache(maxsize=1024)
|
|
470
|
+
def get_exercise_field(
|
|
471
|
+
self,
|
|
472
|
+
exercise: "ExerciseLike",
|
|
473
|
+
field_id: int,
|
|
474
|
+
) -> FormField:
|
|
475
|
+
"""Return a single :class:`FormField` for an exercise by id.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
exercise_id: The exercise identifier.
|
|
479
|
+
field_id: The field identifier.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
A populated :class:`FormField` object.
|
|
483
|
+
"""
|
|
484
|
+
exercise_id = resolve_item_or_id(exercise)
|
|
485
|
+
return FormField.from_dict(
|
|
486
|
+
super().get(f"/api/core/course/exercises/{exercise_id}/fields/{field_id}/"),
|
|
487
|
+
hive_client=self,
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def get_assignment_responses(
|
|
491
|
+
self,
|
|
492
|
+
assignment: "AssignmentLike",
|
|
493
|
+
) -> Generator[AssignmentResponse, None, None]:
|
|
494
|
+
"""Get assignment responses for a given assignment."""
|
|
495
|
+
assignment_id = resolve_item_or_id(assignment)
|
|
496
|
+
return self._get_core_items(
|
|
497
|
+
f"/api/core/assignments/{assignment_id}/responses/",
|
|
498
|
+
AssignmentResponse,
|
|
499
|
+
assignment_id=assignment_id,
|
|
500
|
+
extra_ctor_params={"assignment_id": assignment_id},
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
def get_assignment_response(
|
|
504
|
+
self,
|
|
505
|
+
assignment: "AssignmentLike",
|
|
506
|
+
response_id: int,
|
|
507
|
+
) -> AssignmentResponse:
|
|
508
|
+
"""Return a single :class:`AssignmentResponse` by id."""
|
|
509
|
+
assignment_id = resolve_item_or_id(assignment)
|
|
510
|
+
return AssignmentResponse.from_dict(
|
|
511
|
+
super().get(
|
|
512
|
+
f"/api/core/assignments/{assignment_id}/responses/{response_id}/"
|
|
513
|
+
),
|
|
514
|
+
assignment_id=assignment_id,
|
|
515
|
+
hive_client=self,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def get_queue(self, queue_id: int):
|
|
519
|
+
"""Return a single :class:`Queue` by id.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
queue_id: The queue identifier.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
A populated :class:`Queue` object.
|
|
526
|
+
"""
|
|
527
|
+
return Queue.from_dict(
|
|
528
|
+
super().get(f"/api/core/queues/{queue_id}/"),
|
|
529
|
+
hive_client=self,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
def _get_core_items(
|
|
533
|
+
self,
|
|
534
|
+
endpoint: str,
|
|
535
|
+
item_type: type[CoreItemTypeT],
|
|
536
|
+
/,
|
|
537
|
+
extra_ctor_params: Optional[dict[str, Any]] = None,
|
|
538
|
+
**kwargs: dict[str, Any], # noqa: ANN401
|
|
539
|
+
) -> Generator[CoreItemTypeT, None, None]:
|
|
540
|
+
"""Internal helper to yield typed core items from a list endpoint.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
endpoint: API endpoint path for the list resource.
|
|
544
|
+
item_type: Model class with a ``from_dict`` constructor.
|
|
545
|
+
**kwargs: Filter query parameters forwarded to the endpoint.
|
|
546
|
+
|
|
547
|
+
Yields:
|
|
548
|
+
Instances of ``item_type`` created via ``from_dict``.
|
|
549
|
+
"""
|
|
550
|
+
if extra_ctor_params is None:
|
|
551
|
+
extra_ctor_params = {}
|
|
552
|
+
|
|
553
|
+
query_params = httpx.QueryParams()
|
|
554
|
+
for name, value in kwargs.items():
|
|
555
|
+
if value is not None:
|
|
556
|
+
query_params = query_params.set(name, value)
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
item_type.from_dict(x, **extra_ctor_params, hive_client=self)
|
|
560
|
+
for x in super().get(endpoint, params=query_params)
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
def get_hive_version(self) -> str:
|
|
564
|
+
"""Return the Hive server version string."""
|
|
565
|
+
data = super().get("/api/core/schema/")
|
|
566
|
+
|
|
567
|
+
version = data.get("info", {}).get("version", "")
|
|
568
|
+
if not isinstance(version, str) or not re.match(r"^\d+\.\d+\.\d+", version):
|
|
569
|
+
raise ValueError("Invalid version string received from server")
|
|
570
|
+
return version
|
pyhive/src/__init__.py
ADDED
|
File without changes
|