remotivelabs-cli 0.0.20__tar.gz → 0.0.22__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.
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/PKG-INFO +1 -1
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/recordings.py +84 -27
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/recordings_playback.py +4 -3
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/rest_helper.py +1 -1
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/pyproject.toml +1 -1
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/LICENSE +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/README.md +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/__about__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/__init__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/brokers.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/export.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/files.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/lib/__about__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/lib/broker.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/license_flows.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/licenses.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/playback.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/record.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/scripting.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/broker/signals.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/__init__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/auth.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/auth_tokens.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/brokers.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/cloud_cli.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/configs.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/projects.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/sample_recordings.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/service_account_tokens.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/cloud/service_accounts.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/connect/__init__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/connect/connect.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/connect/protopie/protopie.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/errors.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/remotive.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/requirements.txt +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/settings.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/tools/__init__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/tools/can/__init__.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/tools/can/can.py +0 -0
- {remotivelabs_cli-0.0.20 → remotivelabs_cli-0.0.22}/cli/tools/tools.py +0 -0
@@ -6,7 +6,10 @@ import os
|
|
6
6
|
import re
|
7
7
|
import sys
|
8
8
|
import tempfile
|
9
|
+
import time
|
10
|
+
import urllib.parse
|
9
11
|
from pathlib import Path
|
12
|
+
from typing import Dict, List, Union
|
10
13
|
|
11
14
|
import grpc
|
12
15
|
import requests
|
@@ -193,7 +196,7 @@ def delete_recording_file(
|
|
193
196
|
|
194
197
|
|
195
198
|
@app.command()
|
196
|
-
def upload(
|
199
|
+
def upload( # noqa: C901
|
197
200
|
path: Path = typer.Argument(
|
198
201
|
...,
|
199
202
|
exists=True,
|
@@ -205,38 +208,86 @@ def upload(
|
|
205
208
|
help="Path to recording file to upload",
|
206
209
|
),
|
207
210
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
208
|
-
|
209
|
-
signal_database: str = typer.Option(None, help="Signal database to use with candump"),
|
211
|
+
recording_session: str = typer.Option(default=None, help="Optional existing recording to upload file to"),
|
210
212
|
):
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
exit(1)
|
213
|
+
"""
|
214
|
+
Uploads a recording to RemotiveCloud.
|
215
|
+
Except for recordings from RemotiveBroker you can also upload Can ASC (.asc), Can BLF(.blf) and Can LOG (.log, .txt)
|
216
|
+
"""
|
216
217
|
|
217
218
|
filename = os.path.basename(path.name)
|
218
219
|
rest.ensure_auth_token()
|
219
220
|
|
220
|
-
if
|
221
|
-
rest.headers["x-recording-type"] = "candump"
|
222
|
-
r = rest.handle_post(
|
223
|
-
url=f"/api/project/{project}/files/recording/{filename}",
|
224
|
-
return_response=True,
|
225
|
-
progress_label="Requesting recording upload...",
|
226
|
-
body=json.dumps({"dbcFile": signal_database}),
|
227
|
-
)
|
228
|
-
else:
|
221
|
+
if recording_session is None:
|
229
222
|
r = rest.handle_post(f"/api/project/{project}/files/recording/{filename}", return_response=True)
|
223
|
+
else:
|
224
|
+
r = rest.handle_post(f"/api/project/{project}/files/recording/{recording_session}/recording-file/{filename}", return_response=True)
|
225
|
+
|
226
|
+
upload_url = r.text
|
227
|
+
url_path = urllib.parse.urlparse(upload_url).path
|
228
|
+
# Upload_id is the first part of the path
|
229
|
+
match = re.match(r"^/([^/]+)/organisation/(.*)$", url_path)
|
230
|
+
if match:
|
231
|
+
upload_id = match.group(1)
|
232
|
+
else:
|
233
|
+
ErrorPrinter.print_generic_error(
|
234
|
+
"Something went wrong, please try again. Please contact RemotiveLabs support if this problem remains"
|
235
|
+
)
|
236
|
+
ErrorPrinter.print_hint("Please make sure to use the latest version of RemotiveCLI")
|
237
|
+
exit(1)
|
230
238
|
|
231
|
-
|
239
|
+
upload_response = rest.upload_file_with_signed_url(
|
240
|
+
path=path, url=upload_url, upload_headers={"Content-Type": "application/x-www-form-urlencoded"}, return_response=True
|
241
|
+
)
|
242
|
+
|
243
|
+
# Exact same as in cloud console
|
244
|
+
def get_processing_message(step: str) -> str:
|
245
|
+
if step == "REQUESTED":
|
246
|
+
return "Preparing file..."
|
247
|
+
if step == "VALIDATING":
|
248
|
+
return "Validating file..."
|
249
|
+
if step == "CONVERT":
|
250
|
+
return "Converting file..."
|
251
|
+
if step == "SPLIT":
|
252
|
+
return "Splitting file..."
|
253
|
+
if step == "ZIP":
|
254
|
+
return "Compressing file..."
|
255
|
+
if step == "FINALIZE":
|
256
|
+
return "Finishing up..."
|
257
|
+
return "Processing..."
|
258
|
+
|
259
|
+
# print(response)
|
260
|
+
if 200 <= upload_response.status_code < 300:
|
261
|
+
# We need to print the error message outside the with Progress so the indicator is closed
|
262
|
+
error_message: Union[str, None] = None
|
263
|
+
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True) as p:
|
264
|
+
t = p.add_task("Processing...", total=1)
|
265
|
+
while True:
|
266
|
+
time.sleep(1)
|
267
|
+
r = rest.handle_get(
|
268
|
+
f"/api/project/{project}/files/recording/processing", return_response=True, use_progress_indicator=False
|
269
|
+
)
|
270
|
+
status_list: List[Dict[str, any]] = r.json()
|
271
|
+
res = list(filter(lambda s: s["uploadId"] == upload_id, status_list))
|
272
|
+
if len(res) == 1:
|
273
|
+
tracking_state = res[0]
|
274
|
+
if tracking_state["status"] != "FAILED" and tracking_state["status"] != "SUCCESS":
|
275
|
+
p.update(task_id=t, description=get_processing_message(tracking_state["step"]))
|
276
|
+
else:
|
277
|
+
if tracking_state["status"] == "FAILED":
|
278
|
+
error_message = f"Processing of uploaded file failed: {tracking_state['errors'][0]['message']}"
|
279
|
+
else:
|
280
|
+
print("File successfully uploaded")
|
281
|
+
break
|
282
|
+
else:
|
283
|
+
error_message = "Something went wrong, please try again. Please contact RemotiveLabs support if this problem remains"
|
284
|
+
break
|
285
|
+
if error_message is not None:
|
286
|
+
ErrorPrinter.print_generic_error(error_message)
|
287
|
+
exit(1)
|
232
288
|
|
233
|
-
if 200 <= response.status_code < 300:
|
234
|
-
print(
|
235
|
-
"File successfully uploaded, please run 'remotive cloud recordings list' "
|
236
|
-
"to verify that the recording was successfully processed"
|
237
|
-
)
|
238
289
|
else:
|
239
|
-
rest.err_console.print(f":boom: [bold red]Got status code[/bold red]: {
|
290
|
+
rest.err_console.print(f":boom: [bold red]Got status code[/bold red]: {upload_response.status_code} {upload_response.text}")
|
240
291
|
|
241
292
|
|
242
293
|
@app.command()
|
@@ -450,9 +501,9 @@ def _do_change_playback_mode(mode: str, recording_session: str, broker: str, pro
|
|
450
501
|
# Here we try to verify that we are operating on a recording that is mounted on the
|
451
502
|
# broker so we can verify this before we try playback and can also present some good
|
452
503
|
# error messages
|
453
|
-
tmp = tempfile.
|
454
|
-
broker.download(".cloud.context", tmp
|
455
|
-
with open(tmp
|
504
|
+
tmp = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
|
505
|
+
broker.download(".cloud.context", tmp, True)
|
506
|
+
with open(tmp, "r") as f:
|
456
507
|
json_context = json.loads(f.read())
|
457
508
|
if json_context["recordingSessionId"] != recording_session:
|
458
509
|
ErrorPrinter.print_generic_error(
|
@@ -496,3 +547,9 @@ def get_filename_from_cd(cd):
|
|
496
547
|
if len(fname) == 0:
|
497
548
|
return None
|
498
549
|
return fname[0]
|
550
|
+
|
551
|
+
|
552
|
+
def use_progress(label: str):
|
553
|
+
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
554
|
+
t = p.add_task(label, total=1)
|
555
|
+
return (p, t)
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import datetime
|
4
4
|
import json
|
5
|
+
import os
|
5
6
|
import tempfile
|
6
7
|
from pathlib import Path
|
7
8
|
from typing import List, Union
|
@@ -223,9 +224,9 @@ def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: st
|
|
223
224
|
# Here we try to verify that we are operating on a recording that is mounted on the
|
224
225
|
# broker so we can verify this before we try playback and can also present some good
|
225
226
|
# error messages
|
226
|
-
tmp = tempfile.
|
227
|
-
broker.download(".cloud.context", tmp
|
228
|
-
with open(tmp
|
227
|
+
tmp = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
|
228
|
+
broker.download(".cloud.context", tmp, True)
|
229
|
+
with open(tmp, "r") as f:
|
229
230
|
json_context = json.loads(f.read())
|
230
231
|
if json_context["recordingSessionId"] != recording_session:
|
231
232
|
ErrorPrinter.print_generic_error(
|
@@ -185,7 +185,7 @@ def upload_file_with_signed_url(
|
|
185
185
|
path: Union[str, Path], url: str, upload_headers: dict[str, str], return_response: bool = False, progress_label="Uploading..."
|
186
186
|
):
|
187
187
|
with open(path, "rb") as file:
|
188
|
-
with wrap_file(file, os.stat(path).st_size, description=progress_label) as f:
|
188
|
+
with wrap_file(file, os.stat(path).st_size, description=progress_label, transient=True) as f:
|
189
189
|
r = requests.put(url, data=f, headers=upload_headers)
|
190
190
|
if return_response:
|
191
191
|
check_api_result(r)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|