cloudos-cli 2.70.1__tar.gz → 2.72.0__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.
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/PKG-INFO +1 -1
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/__main__.py +47 -4
- cloudos_cli-2.72.0/cloudos_cli/_version.py +1 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/jobs/job.py +20 -8
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/details.py +8 -1
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/PKG-INFO +1 -1
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/SOURCES.txt +0 -2
- cloudos_cli-2.70.1/cloudos_cli/_version.py +0 -1
- cloudos_cli-2.70.1/cloudos_cli/delete/__init__.py +0 -8
- cloudos_cli-2.70.1/cloudos_cli/delete/results.py +0 -24
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/LICENSE +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/README.md +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/clos.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/configure/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/configure/configure.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/cost/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/cost/cost.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/datasets/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/datasets/datasets.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/import_wf/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/import_wf/import_wf.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/jobs/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/link/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/link/link.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/logging/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/logging/logger.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/procurement/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/procurement/images.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/queue/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/queue/queue.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/related_analyses/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/related_analyses/related_analyses.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/array_job.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/cloud.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/errors.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/last_wf.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/requests.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/resources.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/requires.txt +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/top_level.txt +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/setup.cfg +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/setup.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/functions_for_pytest.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cli_project_create.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cost/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cost/test_job_cost.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_logging/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_logging/test_logger.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_related_analyses/__init__.py +0 -0
- {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_related_analyses/test_related_analyses.py +0 -0
|
@@ -31,7 +31,6 @@ from cloudos_cli.configure.configure import (
|
|
|
31
31
|
CLOUDOS_URL
|
|
32
32
|
)
|
|
33
33
|
from cloudos_cli.related_analyses.related_analyses import related_analyses
|
|
34
|
-
from cloudos_cli.delete.results import delete_job_results
|
|
35
34
|
|
|
36
35
|
|
|
37
36
|
# GLOBAL VARS
|
|
@@ -727,6 +726,12 @@ def job_status(ctx,
|
|
|
727
726
|
@click.option('--link',
|
|
728
727
|
help='Link the working directory to an interactive session.',
|
|
729
728
|
is_flag=True)
|
|
729
|
+
@click.option('--delete',
|
|
730
|
+
help='Delete the results directory of a CloudOS job.',
|
|
731
|
+
is_flag=True)
|
|
732
|
+
@click.option('-y', '--yes',
|
|
733
|
+
help='Skip confirmation prompt when deleting results.',
|
|
734
|
+
is_flag=True)
|
|
730
735
|
@click.option('--session-id',
|
|
731
736
|
help='The specific CloudOS interactive session id. Required when using --link flag.',
|
|
732
737
|
required=False)
|
|
@@ -751,6 +756,8 @@ def job_workdir(ctx,
|
|
|
751
756
|
workspace_id,
|
|
752
757
|
job_id,
|
|
753
758
|
link,
|
|
759
|
+
delete,
|
|
760
|
+
yes,
|
|
754
761
|
session_id,
|
|
755
762
|
status,
|
|
756
763
|
verbose,
|
|
@@ -886,6 +893,39 @@ def job_workdir(ctx,
|
|
|
886
893
|
except Exception as e:
|
|
887
894
|
raise ValueError(f"Failed to retrieve working directory for job '{job_id}': {str(e)}")
|
|
888
895
|
|
|
896
|
+
# Delete workdir directory if requested
|
|
897
|
+
if delete:
|
|
898
|
+
try:
|
|
899
|
+
# Ask for confirmation unless --yes flag is provided
|
|
900
|
+
if not yes:
|
|
901
|
+
confirmation_message = (
|
|
902
|
+
"\n⚠️ Deleting intermediate results is permanent and cannot be undone. "
|
|
903
|
+
"All associated data will be permanently removed and cannot be recovered. "
|
|
904
|
+
"The current job, as well as any other jobs sharing the same working directory, "
|
|
905
|
+
"will no longer be resumable. This action will be logged in the audit trail "
|
|
906
|
+
"(if auditing is enabled for your organisation), and you will be recorded as "
|
|
907
|
+
"the user who performed the deletion. You can skip this confirmation step by "
|
|
908
|
+
"providing -y or --yes flag to cloudos job workdir --delete. Please confirm "
|
|
909
|
+
"that you want to delete intermediate results of this analysis? [y/n] "
|
|
910
|
+
)
|
|
911
|
+
click.secho(confirmation_message, fg='black', bg='yellow')
|
|
912
|
+
user_input = input().strip().lower()
|
|
913
|
+
if user_input != 'y':
|
|
914
|
+
print('\nDeletion cancelled.')
|
|
915
|
+
return
|
|
916
|
+
# Proceed with deletion
|
|
917
|
+
job = jb.Job(cloudos_url, apikey, None, workspace_id, None, None, workflow_id=1234, project_id="None",
|
|
918
|
+
mainfile=None, importsfile=None, verify=verify_ssl)
|
|
919
|
+
job.delete_job_results(job_id, "workDirectory", verify=verify_ssl)
|
|
920
|
+
click.secho('\nIntermediate results directories deleted successfully.', fg='green', bold=True)
|
|
921
|
+
except BadRequestException as e:
|
|
922
|
+
raise ValueError(f"Job '{job_id}' not found or not accessible: {str(e)}")
|
|
923
|
+
except Exception as e:
|
|
924
|
+
raise ValueError(f"Failed to retrieve intermediate results for job '{job_id}': {str(e)}")
|
|
925
|
+
else:
|
|
926
|
+
if yes:
|
|
927
|
+
click.secho("\n'--yes' flag is ignored when '--delete' is not specified.", fg='yellow', bold=True)
|
|
928
|
+
|
|
889
929
|
|
|
890
930
|
@job.command('logs')
|
|
891
931
|
@click.option('-k',
|
|
@@ -1198,8 +1238,11 @@ def job_results(ctx,
|
|
|
1198
1238
|
return
|
|
1199
1239
|
if verbose:
|
|
1200
1240
|
print(f'\nDeleting {len(results)} result directories from CloudOS...')
|
|
1201
|
-
|
|
1202
|
-
|
|
1241
|
+
# Proceed with deletion
|
|
1242
|
+
job = jb.Job(cloudos_url, apikey, None, workspace_id, None, None, workflow_id=1234, project_id="None",
|
|
1243
|
+
mainfile=None, importsfile=None, verify=verify_ssl)
|
|
1244
|
+
job.delete_job_results(job_id, "analysisResults", verify=verify_ssl)
|
|
1245
|
+
click.secho('\nResults directories deleted successfully.', fg='green', bold=True)
|
|
1203
1246
|
else:
|
|
1204
1247
|
if yes:
|
|
1205
1248
|
click.secho("\n'--yes' flag is ignored when '--delete' is not specified.", fg='yellow', bold=True)
|
|
@@ -1288,7 +1331,7 @@ def job_details(ctx,
|
|
|
1288
1331
|
raise ValueError(f"Job '{job_id}' not found or not accessible: {str(e)}")
|
|
1289
1332
|
except Exception as e:
|
|
1290
1333
|
raise ValueError(f"Failed to retrieve details for job '{job_id}': {str(e)}")
|
|
1291
|
-
create_job_details(json.loads(j_details.content), job_id, output_format, output_basename, parameters)
|
|
1334
|
+
create_job_details(json.loads(j_details.content), job_id, output_format, output_basename, parameters, cloudos_url)
|
|
1292
1335
|
|
|
1293
1336
|
|
|
1294
1337
|
@job.command('list')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.72.0'
|
|
@@ -1428,14 +1428,16 @@ class Job(Cloudos):
|
|
|
1428
1428
|
else:
|
|
1429
1429
|
return parent_job_id
|
|
1430
1430
|
|
|
1431
|
-
def delete_job_results(self,
|
|
1431
|
+
def delete_job_results(self, job_id, mode, verify=True):
|
|
1432
1432
|
"""Delete job results folder.
|
|
1433
1433
|
|
|
1434
1434
|
Parameters
|
|
1435
1435
|
----------
|
|
1436
|
-
|
|
1437
|
-
The ID
|
|
1438
|
-
|
|
1436
|
+
job_id : str
|
|
1437
|
+
The CloudOS job ID whose results folder is to be deleted.
|
|
1438
|
+
mode : str
|
|
1439
|
+
The mode to use for deletion (e.g., "analysisResults" or "workDirectory").
|
|
1440
|
+
verify : [bool|string]
|
|
1439
1441
|
Whether to use SSL verification or not. Alternatively, if
|
|
1440
1442
|
a string is passed, it will be interpreted as the path to
|
|
1441
1443
|
the SSL certificate file. Default is True.
|
|
@@ -1452,11 +1454,14 @@ class Job(Cloudos):
|
|
|
1452
1454
|
ValueError
|
|
1453
1455
|
If the folder ID is invalid or the folder does not exist.
|
|
1454
1456
|
"""
|
|
1457
|
+
if mode not in ["analysisResults", "workDirectory"]:
|
|
1458
|
+
raise ValueError(f"Invalid mode '{mode}'. Supported modes are 'analysisResults' and 'workDirectory'.")
|
|
1459
|
+
|
|
1455
1460
|
headers = {
|
|
1456
1461
|
"Content-type": "application/json",
|
|
1457
1462
|
"apikey": self.apikey
|
|
1458
1463
|
}
|
|
1459
|
-
url = f"{self.cloudos_url}/api/v1/
|
|
1464
|
+
url = f"{self.cloudos_url}/api/v1/jobs/{job_id}/data?properties[]={mode}&teamId={self.workspace_id}"
|
|
1460
1465
|
response = retry_requests_delete(url, headers=headers, verify=verify)
|
|
1461
1466
|
|
|
1462
1467
|
# Handle specific status codes according to API specification
|
|
@@ -1464,13 +1469,20 @@ class Job(Cloudos):
|
|
|
1464
1469
|
# NoContent - successful deletion
|
|
1465
1470
|
return {"message": "Results deleted successfully", "status": "deleted"}
|
|
1466
1471
|
elif response.status_code == 400:
|
|
1467
|
-
raise ValueError("Operation not permitted: Your workspace does not have the option to delete results folders enabled. Please consult with the organisation owner to enable this feature.")
|
|
1472
|
+
raise ValueError(f"Operation not permitted: Your workspace does not have the option to delete {'results' if mode == 'analysisResults' else 'intermediate'} folders enabled. Please consult with the organisation owner to enable this feature.")
|
|
1468
1473
|
elif response.status_code == 401:
|
|
1469
1474
|
raise ValueError("Unauthorized: Invalid or missing API key.")
|
|
1470
1475
|
elif response.status_code == 403:
|
|
1471
1476
|
raise ValueError("Forbidden: You don't have permission to delete this folder.")
|
|
1472
1477
|
elif response.status_code == 404:
|
|
1473
|
-
|
|
1478
|
+
if response.content:
|
|
1479
|
+
try:
|
|
1480
|
+
error_message = json.loads(response.content).get('message', f"Job with ID '{job_id}' not found or data does not exist.")
|
|
1481
|
+
except (json.JSONDecodeError, KeyError):
|
|
1482
|
+
error_message = f"Job with ID '{job_id}' not found or data does not exist."
|
|
1483
|
+
else:
|
|
1484
|
+
error_message = f"Job with ID '{job_id}' not found or data does not exist."
|
|
1485
|
+
raise ValueError(error_message)
|
|
1474
1486
|
elif response.status_code == 409:
|
|
1475
1487
|
raise ValueError("Conflict: The folder cannot be deleted due to a conflict (e.g., folder is not empty or has dependencies).")
|
|
1476
1488
|
elif response.status_code == 500:
|
|
@@ -1481,4 +1493,4 @@ class Job(Cloudos):
|
|
|
1481
1493
|
# For any other successful response, parse content if available
|
|
1482
1494
|
if response.content:
|
|
1483
1495
|
return json.loads(response.content)
|
|
1484
|
-
return {"message": "
|
|
1496
|
+
return {"message": f"'{mode}' deleted successfully"}
|
|
@@ -73,7 +73,7 @@ def get_path(param, param_kind_map, execution_platform, storage_provider, mode="
|
|
|
73
73
|
return value
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def create_job_details(j_details_h, job_id, output_format, output_basename, parameters):
|
|
76
|
+
def create_job_details(j_details_h, job_id, output_format, output_basename, parameters, cloudos_url="https://cloudos.lifebit.ai"):
|
|
77
77
|
"""
|
|
78
78
|
Creates formatted job details output from job data in multiple formats.
|
|
79
79
|
|
|
@@ -113,6 +113,8 @@ def create_job_details(j_details_h, job_id, output_format, output_basename, para
|
|
|
113
113
|
Whether to create a separate configuration file containing job parameters.
|
|
114
114
|
If True and parameters exist, creates a '.config' file with Nextflow-style
|
|
115
115
|
parameter formatting.
|
|
116
|
+
cloudos_url : str, optional
|
|
117
|
+
The base URL of the CloudOS instance. Defaults to "https://cloudos.lifebit.ai".
|
|
116
118
|
|
|
117
119
|
Returns
|
|
118
120
|
-------
|
|
@@ -293,6 +295,11 @@ def create_job_details(j_details_h, job_id, output_format, output_basename, para
|
|
|
293
295
|
for key, value in job_details_json.items():
|
|
294
296
|
if key == "Parameters":
|
|
295
297
|
table.add_row(key, "\n".join(value.split(";")))
|
|
298
|
+
elif key == "ID":
|
|
299
|
+
# Add hyperlink to job ID
|
|
300
|
+
job_url = f"{cloudos_url}/app/advanced-analytics/analyses/{value}"
|
|
301
|
+
job_id_with_link = f"[link={job_url}]{value}[/link]"
|
|
302
|
+
table.add_row(key, job_id_with_link)
|
|
296
303
|
else:
|
|
297
304
|
table.add_row(key, str(value))
|
|
298
305
|
|
|
@@ -17,8 +17,6 @@ cloudos_cli/cost/__init__.py
|
|
|
17
17
|
cloudos_cli/cost/cost.py
|
|
18
18
|
cloudos_cli/datasets/__init__.py
|
|
19
19
|
cloudos_cli/datasets/datasets.py
|
|
20
|
-
cloudos_cli/delete/__init__.py
|
|
21
|
-
cloudos_cli/delete/results.py
|
|
22
20
|
cloudos_cli/import_wf/__init__.py
|
|
23
21
|
cloudos_cli/import_wf/import_wf.py
|
|
24
22
|
cloudos_cli/jobs/__init__.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '2.70.1'
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import click
|
|
2
|
-
import cloudos_cli.jobs.job as jb
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def delete_job_results(cloudos_url, apikey, j_id, workspace_id, verify=True):
|
|
6
|
-
job = jb.Job(cloudos_url, apikey, None, workspace_id, None, None, workflow_id=1234, project_id="None",
|
|
7
|
-
mainfile=None, importsfile=None, verify=verify)
|
|
8
|
-
|
|
9
|
-
# Get job results directory
|
|
10
|
-
try:
|
|
11
|
-
j_results_dir = job.get_field_from_jobs_endpoint(j_id, field='analysisResults', verify=verify)
|
|
12
|
-
except Exception as e:
|
|
13
|
-
if "Field 'analysisResults' not found in endpoint 'jobs'" in str(e):
|
|
14
|
-
click.secho("Selected job does not have 'Results' information.", fg="yellow", bold=True)
|
|
15
|
-
return
|
|
16
|
-
else:
|
|
17
|
-
raise e
|
|
18
|
-
|
|
19
|
-
# Get folder ID
|
|
20
|
-
folder_id = j_results_dir.get('folderId')
|
|
21
|
-
if not folder_id:
|
|
22
|
-
raise ValueError("The job does not have a results directory associated.")
|
|
23
|
-
|
|
24
|
-
job.delete_job_results(folder_id, verify=verify)
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_related_analyses/test_related_analyses.py
RENAMED
|
File without changes
|