kleinkram 0.49.0.dev20250728101614__tar.gz → 0.53.0__tar.gz
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.
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/PKG-INFO +1 -1
- kleinkram-0.53.0/kleinkram/api/deser.py +350 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/file_transfer.py +14 -13
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/pagination.py +3 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/query.py +8 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/routes.py +198 -25
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/auth.py +5 -3
- kleinkram-0.53.0/kleinkram/cli/_action.py +140 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_endpoint.py +1 -1
- kleinkram-0.53.0/kleinkram/cli/_file_validator.py +130 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_mission.py +16 -4
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_project.py +2 -2
- kleinkram-0.53.0/kleinkram/cli/_run.py +112 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_upload.py +63 -20
- kleinkram-0.53.0/kleinkram/cli/_verify.py +106 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/app.py +33 -4
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/core.py +17 -5
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/errors.py +12 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/models.py +46 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/printing.py +223 -2
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/utils.py +2 -4
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/PKG-INFO +1 -1
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/SOURCES.txt +3 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/setup.cfg +1 -1
- kleinkram-0.49.0.dev20250728101614/kleinkram/api/deser.py +0 -162
- kleinkram-0.49.0.dev20250728101614/kleinkram/cli/_verify.py +0 -66
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/README.md +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/__init__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/__main__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/_version.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/__init__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/api/client.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/__init__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_download.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_file.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/_list.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/cli/error_handling.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/config.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/main.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/py.typed +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/types.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram/wrappers.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/dependency_links.txt +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/entry_points.txt +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/requires.txt +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/kleinkram.egg-info/top_level.txt +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/pyproject.toml +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/requirements.txt +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/setup.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/testing/__init__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/testing/backend_fixtures.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/__init__.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/conftest.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_config.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_core.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_end_to_end.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_error_handling.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_fixtures.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_printing.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_query.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_utils.py +0 -0
- {kleinkram-0.49.0.dev20250728101614 → kleinkram-0.53.0}/tests/test_wrappers.py +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Any
|
|
6
|
+
from typing import Dict
|
|
7
|
+
from typing import List
|
|
8
|
+
from typing import Literal
|
|
9
|
+
from typing import NewType
|
|
10
|
+
from typing import Tuple
|
|
11
|
+
from uuid import UUID
|
|
12
|
+
|
|
13
|
+
import dateutil.parser
|
|
14
|
+
|
|
15
|
+
from kleinkram.errors import ParsingError
|
|
16
|
+
from kleinkram.models import File, Run, LogEntry, ActionTemplate
|
|
17
|
+
from kleinkram.models import FileState
|
|
18
|
+
from kleinkram.models import MetadataValue
|
|
19
|
+
from kleinkram.models import Mission
|
|
20
|
+
from kleinkram.models import Project
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"_parse_project",
|
|
24
|
+
"_parse_mission",
|
|
25
|
+
"_parse_file",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
ProjectObject = NewType("ProjectObject", Dict[str, Any])
|
|
30
|
+
MissionObject = NewType("MissionObject", Dict[str, Any])
|
|
31
|
+
FileObject = NewType("FileObject", Dict[str, Any])
|
|
32
|
+
RunObject = NewType("RunObject", Dict[str, Any])
|
|
33
|
+
|
|
34
|
+
MISSION = "mission"
|
|
35
|
+
PROJECT = "project"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class FileObjectKeys(str, Enum):
|
|
39
|
+
UUID = "uuid"
|
|
40
|
+
FILENAME = "filename"
|
|
41
|
+
DATE = "date" # at some point this will become a metadata
|
|
42
|
+
CREATED_AT = "createdAt"
|
|
43
|
+
UPDATED_AT = "updatedAt"
|
|
44
|
+
STATE = "state"
|
|
45
|
+
SIZE = "size"
|
|
46
|
+
HASH = "hash"
|
|
47
|
+
TYPE = "type"
|
|
48
|
+
CATEGORIES = "categories"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MissionObjectKeys(str, Enum):
|
|
52
|
+
UUID = "uuid"
|
|
53
|
+
NAME = "name"
|
|
54
|
+
DESCRIPTION = "description"
|
|
55
|
+
CREATED_AT = "createdAt"
|
|
56
|
+
UPDATED_AT = "updatedAt"
|
|
57
|
+
TAGS = "tags"
|
|
58
|
+
FILESIZE = "size"
|
|
59
|
+
FILECOUNT = "filesCount"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ProjectObjectKeys(str, Enum):
|
|
63
|
+
UUID = "uuid"
|
|
64
|
+
NAME = "name"
|
|
65
|
+
DESCRIPTION = "description"
|
|
66
|
+
CREATED_AT = "createdAt"
|
|
67
|
+
UPDATED_AT = "updatedAt"
|
|
68
|
+
REQUIRED_TAGS = "requiredTags"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RunObjectKeys(str, Enum):
|
|
72
|
+
UUID = "uuid"
|
|
73
|
+
STATE = "state"
|
|
74
|
+
CREATED_AT = "createdAt"
|
|
75
|
+
MISSION = "mission"
|
|
76
|
+
TEMPLATE = "template"
|
|
77
|
+
UPDATED_AT = "updatedAt"
|
|
78
|
+
LOGS = "logs"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
@dataclass(frozen=True)
|
|
83
|
+
class ActionTemplate:
|
|
84
|
+
uuid: UUID
|
|
85
|
+
access_rights: int
|
|
86
|
+
command: str
|
|
87
|
+
cpu_cores: int
|
|
88
|
+
cpu_memory_gb: int
|
|
89
|
+
entrypoint: str
|
|
90
|
+
gpu_memory_gb: int
|
|
91
|
+
image_name: str
|
|
92
|
+
max_runtime_minutes: int
|
|
93
|
+
created_at: datetime
|
|
94
|
+
name: str
|
|
95
|
+
version: str
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class TemplateObjectKeys(str, Enum):
|
|
101
|
+
UUID = "uuid"
|
|
102
|
+
NAME = "name"
|
|
103
|
+
ACCESS_RIGHTS = "accessRights"
|
|
104
|
+
COMMAND = "command"
|
|
105
|
+
CPU_CORES = "cpuCores"
|
|
106
|
+
CPU_MEMORY_GB = "cpuMemory"
|
|
107
|
+
ENTRYPOINT = "entrypoint"
|
|
108
|
+
GPU_MEMORY_GB = "gpuMemory"
|
|
109
|
+
IMAGE_NAME = "imageName"
|
|
110
|
+
MAX_RUNTIME_MINUTES = "maxRuntime"
|
|
111
|
+
CREATED_AT = "createdAt"
|
|
112
|
+
VERSION = "version"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LogEntryObjectKeys(str, Enum):
|
|
116
|
+
TIMESTAMP = "timestamp"
|
|
117
|
+
LEVEL = "type"
|
|
118
|
+
MESSAGE = "message"
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _get_nested_info(data, key: Literal["mission", "project"]) -> Tuple[UUID, str]:
|
|
122
|
+
nested_data = data[key]
|
|
123
|
+
return (
|
|
124
|
+
UUID(nested_data[ProjectObjectKeys.UUID], version=4),
|
|
125
|
+
nested_data[ProjectObjectKeys.NAME],
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _parse_datetime(date: str) -> datetime:
|
|
130
|
+
try:
|
|
131
|
+
return dateutil.parser.isoparse(date)
|
|
132
|
+
except ValueError as e:
|
|
133
|
+
raise ParsingError(f"error parsing date: {date}") from e
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _parse_file_state(state: str) -> FileState:
|
|
137
|
+
try:
|
|
138
|
+
return FileState(state)
|
|
139
|
+
except ValueError as e:
|
|
140
|
+
raise ParsingError(f"error parsing file state: {state}") from e
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _parse_metadata(tags: List[Dict]) -> Dict[str, MetadataValue]:
|
|
144
|
+
result = {}
|
|
145
|
+
try:
|
|
146
|
+
for tag in tags:
|
|
147
|
+
entry = {
|
|
148
|
+
tag.get("name"): MetadataValue(
|
|
149
|
+
tag.get("valueAsString"), tag.get("datatype")
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
result.update(entry)
|
|
153
|
+
return result
|
|
154
|
+
except ValueError as e:
|
|
155
|
+
raise ParsingError(f"error parsing metadata: {e}") from e
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _parse_required_tags(tags: List[Dict]) -> list[str]:
|
|
159
|
+
return list(_parse_metadata(tags).keys())
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _parse_project(project_object: ProjectObject) -> Project:
|
|
163
|
+
try:
|
|
164
|
+
id_ = UUID(project_object[ProjectObjectKeys.UUID], version=4)
|
|
165
|
+
name = project_object[ProjectObjectKeys.NAME]
|
|
166
|
+
description = project_object[ProjectObjectKeys.DESCRIPTION]
|
|
167
|
+
created_at = _parse_datetime(project_object[ProjectObjectKeys.CREATED_AT])
|
|
168
|
+
updated_at = _parse_datetime(project_object[ProjectObjectKeys.UPDATED_AT])
|
|
169
|
+
required_tags = _parse_required_tags(
|
|
170
|
+
project_object[ProjectObjectKeys.REQUIRED_TAGS]
|
|
171
|
+
)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
raise ParsingError(f"error parsing project: {project_object}") from e
|
|
174
|
+
return Project(
|
|
175
|
+
id=id_,
|
|
176
|
+
name=name,
|
|
177
|
+
description=description,
|
|
178
|
+
created_at=created_at,
|
|
179
|
+
updated_at=updated_at,
|
|
180
|
+
required_tags=required_tags,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _parse_mission(mission: MissionObject) -> Mission:
|
|
185
|
+
try:
|
|
186
|
+
id_ = UUID(mission[MissionObjectKeys.UUID], version=4)
|
|
187
|
+
name = mission[MissionObjectKeys.NAME]
|
|
188
|
+
created_at = _parse_datetime(mission[MissionObjectKeys.CREATED_AT])
|
|
189
|
+
updated_at = _parse_datetime(mission[MissionObjectKeys.UPDATED_AT])
|
|
190
|
+
metadata = _parse_metadata(mission[MissionObjectKeys.TAGS])
|
|
191
|
+
file_count = mission[MissionObjectKeys.FILECOUNT]
|
|
192
|
+
filesize = mission[MissionObjectKeys.FILESIZE]
|
|
193
|
+
|
|
194
|
+
project_id, project_name = _get_nested_info(mission, PROJECT)
|
|
195
|
+
|
|
196
|
+
parsed = Mission(
|
|
197
|
+
id=id_,
|
|
198
|
+
name=name,
|
|
199
|
+
created_at=created_at,
|
|
200
|
+
updated_at=updated_at,
|
|
201
|
+
metadata=metadata,
|
|
202
|
+
project_id=project_id,
|
|
203
|
+
project_name=project_name,
|
|
204
|
+
number_of_files=file_count,
|
|
205
|
+
size=filesize,
|
|
206
|
+
)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
raise ParsingError(f"error parsing mission: {mission}") from e
|
|
209
|
+
return parsed
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _parse_file(file: FileObject) -> File:
|
|
213
|
+
try:
|
|
214
|
+
name = file[FileObjectKeys.FILENAME]
|
|
215
|
+
id_ = UUID(file[FileObjectKeys.UUID], version=4)
|
|
216
|
+
fsize = file[FileObjectKeys.SIZE]
|
|
217
|
+
fhash = file[FileObjectKeys.HASH]
|
|
218
|
+
ftype = file[FileObjectKeys.TYPE].split(".")[-1]
|
|
219
|
+
fdate = file[FileObjectKeys.DATE]
|
|
220
|
+
created_at = _parse_datetime(file[FileObjectKeys.CREATED_AT])
|
|
221
|
+
updated_at = _parse_datetime(file[FileObjectKeys.UPDATED_AT])
|
|
222
|
+
state = _parse_file_state(file[FileObjectKeys.STATE])
|
|
223
|
+
categories = file[FileObjectKeys.CATEGORIES]
|
|
224
|
+
|
|
225
|
+
mission_id, mission_name = _get_nested_info(file, MISSION)
|
|
226
|
+
project_id, project_name = _get_nested_info(file[MISSION], PROJECT)
|
|
227
|
+
|
|
228
|
+
parsed = File(
|
|
229
|
+
id=id_,
|
|
230
|
+
name=name,
|
|
231
|
+
hash=fhash,
|
|
232
|
+
size=fsize,
|
|
233
|
+
type_=ftype,
|
|
234
|
+
date=fdate,
|
|
235
|
+
categories=categories,
|
|
236
|
+
state=state,
|
|
237
|
+
created_at=created_at,
|
|
238
|
+
updated_at=updated_at,
|
|
239
|
+
mission_id=mission_id,
|
|
240
|
+
mission_name=mission_name,
|
|
241
|
+
project_id=project_id,
|
|
242
|
+
project_name=project_name,
|
|
243
|
+
)
|
|
244
|
+
except Exception as e:
|
|
245
|
+
raise ParsingError(f"error parsing file: {file}") from e
|
|
246
|
+
return parsed
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
"""
|
|
250
|
+
@dataclass(frozen=True)
|
|
251
|
+
class ActionTemplate:
|
|
252
|
+
uuid: UUID
|
|
253
|
+
access_rights: int
|
|
254
|
+
command: str
|
|
255
|
+
cpu_cores: int
|
|
256
|
+
cpu_memory_gb: int
|
|
257
|
+
entrypoint: str
|
|
258
|
+
gpu_memory_gb: int
|
|
259
|
+
image_name: str
|
|
260
|
+
max_runtime_minutes: int
|
|
261
|
+
created_at: datetime
|
|
262
|
+
name: str
|
|
263
|
+
version: str
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _parse_action_template(run_object: RunObject) -> ActionTemplate:
|
|
269
|
+
try:
|
|
270
|
+
uuid_ = UUID(run_object[TemplateObjectKeys.UUID], version=4)
|
|
271
|
+
access_rights = run_object[TemplateObjectKeys.ACCESS_RIGHTS]
|
|
272
|
+
command = run_object[TemplateObjectKeys.COMMAND]
|
|
273
|
+
cpu_cores = run_object[TemplateObjectKeys.CPU_CORES]
|
|
274
|
+
cpu_memory_gb = run_object[TemplateObjectKeys.CPU_MEMORY_GB]
|
|
275
|
+
entrypoint = run_object[TemplateObjectKeys.ENTRYPOINT]
|
|
276
|
+
gpu_memory_gb = run_object[TemplateObjectKeys.GPU_MEMORY_GB]
|
|
277
|
+
image_name = run_object[TemplateObjectKeys.IMAGE_NAME]
|
|
278
|
+
max_runtime_minutes = run_object[TemplateObjectKeys.MAX_RUNTIME_MINUTES]
|
|
279
|
+
created_at = _parse_datetime(run_object[TemplateObjectKeys.CREATED_AT])
|
|
280
|
+
name = run_object[TemplateObjectKeys.NAME]
|
|
281
|
+
version = run_object[TemplateObjectKeys.VERSION]
|
|
282
|
+
|
|
283
|
+
except Exception as e:
|
|
284
|
+
raise ParsingError(f"error parsing action template: {run_object}") from e
|
|
285
|
+
|
|
286
|
+
return ActionTemplate(
|
|
287
|
+
uuid=uuid_,
|
|
288
|
+
access_rights=access_rights,
|
|
289
|
+
command=command,
|
|
290
|
+
cpu_cores=cpu_cores,
|
|
291
|
+
cpu_memory_gb=cpu_memory_gb,
|
|
292
|
+
entrypoint=entrypoint,
|
|
293
|
+
gpu_memory_gb=gpu_memory_gb,
|
|
294
|
+
image_name=image_name,
|
|
295
|
+
max_runtime_minutes=max_runtime_minutes,
|
|
296
|
+
created_at=created_at,
|
|
297
|
+
name=name,
|
|
298
|
+
version=version,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _parse_run(run_object: RunObject) -> Run:
|
|
303
|
+
try:
|
|
304
|
+
uuid_ = UUID(run_object[RunObjectKeys.UUID], version=4)
|
|
305
|
+
state = run_object[RunObjectKeys.STATE]
|
|
306
|
+
created_at = _parse_datetime(run_object[RunObjectKeys.CREATED_AT])
|
|
307
|
+
updated_at = (
|
|
308
|
+
_parse_datetime(run_object[RunObjectKeys.UPDATED_AT])
|
|
309
|
+
if run_object.get(RunObjectKeys.UPDATED_AT)
|
|
310
|
+
else None
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
mission_dict = run_object[RunObjectKeys.MISSION]
|
|
314
|
+
mission_id = UUID(mission_dict[MissionObjectKeys.UUID], version=4)
|
|
315
|
+
mission_name = mission_dict[MissionObjectKeys.NAME]
|
|
316
|
+
|
|
317
|
+
project_dict = mission_dict[PROJECT]
|
|
318
|
+
project_name = project_dict[ProjectObjectKeys.NAME]
|
|
319
|
+
|
|
320
|
+
template_dict = run_object[RunObjectKeys.TEMPLATE]
|
|
321
|
+
template_id = UUID(template_dict[TemplateObjectKeys.UUID], version=4)
|
|
322
|
+
template_name = template_dict[TemplateObjectKeys.NAME]
|
|
323
|
+
logs = []
|
|
324
|
+
for log_entry in run_object.get(RunObjectKeys.LOGS, []):
|
|
325
|
+
log_timestamp = _parse_datetime(log_entry[LogEntryObjectKeys.TIMESTAMP])
|
|
326
|
+
log_level = log_entry[LogEntryObjectKeys.LEVEL]
|
|
327
|
+
log_message = log_entry[LogEntryObjectKeys.MESSAGE]
|
|
328
|
+
logs.append(
|
|
329
|
+
LogEntry(
|
|
330
|
+
timestamp=log_timestamp,
|
|
331
|
+
level=log_level,
|
|
332
|
+
message=log_message,
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
except Exception as e:
|
|
337
|
+
raise ParsingError(f"error parsing run: {run_object}") from e
|
|
338
|
+
|
|
339
|
+
return Run(
|
|
340
|
+
uuid=uuid_,
|
|
341
|
+
state=state,
|
|
342
|
+
created_at=created_at,
|
|
343
|
+
updated_at=updated_at,
|
|
344
|
+
mission_id=mission_id,
|
|
345
|
+
mission_name=mission_name,
|
|
346
|
+
project_name=project_name,
|
|
347
|
+
template_id=template_id,
|
|
348
|
+
template_name=template_name,
|
|
349
|
+
logs=logs,
|
|
350
|
+
)
|
|
@@ -572,19 +572,20 @@ def upload_files(
|
|
|
572
572
|
|
|
573
573
|
avg_speed_bps = total_uploaded_bytes / elapsed_time if elapsed_time > 0 else 0
|
|
574
574
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
575
|
+
if verbose:
|
|
576
|
+
console.print(f"Upload took {elapsed_time:.2f} seconds")
|
|
577
|
+
console.print(f"Total uploaded: {format_bytes(total_uploaded_bytes)}")
|
|
578
|
+
console.print(f"Average speed: {format_bytes(avg_speed_bps, speed=True)}")
|
|
579
|
+
|
|
580
|
+
if failed_files > 0:
|
|
581
|
+
console.print(
|
|
582
|
+
f"\nUploaded {len(files) - failed_files - skipped_files} files, {skipped_files} skipped, {failed_files} uploads failed",
|
|
583
|
+
style="red",
|
|
584
|
+
)
|
|
585
|
+
else:
|
|
586
|
+
console.print(
|
|
587
|
+
f"\nUploaded {len(files) - skipped_files} files, {skipped_files} skipped"
|
|
588
|
+
)
|
|
588
589
|
|
|
589
590
|
|
|
590
591
|
def download_files(
|
|
@@ -17,6 +17,7 @@ DataPage = Dict[str, Any]
|
|
|
17
17
|
PAGE_SIZE = 128
|
|
18
18
|
SKIP = "skip"
|
|
19
19
|
TAKE = "take"
|
|
20
|
+
EXACT_MATCH = "exactMatch"
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def paginated_request(
|
|
@@ -25,6 +26,7 @@ def paginated_request(
|
|
|
25
26
|
params: Optional[Mapping[str, Any]] = None,
|
|
26
27
|
max_entries: Optional[int] = None,
|
|
27
28
|
page_size: int = PAGE_SIZE,
|
|
29
|
+
exact_match: bool = False,
|
|
28
30
|
) -> Generator[DataPage, None, None]:
|
|
29
31
|
total_entries_count = 0
|
|
30
32
|
|
|
@@ -32,6 +34,7 @@ def paginated_request(
|
|
|
32
34
|
|
|
33
35
|
params[TAKE] = page_size
|
|
34
36
|
params[SKIP] = 0
|
|
37
|
+
params[EXACT_MATCH] = str(exact_match).lower() # pass string rather than bool
|
|
35
38
|
|
|
36
39
|
while True:
|
|
37
40
|
resp = client.get(endpoint, params=params)
|
|
@@ -41,6 +41,14 @@ class FileQuery:
|
|
|
41
41
|
mission_query: MissionQuery = field(default_factory=MissionQuery)
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
@dataclass
|
|
45
|
+
class RunQuery:
|
|
46
|
+
mission_ids: List[UUID] = field(default_factory=list)
|
|
47
|
+
mission_patterns: List[str] = field(default_factory=list)
|
|
48
|
+
project_ids: List[UUID] = field(default_factory=list)
|
|
49
|
+
project_patterns: List[str] = field(default_factory=list)
|
|
50
|
+
|
|
51
|
+
|
|
44
52
|
def check_mission_query_is_creatable(query: MissionQuery) -> str:
|
|
45
53
|
"""\
|
|
46
54
|
check if a query is unique and can be used to create a mission
|