kleinkram 0.51.0.dev20251003120233__py3-none-any.whl → 0.51.0.dev20251010055743__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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- kleinkram/api/deser.py +13 -4
- kleinkram/api/pagination.py +3 -0
- kleinkram/api/routes.py +27 -16
- kleinkram/cli/_project.py +1 -1
- kleinkram/core.py +4 -4
- kleinkram/errors.py +1 -0
- {kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/METADATA +1 -1
- {kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/RECORD +11 -11
- {kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/WHEEL +0 -0
- {kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/entry_points.txt +0 -0
- {kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/top_level.txt +0 -0
kleinkram/api/deser.py
CHANGED
|
@@ -4,17 +4,18 @@ from datetime import datetime
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Any
|
|
6
6
|
from typing import Dict
|
|
7
|
+
from typing import List
|
|
7
8
|
from typing import Literal
|
|
8
9
|
from typing import NewType
|
|
9
10
|
from typing import Tuple
|
|
10
|
-
from typing import List
|
|
11
11
|
from uuid import UUID
|
|
12
12
|
|
|
13
13
|
import dateutil.parser
|
|
14
14
|
|
|
15
15
|
from kleinkram.errors import ParsingError
|
|
16
|
-
from kleinkram.models import File
|
|
16
|
+
from kleinkram.models import File
|
|
17
17
|
from kleinkram.models import FileState
|
|
18
|
+
from kleinkram.models import MetadataValue
|
|
18
19
|
from kleinkram.models import Mission
|
|
19
20
|
from kleinkram.models import Project
|
|
20
21
|
|
|
@@ -85,16 +86,22 @@ def _parse_file_state(state: str) -> FileState:
|
|
|
85
86
|
except ValueError as e:
|
|
86
87
|
raise ParsingError(f"error parsing file state: {state}") from e
|
|
87
88
|
|
|
89
|
+
|
|
88
90
|
def _parse_metadata(tags: List[Dict]) -> Dict[str, MetadataValue]:
|
|
89
91
|
result = {}
|
|
90
92
|
try:
|
|
91
93
|
for tag in tags:
|
|
92
|
-
entry = {
|
|
94
|
+
entry = {
|
|
95
|
+
tag.get("name"): MetadataValue(
|
|
96
|
+
tag.get("valueAsString"), tag.get("datatype")
|
|
97
|
+
)
|
|
98
|
+
}
|
|
93
99
|
result.update(entry)
|
|
94
100
|
return result
|
|
95
101
|
except ValueError as e:
|
|
96
102
|
raise ParsingError(f"error parsing metadata: {e}") from e
|
|
97
103
|
|
|
104
|
+
|
|
98
105
|
def _parse_required_tags(tags: List[Dict]) -> list[str]:
|
|
99
106
|
return list(_parse_metadata(tags).keys())
|
|
100
107
|
|
|
@@ -106,7 +113,9 @@ def _parse_project(project_object: ProjectObject) -> Project:
|
|
|
106
113
|
description = project_object[ProjectObjectKeys.DESCRIPTION]
|
|
107
114
|
created_at = _parse_datetime(project_object[ProjectObjectKeys.CREATED_AT])
|
|
108
115
|
updated_at = _parse_datetime(project_object[ProjectObjectKeys.UPDATED_AT])
|
|
109
|
-
required_tags = _parse_required_tags(
|
|
116
|
+
required_tags = _parse_required_tags(
|
|
117
|
+
project_object[ProjectObjectKeys.REQUIRED_TAGS]
|
|
118
|
+
)
|
|
110
119
|
except Exception as e:
|
|
111
120
|
raise ParsingError(f"error parsing project: {project_object}") from e
|
|
112
121
|
return Project(
|
kleinkram/api/pagination.py
CHANGED
|
@@ -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)
|
kleinkram/api/routes.py
CHANGED
|
@@ -40,13 +40,14 @@ from kleinkram.errors import InvalidMissionQuery
|
|
|
40
40
|
from kleinkram.errors import InvalidProjectQuery
|
|
41
41
|
from kleinkram.errors import MissionExists
|
|
42
42
|
from kleinkram.errors import MissionNotFound
|
|
43
|
+
from kleinkram.errors import MissionValidationError
|
|
43
44
|
from kleinkram.errors import ProjectExists
|
|
44
45
|
from kleinkram.errors import ProjectNotFound
|
|
45
|
-
from kleinkram.errors import MissionValidationError
|
|
46
46
|
from kleinkram.models import File
|
|
47
47
|
from kleinkram.models import Mission
|
|
48
48
|
from kleinkram.models import Project
|
|
49
|
-
from kleinkram.utils import is_valid_uuid4
|
|
49
|
+
from kleinkram.utils import is_valid_uuid4
|
|
50
|
+
from kleinkram.utils import split_args
|
|
50
51
|
|
|
51
52
|
__all__ = [
|
|
52
53
|
"_get_api_version",
|
|
@@ -162,15 +163,22 @@ def get_projects(
|
|
|
162
163
|
client: AuthenticatedClient,
|
|
163
164
|
project_query: ProjectQuery,
|
|
164
165
|
max_entries: Optional[int] = None,
|
|
166
|
+
exact_match: bool = False,
|
|
165
167
|
) -> Generator[Project, None, None]:
|
|
166
168
|
params = _project_query_to_params(project_query)
|
|
167
169
|
response_stream = paginated_request(
|
|
168
|
-
client,
|
|
170
|
+
client,
|
|
171
|
+
PROJECT_ENDPOINT,
|
|
172
|
+
params=params,
|
|
173
|
+
max_entries=max_entries,
|
|
174
|
+
exact_match=exact_match,
|
|
169
175
|
)
|
|
170
176
|
yield from map(lambda p: _parse_project(ProjectObject(p)), response_stream)
|
|
171
177
|
|
|
172
178
|
|
|
173
|
-
def get_project(
|
|
179
|
+
def get_project(
|
|
180
|
+
client: AuthenticatedClient, query: ProjectQuery, exact_match: bool = False
|
|
181
|
+
) -> Project:
|
|
174
182
|
"""\
|
|
175
183
|
get a unique project by specifying a project spec
|
|
176
184
|
"""
|
|
@@ -179,7 +187,7 @@ def get_project(client: AuthenticatedClient, query: ProjectQuery) -> Project:
|
|
|
179
187
|
f"Project query does not uniquely determine project: {query}"
|
|
180
188
|
)
|
|
181
189
|
try:
|
|
182
|
-
return next(get_projects(client, query))
|
|
190
|
+
return next(get_projects(client, query, exact_match=exact_match))
|
|
183
191
|
except StopIteration:
|
|
184
192
|
raise ProjectNotFound(f"Project not found: {query}")
|
|
185
193
|
|
|
@@ -224,7 +232,7 @@ def _mission_name_is_available(
|
|
|
224
232
|
|
|
225
233
|
|
|
226
234
|
def _validate_mission_name(
|
|
227
|
-
|
|
235
|
+
client: AuthenticatedClient, project_id: UUID, mission_name: str
|
|
228
236
|
) -> None:
|
|
229
237
|
if not _mission_name_is_available(client, mission_name, project_id):
|
|
230
238
|
raise MissionExists(
|
|
@@ -241,7 +249,7 @@ def _validate_mission_name(
|
|
|
241
249
|
if mission_name.endswith(" "):
|
|
242
250
|
raise ValueError(
|
|
243
251
|
"A mission name cannot end with a whitespace. "
|
|
244
|
-
f"The given mission name was
|
|
252
|
+
f"The given mission name was '{mission_name}'"
|
|
245
253
|
)
|
|
246
254
|
|
|
247
255
|
|
|
@@ -253,7 +261,10 @@ def _project_name_is_available(client: AuthenticatedClient, project_name: str) -
|
|
|
253
261
|
return True
|
|
254
262
|
return False
|
|
255
263
|
|
|
256
|
-
|
|
264
|
+
|
|
265
|
+
def _validate_mission_created(
|
|
266
|
+
client: AuthenticatedClient, project_id: str, mission_name: str
|
|
267
|
+
) -> None:
|
|
257
268
|
"""
|
|
258
269
|
validate that a mission is successfully created
|
|
259
270
|
"""
|
|
@@ -350,20 +361,20 @@ def _create_project(
|
|
|
350
361
|
return UUID(resp.json()["uuid"], version=4)
|
|
351
362
|
|
|
352
363
|
|
|
353
|
-
def _validate_tag_value(
|
|
354
|
-
tag_value, tag_datatype
|
|
355
|
-
) -> None:
|
|
364
|
+
def _validate_tag_value(tag_value, tag_datatype) -> None:
|
|
356
365
|
if tag_datatype == "NUMBER":
|
|
357
366
|
try:
|
|
358
367
|
float(tag_value)
|
|
359
|
-
except:
|
|
368
|
+
except ValueError:
|
|
360
369
|
raise InvalidMissionMetadata(f"Value '{tag_value}' is not a valid NUMBER")
|
|
361
370
|
elif tag_datatype == "BOOLEAN":
|
|
362
371
|
if tag_value.lower() not in {"true", "false"}:
|
|
363
|
-
raise InvalidMissionMetadata(
|
|
372
|
+
raise InvalidMissionMetadata(
|
|
373
|
+
f"Value '{tag_value}' is not a valid BOOLEAN (expected 'true' or 'false')"
|
|
374
|
+
)
|
|
364
375
|
else:
|
|
365
|
-
pass
|
|
366
|
-
#TODO: add check for LOCATION tag datatype
|
|
376
|
+
pass # any string is fine
|
|
377
|
+
# TODO: add check for LOCATION tag datatype
|
|
367
378
|
|
|
368
379
|
|
|
369
380
|
def _get_metadata_type_id_by_name(
|
|
@@ -376,7 +387,7 @@ def _get_metadata_type_id_by_name(
|
|
|
376
387
|
|
|
377
388
|
resp.raise_for_status()
|
|
378
389
|
try:
|
|
379
|
-
data = resp.json()[
|
|
390
|
+
data = resp.json()["data"][0]
|
|
380
391
|
except IndexError:
|
|
381
392
|
return None, None
|
|
382
393
|
|
kleinkram/cli/_project.py
CHANGED
|
@@ -90,7 +90,7 @@ def delete(
|
|
|
90
90
|
project_query = ProjectQuery(ids=project_ids, patterns=project_patterns)
|
|
91
91
|
|
|
92
92
|
client = AuthenticatedClient()
|
|
93
|
-
project_id = get_project(client=client, query=project_query).id
|
|
93
|
+
project_id = get_project(client=client, query=project_query, exact_match=True).id
|
|
94
94
|
kleinkram.core.delete_project(client=client, project_id=project_id)
|
|
95
95
|
|
|
96
96
|
|
kleinkram/core.py
CHANGED
|
@@ -107,9 +107,7 @@ def upload(
|
|
|
107
107
|
|
|
108
108
|
if create and mission is None:
|
|
109
109
|
# check if project exists and get its id at the same time
|
|
110
|
-
project = kleinkram.api.routes.get_project(
|
|
111
|
-
client, query=query.project_query
|
|
112
|
-
)
|
|
110
|
+
project = kleinkram.api.routes.get_project(client, query=query.project_query)
|
|
113
111
|
project_id = project.id
|
|
114
112
|
project_required_tags = project.required_tags
|
|
115
113
|
mission_name = check_mission_query_is_creatable(query)
|
|
@@ -284,7 +282,9 @@ def delete_mission(*, client: AuthenticatedClient, mission_id: UUID) -> None:
|
|
|
284
282
|
|
|
285
283
|
def delete_project(*, client: AuthenticatedClient, project_id: UUID) -> None:
|
|
286
284
|
pquery = ProjectQuery(ids=[project_id])
|
|
287
|
-
_ = kleinkram.api.routes.get_project(
|
|
285
|
+
_ = kleinkram.api.routes.get_project(
|
|
286
|
+
client, pquery, exact_match=True
|
|
287
|
+
) # check if project exists
|
|
288
288
|
|
|
289
289
|
# delete all missions and files
|
|
290
290
|
missions = list(
|
kleinkram/errors.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kleinkram
|
|
3
|
-
Version: 0.51.0.
|
|
3
|
+
Version: 0.51.0.dev20251010055743
|
|
4
4
|
Summary: give me your bags
|
|
5
5
|
Author: Cyrill Püntener, Dominique Garmier, Johann Schwabe
|
|
6
6
|
Author-email: pucyril@ethz.ch, dgarmier@ethz.ch, jschwab@ethz.ch
|
{kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/RECORD
RENAMED
|
@@ -3,8 +3,8 @@ kleinkram/__main__.py,sha256=B9RiZxfO4jpCmWPUHyKJ7_EoZlEG4sPpH-nz7T_YhhQ,125
|
|
|
3
3
|
kleinkram/_version.py,sha256=QYJyRTcqFcJj4qWYpqs7WcoOP6jxDMqyvxLY-cD6KcE,129
|
|
4
4
|
kleinkram/auth.py,sha256=PdSYZZO8AauNLZbn9PBgPM3o-O_nwoOKTj94EGnPRE8,3003
|
|
5
5
|
kleinkram/config.py,sha256=nx6uSM5nLP4SKe8b9VAx4KDtCCwtyshXmzbEJcUwpsY,7411
|
|
6
|
-
kleinkram/core.py,sha256=
|
|
7
|
-
kleinkram/errors.py,sha256=
|
|
6
|
+
kleinkram/core.py,sha256=N91W_IRw7yH9pR-_wAmaVjcGgiz0xF7QwG20863oOiY,9760
|
|
7
|
+
kleinkram/errors.py,sha256=WrEOpSwOUr-_waaQ237E6Gm1P5A2OrI55g6Q6iGgkNk,1012
|
|
8
8
|
kleinkram/main.py,sha256=BTE0mZN__xd46wBhFi6iBlK9eGGQvJ1LdUMsbnysLi0,172
|
|
9
9
|
kleinkram/models.py,sha256=0C_TharLDHA4RCe6Plas9N_uO_teN1Z4iP70WljOAfs,1899
|
|
10
10
|
kleinkram/printing.py,sha256=9o4UQq9MYkGwMIlTchbdMLjUROdJWB100Lq1b3OFfko,12280
|
|
@@ -14,18 +14,18 @@ kleinkram/utils.py,sha256=AtaTvEQ0TrGaQtZylwniE9l1u7_IRYigLT2bc_jc-lQ,6790
|
|
|
14
14
|
kleinkram/wrappers.py,sha256=ZScoEov5Q6D2rvaJJ8E-4f58P_NGWrGc9mRPYxSqOC0,13127
|
|
15
15
|
kleinkram/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
kleinkram/api/client.py,sha256=VwuT97_WdbDpcVGwMXB0fRnUoQnUSf7BOP5eXUFokfI,5932
|
|
17
|
-
kleinkram/api/deser.py,sha256=
|
|
17
|
+
kleinkram/api/deser.py,sha256=6ar6_WbgvTIkx1rNRzvVP9YNa5BrFD4181q1fml1KwU,5637
|
|
18
18
|
kleinkram/api/file_transfer.py,sha256=Ija34JXaszZR7_hvb08aVzq-DB2KG3ze-qqb7zjrchQ,19985
|
|
19
|
-
kleinkram/api/pagination.py,sha256=
|
|
19
|
+
kleinkram/api/pagination.py,sha256=VqjIPMzcD2FY3yeBmP76S7vprUGnuFfTLOzbskqnl0U,1511
|
|
20
20
|
kleinkram/api/query.py,sha256=9Exi4hJR7Ml38_zjAcOvSEoIAxZLlpM6QwwzO9fs5Gk,3293
|
|
21
|
-
kleinkram/api/routes.py,sha256=
|
|
21
|
+
kleinkram/api/routes.py,sha256=buWu4BKdAF1Tk3fmT-MiuG_otJ2Sv4lIcEVE9cZ8isg,15264
|
|
22
22
|
kleinkram/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
kleinkram/cli/_download.py,sha256=e0fDyp_CFOdbKIUGKmtITvAVINa6STYJk5w5QlElXSs,2394
|
|
24
24
|
kleinkram/cli/_endpoint.py,sha256=oY0p4bnuHLEDJCXtTmir4AHswcKAygZ8I4IWC3RFcKc,1796
|
|
25
25
|
kleinkram/cli/_file.py,sha256=Q2fLDdUyfHFmdGC6wIxMqgEl0F76qszhzWJrRV5rTBM,2973
|
|
26
26
|
kleinkram/cli/_list.py,sha256=5gI3aIUeKC0_eWPQqdFXSBBFvpkTTJSm31TamHa197c,3090
|
|
27
27
|
kleinkram/cli/_mission.py,sha256=3ZMPRlPZIvJwmFQqeXu6N8DcmYtSVGj4xWHuAdKAlsc,5845
|
|
28
|
-
kleinkram/cli/_project.py,sha256=
|
|
28
|
+
kleinkram/cli/_project.py,sha256=tvVwcNaBYKZhIh6KjPcdyyTmaep6y-GvG_sV7O49Ov0,3416
|
|
29
29
|
kleinkram/cli/_upload.py,sha256=8gKg_QIFUJh4Sz06EEX3gQoa3Rl41KTdZWqtsntypJU,3531
|
|
30
30
|
kleinkram/cli/_verify.py,sha256=n9QThY0JnqaIqw6udYXdRQGcpUl2lIbFXGQIgpTnDPE,2112
|
|
31
31
|
kleinkram/cli/app.py,sha256=Yetkt2jd6cggor7mPpV9Lcp6aLd45rACdF1nBW0uy9k,7546
|
|
@@ -43,8 +43,8 @@ tests/test_printing.py,sha256=kPzpIQOtQJ9yQ32mM8cMGDVOGsbrZZLQhfsXN1Pe68Q,2231
|
|
|
43
43
|
tests/test_query.py,sha256=fExmCKXLA7-9j2S2sF_sbvRX_2s6Cp3a7OTcqE25q9g,3864
|
|
44
44
|
tests/test_utils.py,sha256=eUBYrn3xrcgcaxm1X4fqZaX4tRvkbI6rh6BUbNbu9T0,4784
|
|
45
45
|
tests/test_wrappers.py,sha256=TbcTyO2L7fslbzgfDdcVZkencxNQ8cGPZm_iB6c9d6Q,2673
|
|
46
|
-
kleinkram-0.51.0.
|
|
47
|
-
kleinkram-0.51.0.
|
|
48
|
-
kleinkram-0.51.0.
|
|
49
|
-
kleinkram-0.51.0.
|
|
50
|
-
kleinkram-0.51.0.
|
|
46
|
+
kleinkram-0.51.0.dev20251010055743.dist-info/METADATA,sha256=09mxfAwJAQrgmlf7ZKFUMMHvxOrvOL-AzWg5CblTCFE,2846
|
|
47
|
+
kleinkram-0.51.0.dev20251010055743.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
48
|
+
kleinkram-0.51.0.dev20251010055743.dist-info/entry_points.txt,sha256=SaB2l5aqhSr8gmaMw2kvQU90a8Bnl7PedU8cWYxkfYo,46
|
|
49
|
+
kleinkram-0.51.0.dev20251010055743.dist-info/top_level.txt,sha256=N3-sJagEHu1Tk1X6Dx1X1q0pLDNbDZpLzRxVftvepds,24
|
|
50
|
+
kleinkram-0.51.0.dev20251010055743.dist-info/RECORD,,
|
{kleinkram-0.51.0.dev20251003120233.dist-info → kleinkram-0.51.0.dev20251010055743.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|