kleinkram 0.33.0.dev20241027195827__tar.gz → 0.33.0.dev20241028075757__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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/PKG-INFO +1 -1
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/pyproject.toml +1 -1
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/endpoint/endpoint.py +2 -1
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/error_handling.py +2 -1
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/helper.py +14 -1
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/main.py +97 -66
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/project/project.py +19 -5
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/.gitignore +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/LICENSE +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/README.md +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/deploy.sh +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/dev.sh +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/requirements.txt +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/klein.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/__init__.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/api_client.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/auth/auth.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/consts.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/file/file.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/mission/mission.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/queue/queue.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/tag/tag.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/topic/topic.py +0 -0
- {kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/user/user.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: kleinkram
|
|
3
|
-
Version: 0.33.0.
|
|
3
|
+
Version: 0.33.0.dev20241028075757
|
|
4
4
|
Summary: A CLI for the ETH project kleinkram
|
|
5
5
|
Project-URL: Homepage, https://github.com/leggedrobotics/kleinkram
|
|
6
6
|
Project-URL: Issues, https://github.com/leggedrobotics/kleinkram/issues
|
|
@@ -4,7 +4,8 @@ from kleinkram.auth.auth import TokenFile
|
|
|
4
4
|
|
|
5
5
|
endpoint = typer.Typer(
|
|
6
6
|
name="endpoint",
|
|
7
|
-
help="Get Or Set the current endpoint"
|
|
7
|
+
help="Get Or Set the current endpoint.\n\nThe endpoint is used to determine the API server to connect to"
|
|
8
|
+
"(default is the API server of https://datasets.leggedrobotics.com).",
|
|
8
9
|
no_args_is_help=True,
|
|
9
10
|
context_settings={"help_option_names": ["-h", "--help"]},
|
|
10
11
|
)
|
|
@@ -28,8 +28,9 @@ class AccessDeniedException(Exception):
|
|
|
28
28
|
|
|
29
29
|
def not_yet_implemented_handler(e: Exception):
|
|
30
30
|
console = Console(file=sys.stderr)
|
|
31
|
+
default_msg = "This feature is not yet implemented. Please check for updates or use the web interface."
|
|
31
32
|
panel = Panel(
|
|
32
|
-
"
|
|
33
|
+
f"{default_msg}",
|
|
33
34
|
title="Not Yet Implemented",
|
|
34
35
|
style="yellow",
|
|
35
36
|
padding=(1, 2),
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/helper.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import glob
|
|
2
2
|
import os
|
|
3
3
|
import queue
|
|
4
|
+
import re
|
|
4
5
|
import sys
|
|
5
6
|
import threading
|
|
6
7
|
from datetime import datetime
|
|
@@ -149,7 +150,7 @@ def uploadFile(
|
|
|
149
150
|
if "error" in file_with_access and (
|
|
150
151
|
file_with_access["error"] is not None or file_with_access["error"] != ""
|
|
151
152
|
):
|
|
152
|
-
console = Console(file=sys.stderr, style="red")
|
|
153
|
+
console = Console(file=sys.stderr, style="red", highlight=False)
|
|
153
154
|
console.print(
|
|
154
155
|
f"Error uploading file: {file_with_access['fileName']} ({filepath}): {file_with_access['error']}"
|
|
155
156
|
)
|
|
@@ -252,6 +253,18 @@ def promptForTags(setTags: Dict[str, str], requiredTags: Dict[str, str]):
|
|
|
252
253
|
setTags[required_tag["uuid"]] = tag_value
|
|
253
254
|
|
|
254
255
|
|
|
256
|
+
def is_valid_UUIDv4(uuid: str) -> bool:
|
|
257
|
+
has_correct_length = len(uuid) == 36
|
|
258
|
+
|
|
259
|
+
# is UUID4
|
|
260
|
+
uuid_regex = (
|
|
261
|
+
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
|
262
|
+
)
|
|
263
|
+
is_valid_uuid = re.match(uuid_regex, uuid)
|
|
264
|
+
|
|
265
|
+
return has_correct_length and is_valid_uuid
|
|
266
|
+
|
|
267
|
+
|
|
255
268
|
if __name__ == "__main__":
|
|
256
269
|
res = expand_and_match(
|
|
257
270
|
"~/Downloads/dodo_mission_2024_02_08-20240408T074313Z-003/**.bag"
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/main.py
RENAMED
|
@@ -25,7 +25,13 @@ from kleinkram.queue.queue import queue
|
|
|
25
25
|
from kleinkram.tag.tag import tag
|
|
26
26
|
from kleinkram.topic.topic import topic
|
|
27
27
|
from kleinkram.user.user import user
|
|
28
|
-
from .helper import
|
|
28
|
+
from .helper import (
|
|
29
|
+
uploadFiles,
|
|
30
|
+
expand_and_match,
|
|
31
|
+
canUploadMission,
|
|
32
|
+
promptForTags,
|
|
33
|
+
is_valid_UUIDv4,
|
|
34
|
+
)
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
class CommandPanel(str, Enum):
|
|
@@ -113,7 +119,9 @@ app.command(hidden=True)(setCliKey)
|
|
|
113
119
|
|
|
114
120
|
@app.command("download", rich_help_panel=CommandPanel.CoreCommands)
|
|
115
121
|
def download():
|
|
116
|
-
|
|
122
|
+
print(
|
|
123
|
+
"Not implemented yet. Consider using the 'klein file download' or 'klein mission download' commands."
|
|
124
|
+
)
|
|
117
125
|
|
|
118
126
|
|
|
119
127
|
@app.command("upload", rich_help_panel=CommandPanel.CoreCommands, no_args_is_help=True)
|
|
@@ -122,8 +130,8 @@ def upload(
|
|
|
122
130
|
List[str],
|
|
123
131
|
typer.Option(help="Path to files to upload, Regex supported"),
|
|
124
132
|
],
|
|
125
|
-
project: Annotated[str, typer.Option(help="Name of Project")],
|
|
126
|
-
mission: Annotated[str, typer.Option(help="Name of Mission to create")],
|
|
133
|
+
project: Annotated[str, typer.Option(help="Name or UUID of a Project")],
|
|
134
|
+
mission: Annotated[str, typer.Option(help="Name of UUID Mission to create")],
|
|
127
135
|
tags: Annotated[
|
|
128
136
|
Optional[List[str]],
|
|
129
137
|
typer.Option(help="Tags to add to the mission"),
|
|
@@ -150,76 +158,33 @@ def upload(
|
|
|
150
158
|
- 'klein upload --path "~/data/**/*.bag" --project "Project 1" --mission "Mission 1" --tags "0700946d-1d6a-4520-b263-0e177f49c35b:LEE-H" --tags "1565118d-593c-4517-8c2d-9658452d9319:Dodo"'\n
|
|
151
159
|
|
|
152
160
|
"""
|
|
153
|
-
files = []
|
|
154
|
-
for p in path:
|
|
155
|
-
files.extend(expand_and_match(p))
|
|
156
|
-
|
|
157
|
-
print(
|
|
158
|
-
f"Uploading the following files to mission '{mission}' in project '{project}':"
|
|
159
|
-
)
|
|
160
|
-
filename_filepaths_map = {}
|
|
161
|
-
for path in files:
|
|
162
|
-
if not os.path.isdir(path):
|
|
163
|
-
|
|
164
|
-
filename = path.split("/")[-1]
|
|
165
|
-
filename_without_extension, extension = os.path.splitext(filename)
|
|
166
|
-
if fix_filenames:
|
|
167
|
-
|
|
168
|
-
# replace all non-alphanumeric characters with underscores
|
|
169
|
-
filename_without_extension = "".join(
|
|
170
|
-
char if char.isalnum() else "_"
|
|
171
|
-
for char in filename_without_extension
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
# trim filename to 40 characters
|
|
175
|
-
filename_without_extension = filename_without_extension[:40]
|
|
176
|
-
filename = f"{filename_without_extension}{extension}"
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
not filename.replace(".", "")
|
|
180
|
-
.replace("_", "")
|
|
181
|
-
.replace("-", "")
|
|
182
|
-
.isalnum()
|
|
183
|
-
):
|
|
184
|
-
raise ValueError(
|
|
185
|
-
f"Filename '{filename}' is not valid. It must only contain alphanumeric characters, underscores and "
|
|
186
|
-
f"hyphens. Consider using the '--fix-filenames' option to automatically fix the filenames."
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
if not 3 <= len(filename_without_extension) <= 40:
|
|
190
|
-
raise ValueError(
|
|
191
|
-
f"Filename '{filename}' is not valid. It must be between 3 and 40 characters long. Consider using "
|
|
192
|
-
f"the '--fix-filenames' option to automatically fix the filenames."
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
filename_filepaths_map[filename] = path
|
|
196
|
-
typer.secho(f" - {filename}", fg=typer.colors.RESET)
|
|
197
|
-
print("\n\n")
|
|
198
|
-
|
|
199
|
-
filenames = list(filename_filepaths_map.keys())
|
|
200
|
-
|
|
201
|
-
if not filenames:
|
|
202
|
-
raise ValueError("No files found matching the given path.")
|
|
203
|
-
|
|
204
|
-
# validate filenames
|
|
205
|
-
if len(filenames) != len(set(filenames)):
|
|
206
|
-
raise ValueError(
|
|
207
|
-
"Filenames must be unique. Please check the files you are trying to upload. This can happen if you have "
|
|
208
|
-
"multiple files with the same name in different directories or use the '--fix-filenames' option."
|
|
209
|
-
)
|
|
210
161
|
|
|
211
162
|
client = AuthenticatedClient()
|
|
212
163
|
|
|
213
|
-
|
|
214
|
-
|
|
164
|
+
##############################
|
|
165
|
+
# Check if project exists
|
|
166
|
+
##############################
|
|
167
|
+
if is_valid_UUIDv4(project):
|
|
168
|
+
get_project_url = "/project/one"
|
|
169
|
+
project_response = client.get(get_project_url, params={"uuid": project})
|
|
170
|
+
else:
|
|
171
|
+
get_project_url = "/project/byName"
|
|
172
|
+
project_response = client.get(get_project_url, params={"name": project})
|
|
173
|
+
|
|
215
174
|
if project_response.status_code >= 400:
|
|
216
|
-
if not create_project:
|
|
175
|
+
if not create_project and not is_valid_UUIDv4(project):
|
|
217
176
|
raise AccessDeniedException(
|
|
218
177
|
f"The project '{project}' does not exist or you do not have access to it.\n"
|
|
219
178
|
f"Consider using the following command to create a project: 'klein project create' "
|
|
220
179
|
f"or consider passing the flag '--create-project' to create the project automatically.",
|
|
221
180
|
f"{project_response.json()['message']} ({project_response.status_code})",
|
|
222
181
|
)
|
|
182
|
+
elif is_valid_UUIDv4(project):
|
|
183
|
+
raise ValueError(
|
|
184
|
+
f"Project '{project}' does not exist. UUIDs cannot be used to create projects.\n"
|
|
185
|
+
f"Please provide a valid project name or consider creating the project using the"
|
|
186
|
+
f" following command: 'klein project create'"
|
|
187
|
+
)
|
|
223
188
|
else:
|
|
224
189
|
print(f"Project '{project}' does not exist. Creating it now.")
|
|
225
190
|
create_project_url = "/project/create"
|
|
@@ -260,8 +225,16 @@ def upload(
|
|
|
260
225
|
)
|
|
261
226
|
promptForTags(tags_dict, required_tags)
|
|
262
227
|
|
|
263
|
-
|
|
264
|
-
|
|
228
|
+
##############################
|
|
229
|
+
# Check if mission exists
|
|
230
|
+
##############################
|
|
231
|
+
if is_valid_UUIDv4(mission):
|
|
232
|
+
get_mission_url = "/mission/one"
|
|
233
|
+
mission_response = client.get(get_mission_url, params={"uuid": mission})
|
|
234
|
+
else:
|
|
235
|
+
get_mission_url = "/mission/byName"
|
|
236
|
+
mission_response = client.get(get_mission_url, params={"name": mission})
|
|
237
|
+
|
|
265
238
|
if mission_response.status_code >= 400:
|
|
266
239
|
if not create_mission:
|
|
267
240
|
raise AccessDeniedException(
|
|
@@ -290,6 +263,64 @@ def upload(
|
|
|
290
263
|
|
|
291
264
|
mission_json = mission_response.json()
|
|
292
265
|
|
|
266
|
+
files = []
|
|
267
|
+
for p in path:
|
|
268
|
+
files.extend(expand_and_match(p))
|
|
269
|
+
|
|
270
|
+
print(
|
|
271
|
+
f"Uploading the following files to mission '{mission_json['name']}' in project '{project_json['name']}':"
|
|
272
|
+
)
|
|
273
|
+
filename_filepaths_map = {}
|
|
274
|
+
for path in files:
|
|
275
|
+
if not os.path.isdir(path):
|
|
276
|
+
|
|
277
|
+
filename = path.split("/")[-1]
|
|
278
|
+
filename_without_extension, extension = os.path.splitext(filename)
|
|
279
|
+
if fix_filenames:
|
|
280
|
+
|
|
281
|
+
# replace all non-alphanumeric characters with underscores
|
|
282
|
+
filename_without_extension = "".join(
|
|
283
|
+
char if char.isalnum() else "_"
|
|
284
|
+
for char in filename_without_extension
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# trim filename to 40 characters
|
|
288
|
+
filename_without_extension = filename_without_extension[:40]
|
|
289
|
+
filename = f"{filename_without_extension}{extension}"
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
not filename.replace(".", "")
|
|
293
|
+
.replace("_", "")
|
|
294
|
+
.replace("-", "")
|
|
295
|
+
.isalnum()
|
|
296
|
+
):
|
|
297
|
+
raise ValueError(
|
|
298
|
+
f"Filename '{filename}' is not valid. It must only contain alphanumeric characters, underscores and "
|
|
299
|
+
f"hyphens. Consider using the '--fix-filenames' option to automatically fix the filenames."
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if not 3 <= len(filename_without_extension) <= 40:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Filename '{filename}' is not valid. It must be between 3 and 40 characters long. Consider using "
|
|
305
|
+
f"the '--fix-filenames' option to automatically fix the filenames."
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
filename_filepaths_map[filename] = path
|
|
309
|
+
typer.secho(f" - {filename}", fg=typer.colors.RESET)
|
|
310
|
+
print("\n\n")
|
|
311
|
+
|
|
312
|
+
filenames = list(filename_filepaths_map.keys())
|
|
313
|
+
|
|
314
|
+
if not filenames:
|
|
315
|
+
raise ValueError("No files found matching the given path.")
|
|
316
|
+
|
|
317
|
+
# validate filenames
|
|
318
|
+
if len(filenames) != len(set(filenames)):
|
|
319
|
+
raise ValueError(
|
|
320
|
+
"Filenames must be unique. Please check the files you are trying to upload. This can happen if you have "
|
|
321
|
+
"multiple files with the same name in different directories or use the '--fix-filenames' option."
|
|
322
|
+
)
|
|
323
|
+
|
|
293
324
|
get_temporary_credentials = "/file/temporaryAccess"
|
|
294
325
|
response = client.post(
|
|
295
326
|
get_temporary_credentials,
|
|
@@ -24,6 +24,10 @@ def list_projects():
|
|
|
24
24
|
response.raise_for_status()
|
|
25
25
|
projects = response.json()[0]
|
|
26
26
|
|
|
27
|
+
if len(projects) == 0:
|
|
28
|
+
print("No projects found. Create a new project using 'klein project create'")
|
|
29
|
+
return
|
|
30
|
+
|
|
27
31
|
stdout_console = Console(stderr=False)
|
|
28
32
|
stderr_console = Console(stderr=True)
|
|
29
33
|
stderr_console.print(f"\nfound {len(projects)} projects with the following UUIDs:")
|
|
@@ -46,7 +50,7 @@ def list_projects():
|
|
|
46
50
|
stderr_console.print("\n")
|
|
47
51
|
|
|
48
52
|
|
|
49
|
-
@project.command("details", help="Get details of a project")
|
|
53
|
+
@project.command("details", help="Get details of a project", no_args_is_help=True)
|
|
50
54
|
def project_details(
|
|
51
55
|
project_uuid: Annotated[
|
|
52
56
|
str, typer.Argument(help="UUID of the project to get details of")
|
|
@@ -97,7 +101,7 @@ def project_details(
|
|
|
97
101
|
stdout_console.print(mission["uuid"])
|
|
98
102
|
|
|
99
103
|
|
|
100
|
-
@project.command("create")
|
|
104
|
+
@project.command("create", no_args_is_help=True, help="Create a new project")
|
|
101
105
|
def create_project(
|
|
102
106
|
name: Annotated[str, typer.Option(help="Name of Project")],
|
|
103
107
|
description: Annotated[str, typer.Option(help="Description of Project")],
|
|
@@ -115,10 +119,20 @@ def create_project(
|
|
|
115
119
|
if response.status_code >= 400:
|
|
116
120
|
response_json = response.json()
|
|
117
121
|
response_text = response_json["message"]
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
122
|
+
raise ValueError(f"Failed to create project\n » {response_text}!")
|
|
123
|
+
|
|
124
|
+
stderr_console = Console(stderr=True)
|
|
125
|
+
stderr_console.print(f"Project '{name}' created successfully.")
|
|
126
|
+
|
|
127
|
+
stdout_console = Console(stderr=False)
|
|
128
|
+
stderr_console.print("\nProject UUID:\n - ", end="")
|
|
129
|
+
stdout_console.print(response.json()["uuid"])
|
|
121
130
|
|
|
122
131
|
except httpx.HTTPError as e:
|
|
123
132
|
print(f"Failed to create project: {e}")
|
|
124
133
|
raise e
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@project.command("delete", help="Delete a project")
|
|
137
|
+
def delete_project():
|
|
138
|
+
raise NotImplementedError()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/auth/auth.py
RENAMED
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/consts.py
RENAMED
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/file/file.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/tag/tag.py
RENAMED
|
File without changes
|
|
File without changes
|
{kleinkram-0.33.0.dev20241027195827 → kleinkram-0.33.0.dev20241028075757}/src/kleinkram/user/user.py
RENAMED
|
File without changes
|