kleinkram 0.0.120__tar.gz → 0.0.121.dev20240731104925__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kleinkram
3
- Version: 0.0.120
3
+ Version: 0.0.121.dev20240731104925
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,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kleinkram"
7
- version = "0.0.120"
7
+ version = "0.0.121-dev20240731104925"
8
8
  authors = [
9
9
  { name="Johann Schwabe", email="jschwab@ethz.ch" },
10
10
  ]
@@ -94,7 +94,9 @@ class AuthenticatedClient(httpx.Client):
94
94
  self.tokenfile = TokenFile()
95
95
  self._load_cookies()
96
96
  except Exception as e:
97
- print(f"{self.tokenfile.endpoint} is not authenticated. Please run 'klein login'.")
97
+ print(
98
+ f"{self.tokenfile.endpoint} is not authenticated. Please run 'klein login'."
99
+ )
98
100
 
99
101
  def _load_cookies(self):
100
102
  if self.tokenfile.isCliToken():
@@ -110,7 +112,10 @@ class AuthenticatedClient(httpx.Client):
110
112
  if not refresh_token:
111
113
  print("No refresh token found. Please login again.")
112
114
  raise Exception("No refresh token found.")
113
- self.cookies.set(REFRESH_TOKEN, refresh_token, )
115
+ self.cookies.set(
116
+ REFRESH_TOKEN,
117
+ refresh_token,
118
+ )
114
119
  response = self.post(
115
120
  "/auth/refresh-token",
116
121
  )
@@ -124,16 +129,16 @@ class AuthenticatedClient(httpx.Client):
124
129
  response = super().request(
125
130
  method, self.tokenfile.endpoint + url, *args, **kwargs
126
131
  )
127
- if (
128
- url == "/auth/refresh-token"
129
- ) and response.status_code == 401:
132
+ if (url == "/auth/refresh-token") and response.status_code == 401:
130
133
  print("Refresh token expired. Please login again.")
131
134
  response.status_code = 403
132
135
  exit(1)
133
136
  if response.status_code == 401:
134
137
  print("Token expired, refreshing token...")
135
138
  self.refresh_token()
136
- response = super().request(method, self.tokenfile.endpoint + url, *args, **kwargs)
139
+ response = super().request(
140
+ method, self.tokenfile.endpoint + url, *args, **kwargs
141
+ )
137
142
  return response
138
143
 
139
144
 
@@ -141,8 +146,10 @@ client = AuthenticatedClient()
141
146
 
142
147
 
143
148
  def login(
144
- key: Optional[str] = typer.Option(None, help="CLI Key", hidden=True),
145
- open_browser: Optional[bool] = typer.Option(True, help="Open browser for authentication"),
149
+ key: Optional[str] = typer.Option(None, help="CLI Key", hidden=True),
150
+ open_browser: Optional[bool] = typer.Option(
151
+ True, help="Open browser for authentication"
152
+ ),
146
153
  ):
147
154
  """
148
155
  Login into the currently set endpoint.\n
@@ -175,12 +182,16 @@ def login(
175
182
 
176
183
  return
177
184
 
178
- print(f"Please open the following URL manually in your browser to authenticate: {url + '-no-redirect'}")
185
+ print(
186
+ f"Please open the following URL manually in your browser to authenticate: {url + '-no-redirect'}"
187
+ )
179
188
  print("Enter the authentication token provided after logging in:")
180
189
  manual_auth_token = input("Authentication Token: ")
181
190
  manual_refresh_token = input("Refresh Token: ")
182
191
  if manual_auth_token:
183
- tokenfile.saveTokens({AUTH_TOKEN: manual_auth_token, REFRESH_TOKEN: manual_refresh_token})
192
+ tokenfile.saveTokens(
193
+ {AUTH_TOKEN: manual_auth_token, REFRESH_TOKEN: manual_refresh_token}
194
+ )
184
195
  print("Authentication complete. Tokens saved to tokens.json.")
185
196
  else:
186
197
  print("No authentication token provided.")
@@ -188,7 +199,7 @@ def login(
188
199
 
189
200
 
190
201
  def setEndpoint(
191
- endpoint: Optional[str] = typer.Argument(None, help="API endpoint to use")
202
+ endpoint: Optional[str] = typer.Argument(None, help="API endpoint to use")
192
203
  ):
193
204
  """
194
205
  Set the current endpoint
@@ -220,9 +231,7 @@ def endpoint():
220
231
  print("- " + _endpoint)
221
232
 
222
233
 
223
- def setCliKey(
224
- key: Annotated[str, typer.Argument(help="CLI Key")]
225
- ):
234
+ def setCliKey(key: Annotated[str, typer.Argument(help="CLI Key")]):
226
235
  """
227
236
  Set the CLI key (Actions Only)
228
237
 
@@ -0,0 +1 @@
1
+ API_URL='https://api.datasets.leggedrobotics.com'
@@ -56,7 +56,9 @@ def uploadFile(_queue: queue.Queue, paths: Dict[str, str], pbar: tqdm):
56
56
  pbar.update(100) # Update progress for each file
57
57
  client.post("/queue/confirmUpload", json={"uuid": uuid})
58
58
  else:
59
- print(f"Failed to upload {filename}. HTTP status: {response.status_code}")
59
+ print(
60
+ f"Failed to upload {filename}. HTTP status: {response.status_code}"
61
+ )
60
62
  _queue.task_done()
61
63
  except queue.Empty:
62
64
  break
@@ -18,7 +18,7 @@ projects = typer.Typer(name="projects", help="Project operations")
18
18
  missions = typer.Typer(name="missions", help="Mission operations")
19
19
  files = typer.Typer(name="files", help="File operations")
20
20
  topics = typer.Typer(name="topics", help="Topic operations")
21
- queue = typer.Typer(name="queue", help="Queue operations")
21
+ queue = typer.Typer(name="queue", help="Status of files uploading")
22
22
  user = typer.Typer(name="users", help="User operations")
23
23
  tagtypes = typer.Typer(name="tagtypes", help="TagType operations")
24
24
  tag = typer.Typer(name="tag", help="Tag operations")
@@ -40,9 +40,11 @@ app.command(hidden=True)(setCliKey)
40
40
 
41
41
  @files.command("list")
42
42
  def list_files(
43
- project: Optional[str] = typer.Option(None, help="Name of Project"),
44
- mission: Optional[str] = typer.Option(None, help="Name of Mission"),
45
- topics: Optional[List[str]] = typer.Option(None, help="Comma separated list of topics")
43
+ project: Optional[str] = typer.Option(None, help="Name of Project"),
44
+ mission: Optional[str] = typer.Option(None, help="Name of Mission"),
45
+ topics: Optional[List[str]] = typer.Option(
46
+ None, help="Comma separated list of topics"
47
+ ),
46
48
  ):
47
49
  """
48
50
  List all files with optional filters for project, mission, or topics.
@@ -115,12 +117,13 @@ def list_projects():
115
117
 
116
118
  @missions.command("list")
117
119
  def list_missions(
118
- project: Optional[str] = typer.Option(None, help="Name of Project"),
119
- verbose: Optional[bool] = typer.Option(False, help="Outputs a table with more information"),
120
+ project: Optional[str] = typer.Option(None, help="Name of Project"),
121
+ verbose: Optional[bool] = typer.Option(
122
+ False, help="Outputs a table with more information"
123
+ ),
120
124
  ):
121
125
  """
122
126
  List all missions with optional filter for project.
123
-
124
127
  """
125
128
  try:
126
129
  url = "/mission"
@@ -141,7 +144,9 @@ def list_missions(
141
144
  print("missions by Project:")
142
145
  if not verbose:
143
146
  for project_uuid, missions in missions_by_project_uuid.items():
144
- print(f"* {missions_by_project_uuid[project_uuid][0]['project']['name']}")
147
+ print(
148
+ f"* {missions_by_project_uuid[project_uuid][0]['project']['name']}"
149
+ )
145
150
  for mission in missions:
146
151
  print(f" - {mission['name']}")
147
152
  else:
@@ -163,8 +168,8 @@ def list_missions(
163
168
 
164
169
  @missions.command("byUUID")
165
170
  def mission_by_uuid(
166
- uuid: Annotated[str, typer.Argument()],
167
- json: Optional[bool] = typer.Option(False, help="Output as JSON"),
171
+ uuid: Annotated[str, typer.Argument()],
172
+ json: Optional[bool] = typer.Option(False, help="Output as JSON"),
168
173
  ):
169
174
  """
170
175
  Get mission name, project name, creator and table of its files given a Mission UUID
@@ -194,14 +199,16 @@ def mission_by_uuid(
194
199
 
195
200
  @topics.command("list")
196
201
  def topics(
197
- file: Annotated[str, typer.Option(help="Name of File")],
198
- full: Annotated[bool, typer.Option(help="As a table with additional parameters")] = False,
199
- # Todo add mission / project as optional argument as filenames are not unique
202
+ file: Annotated[str, typer.Option(help="Name of File")],
203
+ full: Annotated[
204
+ bool, typer.Option(help="As a table with additional parameters")
205
+ ] = False,
206
+ # Todo add mission / project as optional argument as filenames are not unique or handle multiple files
200
207
  ):
201
208
  """
202
209
  List topics for a file
203
210
 
204
- Only makes sense with MCAP files as we don't associate topics with BAGs.
211
+ Only makes sense with MCAP files as we don't associate topics with BAGs as that would be redundant.
205
212
  """
206
213
  if file.endswith(".bag"):
207
214
  print("BAG files generally do not have topics")
@@ -231,15 +238,18 @@ def topics(
231
238
 
232
239
  @projects.command("create")
233
240
  def create_project(
234
- name: Annotated[str, typer.Option(help="Name of Project")],
235
- description: Annotated[str, typer.Option(help="Description of Project")],
241
+ name: Annotated[str, typer.Option(help="Name of Project")],
242
+ description: Annotated[str, typer.Option(help="Description of Project")],
236
243
  ):
237
244
  """
238
245
  Create a new project
239
246
  """
247
+ #Todo add required tags as option.
240
248
  try:
241
249
  url = "/project/create"
242
- response = client.post(url, json={"name": name, "description": description, "requiredTags": []}) # TODO: Add required tags as option
250
+ response = client.post(
251
+ url, json={"name": name, "description": description, "requiredTags": []}
252
+ ) # TODO: Add required tags as option
243
253
  if response.status_code >= 400:
244
254
  response_json = response.json()
245
255
  response_text = response_json["message"]
@@ -253,9 +263,13 @@ def create_project(
253
263
 
254
264
  @app.command("upload")
255
265
  def upload(
256
- path: Annotated[str, typer.Option(prompt=True, help="Path to files to upload, Regex supported")],
257
- project: Annotated[str, typer.Option(prompt=True, help="Name of Project")],
258
- mission: Annotated[str, typer.Option(prompt=True, help="Name of Mission to create")],
266
+ path: Annotated[
267
+ str, typer.Option(prompt=True, help="Path to files to upload, Regex supported")
268
+ ],
269
+ project: Annotated[str, typer.Option(prompt=True, help="Name of Project")],
270
+ mission: Annotated[
271
+ str, typer.Option(prompt=True, help="Name of Mission to create")
272
+ ],
259
273
  ):
260
274
  """
261
275
  Upload files matching the path to a mission in a project.
@@ -266,7 +280,9 @@ def upload(
266
280
 
267
281
  """
268
282
  files = expand_and_match(path)
269
- filenames = list(map(lambda x: x.split("/")[-1], filter(lambda x: not os.path.isdir(x),files)))
283
+ filenames = list(
284
+ map(lambda x: x.split("/")[-1], filter(lambda x: not os.path.isdir(x), files))
285
+ )
270
286
  if not filenames:
271
287
  print("No files found")
272
288
  return
@@ -301,7 +317,8 @@ def upload(
301
317
 
302
318
  create_mission_url = "/mission/create"
303
319
  new_mission = client.post(
304
- create_mission_url, json={"name": mission, "projectUUID": project_json["uuid"], "tags": []}
320
+ create_mission_url,
321
+ json={"name": mission, "projectUUID": project_json["uuid"], "tags": []},
305
322
  )
306
323
  new_mission.raise_for_status()
307
324
  new_mission_data = new_mission.json()
@@ -467,7 +484,7 @@ def demote(email: Annotated[str, typer.Option()]):
467
484
 
468
485
  @files.command("download")
469
486
  def download(
470
- missionuuid: Annotated[str, typer.Argument()],
487
+ missionuuid: Annotated[str, typer.Argument()],
471
488
  ):
472
489
  """Download file"""
473
490
  try:
@@ -477,15 +494,19 @@ def download(
477
494
  except:
478
495
  print("Failed to download file")
479
496
 
480
- @missions.command('tag')
497
+
498
+ @missions.command("tag")
481
499
  def addTag(
482
- missionuuid: Annotated[str, typer.Argument()],
483
- tagtypeuuid: Annotated[str, typer.Argument()],
484
- value: Annotated[str, typer.Argument()],
500
+ missionuuid: Annotated[str, typer.Argument()],
501
+ tagtypeuuid: Annotated[str, typer.Argument()],
502
+ value: Annotated[str, typer.Argument()],
485
503
  ):
486
504
  """Tag a mission"""
487
505
  try:
488
- response = client.post("/tag/addTag", json={"mission": missionuuid, "tagType": tagtypeuuid, "value": value})
506
+ response = client.post(
507
+ "/tag/addTag",
508
+ json={"mission": missionuuid, "tagType": tagtypeuuid, "value": value},
509
+ )
489
510
  if response.status_code < 400:
490
511
  print("Tagged mission")
491
512
  else:
@@ -497,9 +518,9 @@ def addTag(
497
518
  sys.exit(1)
498
519
 
499
520
 
500
- @tagtypes.command('list')
521
+ @tagtypes.command("list")
501
522
  def tagTypes(
502
- verbose: Annotated[bool, typer.Option()] = False,
523
+ verbose: Annotated[bool, typer.Option()] = False,
503
524
  ):
504
525
  """List all tagtypes"""
505
526
  try:
@@ -507,7 +528,7 @@ def tagTypes(
507
528
  response.raise_for_status()
508
529
  data = response.json()
509
530
  if verbose:
510
- table = Table("UUID","Name", "Datatype")
531
+ table = Table("UUID", "Name", "Datatype")
511
532
  for tagtype in data:
512
533
  table.add_row(tagtype["uuid"], tagtype["name"], tagtype["datatype"])
513
534
  else:
@@ -518,9 +539,10 @@ def tagTypes(
518
539
  except:
519
540
  print("Failed to fetch tagtypes")
520
541
 
521
- @tag.command('delete')
542
+
543
+ @tag.command("delete")
522
544
  def deleteTag(
523
- taguuid: Annotated[str, typer.Argument()],
545
+ taguuid: Annotated[str, typer.Argument()],
524
546
  ):
525
547
  """Delete a tag"""
526
548
  try:
@@ -531,8 +553,7 @@ def deleteTag(
531
553
  print(response)
532
554
  print("Failed to delete tag")
533
555
  except:
534
- print("Failed to delete tag"
535
- )
556
+ print("Failed to delete tag")
536
557
 
537
558
 
538
559
  if __name__ == "__main__":
@@ -1,2 +0,0 @@
1
- API_URL = "http://localhost:3000"
2
- # API_URL = "https://api.datasets.leggedrobotics.com"