kleinkram 0.21.7.dev20240919182750__py3-none-any.whl → 0.21.9.dev20240920112301__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/helper.py CHANGED
@@ -2,7 +2,11 @@ import glob
2
2
  import os
3
3
  import queue
4
4
  import threading
5
+ from datetime import datetime
5
6
  from functools import partial
7
+
8
+ import typer
9
+ from botocore.config import Config
6
10
  from typing_extensions import Dict
7
11
  import boto3
8
12
 
@@ -114,7 +118,12 @@ def uploadFiles(files: Dict[str, str], credentials: Dict[str, str], nrThreads: i
114
118
  minio_endpoint = "http://localhost:9000"
115
119
  else:
116
120
  minio_endpoint = api_endpoint.replace("api", "minio")
117
- s3 = session.resource("s3", endpoint_url=minio_endpoint)
121
+
122
+ config = Config(retries={
123
+ 'max_attempts': 10,
124
+ 'mode': 'standard'
125
+ })
126
+ s3 = session.resource("s3", endpoint_url=minio_endpoint, config=config)
118
127
 
119
128
  _queue = queue.Queue()
120
129
  for file in files.items():
@@ -162,6 +171,44 @@ def uploadFile(_queue: queue.Queue, s3: BaseClient, transferCallback: TransferCa
162
171
  _queue.task_done()
163
172
 
164
173
 
174
+ def canUploadMission(client: AuthenticatedClient, project_uuid: str):
175
+ permissions = client.get("/user/permissions")
176
+ permissions.raise_for_status()
177
+ permissions_json = permissions.json()
178
+ for_project = filter(lambda x: x["uuid"] == project_uuid, permissions_json["projects"])
179
+ max_for_project = max(map(lambda x: x["access"], for_project))
180
+ return max_for_project >= 10
181
+
182
+
183
+ def promptForTags(setTags: Dict[str, str], requiredTags: Dict[str, str]):
184
+ for required_tag in requiredTags:
185
+ if required_tag["name"] not in setTags:
186
+ while True:
187
+ if required_tag["datatype"] in ["LOCATION", "STRING", "LINK"]:
188
+ tag_value = typer.prompt("Provide value for required tag " + required_tag["name"])
189
+ if tag_value != "":
190
+ break
191
+ elif required_tag["datatype"] == "BOOLEAN":
192
+ tag_value = typer.confirm("Provide (y/N) for required tag " + required_tag["name"])
193
+ break
194
+ elif required_tag["datatype"] == "NUMBER":
195
+ tag_value = typer.prompt("Provide number for required tag " + required_tag["name"])
196
+ try:
197
+ tag_value = float(tag_value)
198
+ break
199
+ except ValueError:
200
+ typer.echo("Invalid number format. Please provide a number.")
201
+ elif required_tag["datatype"] == "DATE":
202
+ tag_value = typer.prompt("Provide date for required tag " + required_tag["name"])
203
+ try:
204
+ tag_value = datetime.strptime(tag_value, "%Y-%m-%d %H:%M:%S")
205
+ break
206
+ except ValueError:
207
+ print("Invalid date format. Please use 'YYYY-MM-DD HH:MM:SS'")
208
+
209
+ setTags[required_tag["uuid"]] = tag_value
210
+
211
+
165
212
  if __name__ == "__main__":
166
213
  res = expand_and_match(
167
214
  "~/Downloads/dodo_mission_2024_02_08-20240408T074313Z-003/**.bag"
kleinkram/main.py CHANGED
@@ -9,7 +9,7 @@ from rich import print
9
9
  from rich.table import Table
10
10
  from typer.core import TyperGroup
11
11
  from typer.models import Context
12
- from typing_extensions import Annotated, List
12
+ from typing_extensions import Annotated, List, Optional
13
13
 
14
14
  from kleinkram.api_client import AuthenticatedClient
15
15
  from kleinkram.auth.auth import login, setCliKey, logout
@@ -22,7 +22,7 @@ from kleinkram.queue.queue import queue
22
22
  from kleinkram.tag.tag import tag
23
23
  from kleinkram.topic.topic import topic
24
24
  from kleinkram.user.user import user
25
- from .helper import uploadFiles, expand_and_match
25
+ from .helper import uploadFiles, expand_and_match, canUploadMission, promptForTags
26
26
 
27
27
 
28
28
  class CommandPanel(str, Enum):
@@ -75,14 +75,14 @@ app = ErrorHandledTyper(
75
75
 
76
76
  @app.callback()
77
77
  def version(
78
- version: bool = typer.Option(
79
- None,
80
- "--version",
81
- "-v",
82
- callback=version_callback,
83
- is_eager=True,
84
- help="Print the version and exit",
85
- )
78
+ version: bool = typer.Option(
79
+ None,
80
+ "--version",
81
+ "-v",
82
+ callback=version_callback,
83
+ is_eager=True,
84
+ help="Print the version and exit",
85
+ )
86
86
  ):
87
87
  pass
88
88
 
@@ -109,20 +109,21 @@ def download():
109
109
 
110
110
  @app.command("upload", rich_help_panel=CommandPanel.CoreCommands)
111
111
  def upload(
112
- path: Annotated[
113
- str, typer.Option(prompt=True, help="Path to files to upload, Regex supported")
114
- ],
115
- project: Annotated[str, typer.Option(prompt=True, help="Name of Project")],
116
- mission: Annotated[
117
- str, typer.Option(prompt=True, help="Name of Mission to create")
118
- ],
112
+ path: Annotated[
113
+ str, typer.Option(prompt=True, help="Path to files to upload, Regex supported")
114
+ ],
115
+ project: Annotated[str, typer.Option(prompt=True, help="Name of Project")],
116
+ mission: Annotated[
117
+ str, typer.Option(prompt=True, help="Name of Mission to create")
118
+ ],
119
+ tags: Annotated[Optional[List[str]], typer.Option(prompt=False, help="Tags to add to the mission")] = None,
119
120
  ):
120
121
  """
121
122
  Upload files matching the path to a mission in a project.
122
123
 
123
124
  The mission name must be unique within the project and not yet created.\n
124
125
  Examples:\n
125
- - 'klein upload --path "~/data/**/*.bag" --project "Project 1" --mission "Mission 1"'\n
126
+ - '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
126
127
 
127
128
  """
128
129
  files = expand_and_match(path)
@@ -147,7 +148,6 @@ def upload(
147
148
  get_project_url = "/project/byName"
148
149
  project_response = client.get(get_project_url, params={"name": project})
149
150
  if project_response.status_code >= 400:
150
-
151
151
  raise AccessDeniedException(
152
152
  f"The project '{project}' does not exist or you do not have access to it.\n"
153
153
  f"Consider using the following command to create a project: 'klein project create'\n",
@@ -159,6 +159,17 @@ def upload(
159
159
  print(f"Project not found: '{project}'")
160
160
  return
161
161
 
162
+ can_upload = canUploadMission(client, project_json['uuid'])
163
+ if not can_upload:
164
+ raise AccessDeniedException(
165
+ f"You do not have the required permissions to upload to project '{project}'\n", "Access Denied")
166
+
167
+ if not tags:
168
+ tags = []
169
+ tags_dict = {item.split(":")[0]: item.split(":")[1] for item in tags}
170
+
171
+ promptForTags(tags_dict, project_json["requiredTags"])
172
+
162
173
  get_mission_url = "/mission/byName"
163
174
  mission_response = client.get(get_mission_url, params={"name": mission})
164
175
  mission_response.raise_for_status()
@@ -174,18 +185,23 @@ def upload(
174
185
  create_mission_url = "/mission/create"
175
186
  new_mission = client.post(
176
187
  create_mission_url,
177
- json={"name": mission, "projectUUID": project_json["uuid"], "tags": []},
188
+ json={"name": mission, "projectUUID": project_json["uuid"], "tags": tags_dict},
178
189
  )
179
- new_mission.raise_for_status()
190
+ if new_mission.status_code >= 400:
191
+ raise ValueError(
192
+ "Failed to create mission. Status Code: " + str(new_mission.status_code) + "\n" + new_mission.json()[
193
+ "message"])
180
194
  new_mission_data = new_mission.json()
181
195
 
182
196
  get_temporary_credentials = "/file/temporaryAccess"
183
-
184
197
  response_2 = client.post(
185
198
  get_temporary_credentials,
186
199
  json={"filenames": filenames, "missionUUID": new_mission_data["uuid"]},
187
200
  )
188
- response_2.raise_for_status()
201
+ if response_2.status_code >= 400:
202
+ raise ValueError(
203
+ "Failed to get temporary credentials. Status Code: " + str(response_2.status_code) + "\n" + response_2.json()[
204
+ "message"])
189
205
  temp_credentials = response_2.json()
190
206
  credential = temp_credentials["credentials"]
191
207
  confirmed_files = temp_credentials["files"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kleinkram
3
- Version: 0.21.7.dev20240919182750
3
+ Version: 0.21.9.dev20240920112301
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
@@ -2,8 +2,8 @@ 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
4
  kleinkram/error_handling.py,sha256=Nm3tUtc4blQylJLNChSLgkDLFDHg3Xza0CghRrd_SYE,4015
5
- kleinkram/helper.py,sha256=nq4LGn-dcRd7y69XnWl2Xz36lTcTaWWe1PZwqGWeOVQ,5590
6
- kleinkram/main.py,sha256=v3wWCIn-4CxKwcf9mq3UnkqIR1XvbHzEjrX9Ri06-hk,8123
5
+ kleinkram/helper.py,sha256=1DoQpJsYF1tWjUIwBPLkorxtFiaewOr2ipcnFWPM1nA,7673
6
+ kleinkram/main.py,sha256=tArawTfNU88vL7ClW4xs8bXDjBviTZ7Gr9hRKzVuWuk,9263
7
7
  kleinkram/auth/auth.py,sha256=w3-TsxWxURzLQ3_p43zgV4Rlh4dVL_WqI6HG2aes-b4,4991
8
8
  kleinkram/endpoint/endpoint.py,sha256=neyW1lR7RNiRj8nwCl77StQrofwRCbnCY0mqLJkqW84,1419
9
9
  kleinkram/file/file.py,sha256=8CCYu9MdG24Rh0aEcNB3OEMUzpLMSoV4VIhdeq2n8iA,3206
@@ -13,8 +13,8 @@ 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.21.7.dev20240919182750.dist-info/METADATA,sha256=jI-c3NFKlZnW7hVWD8R9pRBMClOtH_tucyI0F8pdZ9E,845
17
- kleinkram-0.21.7.dev20240919182750.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
18
- kleinkram-0.21.7.dev20240919182750.dist-info/entry_points.txt,sha256=RHXtRzcreVHImatgjhQwZQ6GdJThElYjHEWcR1BPXUI,45
19
- kleinkram-0.21.7.dev20240919182750.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
20
- kleinkram-0.21.7.dev20240919182750.dist-info/RECORD,,
16
+ kleinkram-0.21.9.dev20240920112301.dist-info/METADATA,sha256=k8Nutmz8CqMktCt62m7IwBfioE8j4cnRlWqA9EXXt2M,845
17
+ kleinkram-0.21.9.dev20240920112301.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
18
+ kleinkram-0.21.9.dev20240920112301.dist-info/entry_points.txt,sha256=RHXtRzcreVHImatgjhQwZQ6GdJThElYjHEWcR1BPXUI,45
19
+ kleinkram-0.21.9.dev20240920112301.dist-info/licenses/LICENSE,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
20
+ kleinkram-0.21.9.dev20240920112301.dist-info/RECORD,,