kleinkram 0.33.0.dev20241024133645__py3-none-any.whl → 0.33.0.dev20241028075757__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.

@@ -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
- "This feature is not yet implemented, please check for updates.",
33
+ f"{default_msg}",
33
34
  title="Not Yet Implemented",
34
35
  style="yellow",
35
36
  padding=(1, 2),
kleinkram/helper.py CHANGED
@@ -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/main.py CHANGED
@@ -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 uploadFiles, expand_and_match, canUploadMission, promptForTags
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
- raise NotImplementedError("Not implemented yet.")
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
- get_project_url = "/project/byName"
214
- project_response = client.get(get_project_url, params={"name": project})
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
- get_mission_url = "/mission/byName"
264
- mission_response = client.get(get_mission_url, params={"name": mission})
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,
@@ -143,15 +143,20 @@ def mission_by_uuid(
143
143
 
144
144
  if json:
145
145
  print(data)
146
- else:
147
- print(f"mission: {data['name']}")
148
- print(f"Creator: {data['creator']['name']}")
149
- print("Project: " + data["project"]["name"])
150
- table = Table("Filename", "Size", "date")
151
- for file in data["files"]:
152
- table.add_row(file["filename"], f"{file['size']}", file["date"])
153
- console = Console()
154
- console.print(table)
146
+ return
147
+ print(f"mission: {data['name']}")
148
+ print(f"Creator: {data['creator']['name']}")
149
+ print("Project: " + data["project"]["name"])
150
+ table = Table("Filename", "Size", "date")
151
+
152
+ if "files" not in data:
153
+ print("No files found for mission.")
154
+ return
155
+
156
+ for file in data["files"]:
157
+ table.add_row(file["filename"], f"{file['size']}", file["date"])
158
+ console = Console()
159
+ console.print(table)
155
160
 
156
161
 
157
162
  @missionCommands.command("download")
@@ -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
- print(f"Failed to create project: {response_text}")
119
- return
120
- print("Project created")
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()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kleinkram
3
- Version: 0.33.0.dev20241024133645
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
@@ -1,20 +1,20 @@
1
1
  kleinkram/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  kleinkram/api_client.py,sha256=1GPsM-XFbPYEKP7RfWmzMTwxRqnVh4wtHVuW25KT8kA,2264
3
3
  kleinkram/consts.py,sha256=pm_6OuQcO-tYcRhwauTtyRRsuYY0y0yb6EGuIl49LnI,50
4
- kleinkram/error_handling.py,sha256=-Ai5xB_aGHDhzOhZyCJWijarSgU0_QJJrrvTFCAK6qI,4644
5
- kleinkram/helper.py,sha256=mhRCztnkJ-Y9udpRKUY92ymVEZTlwzDrfLZkRupDqys,8953
6
- kleinkram/main.py,sha256=cgDTfFQW91wlHZYg4_MmlERcFXDd1z--lUP6bGu8YnM,12536
4
+ kleinkram/error_handling.py,sha256=vvNtXSOnXs4b-aYEUIA0GVQWXLRW5I5FtNE1PZPHEPc,4704
5
+ kleinkram/helper.py,sha256=ELvs-p7zpJUP_rtTIVpcUCTz0D4WegoUZQ11UqUrUkg,9288
6
+ kleinkram/main.py,sha256=Ew4KViP1GkqUZYRtFbZ3HRxlU1TMGnfWJq8dBr0Y8_A,13615
7
7
  kleinkram/auth/auth.py,sha256=w3-TsxWxURzLQ3_p43zgV4Rlh4dVL_WqI6HG2aes-b4,4991
8
- kleinkram/endpoint/endpoint.py,sha256=uez5UrAnP7L5rVHUysA9tFkN3dB3dG1Ojt9g3w-UWuQ,1441
8
+ kleinkram/endpoint/endpoint.py,sha256=WmHUH10_OSZUMrovh2yBeth9dBcn0yE7PxnnD4yPW-Y,1582
9
9
  kleinkram/file/file.py,sha256=gLCZDHHgQWq25OmeG-lwkIh4aRZaLK12xxLkbhZ_m-g,5390
10
- kleinkram/mission/mission.py,sha256=n7W7RKMuJSXXhgFKIGaxX1GyXexnrHUIcTFem6UsEBs,10019
11
- kleinkram/project/project.py,sha256=le85GN9RgrqJeAL5mS-PhowFDjv-HBCYhgkKeFAUcGs,3780
10
+ kleinkram/mission/mission.py,sha256=SkTjauagLamwZaQTWTeGmXAJ9YgUrBdh_3EbjYNZfZM,10082
11
+ kleinkram/project/project.py,sha256=4BOT3iOZZnMrZuALzBxQ5r-8dC8eZkel1OjxUFzK0w4,4336
12
12
  kleinkram/queue/queue.py,sha256=MaLBjAu8asi9BkPvbbT-5AobCcpy3ex5rxM1kHpRINA,181
13
13
  kleinkram/tag/tag.py,sha256=JSHbDPVfsvP34MuQhw__DPQk-Bah5G9BgwYsj_K_JGc,1805
14
14
  kleinkram/topic/topic.py,sha256=IaXhrIHcJ3FSIr0WC-N7u9fkz-lAvSBgQklTX67t0Yc,1641
15
15
  kleinkram/user/user.py,sha256=hDrbWeFPPnh2sswDd445SwcIFGyAbfXXWpYq1VqrK0g,1379
16
- kleinkram-0.33.0.dev20241024133645.dist-info/METADATA,sha256=Wc73pOlVWTIdUE9mmPX5wF6TTUUkEM92R4-3WUBkKsk,845
17
- kleinkram-0.33.0.dev20241024133645.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
18
- kleinkram-0.33.0.dev20241024133645.dist-info/entry_points.txt,sha256=RHXtRzcreVHImatgjhQwZQ6GdJThElYjHEWcR1BPXUI,45
19
- kleinkram-0.33.0.dev20241024133645.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
20
- kleinkram-0.33.0.dev20241024133645.dist-info/RECORD,,
16
+ kleinkram-0.33.0.dev20241028075757.dist-info/METADATA,sha256=SM3kUjRLEUKdAibKkucSiFo4ZQBvo4gC4bQ2vEYApAo,845
17
+ kleinkram-0.33.0.dev20241028075757.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
18
+ kleinkram-0.33.0.dev20241028075757.dist-info/entry_points.txt,sha256=RHXtRzcreVHImatgjhQwZQ6GdJThElYjHEWcR1BPXUI,45
19
+ kleinkram-0.33.0.dev20241028075757.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
20
+ kleinkram-0.33.0.dev20241028075757.dist-info/RECORD,,