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.
Files changed (55) hide show
  1. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/PKG-INFO +1 -1
  2. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/__main__.py +47 -4
  3. cloudos_cli-2.72.0/cloudos_cli/_version.py +1 -0
  4. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/jobs/job.py +20 -8
  5. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/details.py +8 -1
  6. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/PKG-INFO +1 -1
  7. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/SOURCES.txt +0 -2
  8. cloudos_cli-2.70.1/cloudos_cli/_version.py +0 -1
  9. cloudos_cli-2.70.1/cloudos_cli/delete/__init__.py +0 -8
  10. cloudos_cli-2.70.1/cloudos_cli/delete/results.py +0 -24
  11. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/LICENSE +0 -0
  12. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/README.md +0 -0
  13. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/__init__.py +0 -0
  14. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/clos.py +0 -0
  15. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/configure/__init__.py +0 -0
  16. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/configure/configure.py +0 -0
  17. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/cost/__init__.py +0 -0
  18. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/cost/cost.py +0 -0
  19. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/datasets/__init__.py +0 -0
  20. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/datasets/datasets.py +0 -0
  21. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/import_wf/__init__.py +0 -0
  22. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/import_wf/import_wf.py +0 -0
  23. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/jobs/__init__.py +0 -0
  24. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/link/__init__.py +0 -0
  25. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/link/link.py +0 -0
  26. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/logging/__init__.py +0 -0
  27. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/logging/logger.py +0 -0
  28. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/procurement/__init__.py +0 -0
  29. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/procurement/images.py +0 -0
  30. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/queue/__init__.py +0 -0
  31. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/queue/queue.py +0 -0
  32. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/related_analyses/__init__.py +0 -0
  33. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/related_analyses/related_analyses.py +0 -0
  34. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/__init__.py +0 -0
  35. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/array_job.py +0 -0
  36. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/cloud.py +0 -0
  37. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/errors.py +0 -0
  38. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/last_wf.py +0 -0
  39. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/requests.py +0 -0
  40. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli/utils/resources.py +0 -0
  41. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
  42. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
  43. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/requires.txt +0 -0
  44. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/cloudos_cli.egg-info/top_level.txt +0 -0
  45. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/setup.cfg +0 -0
  46. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/setup.py +0 -0
  47. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/__init__.py +0 -0
  48. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/functions_for_pytest.py +0 -0
  49. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cli_project_create.py +0 -0
  50. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cost/__init__.py +0 -0
  51. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_cost/test_job_cost.py +0 -0
  52. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_logging/__init__.py +0 -0
  53. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_logging/test_logger.py +0 -0
  54. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_related_analyses/__init__.py +0 -0
  55. {cloudos_cli-2.70.1 → cloudos_cli-2.72.0}/tests/test_related_analyses/test_related_analyses.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.70.1
3
+ Version: 2.72.0
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -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
- delete_job_results(cloudos_url, apikey, job_id, workspace_id, verify_ssl)
1202
- print('Results directories deleted successfully.')
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, folder_id, verify=True):
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
- folder_id : str
1437
- The ID of the folder to delete (typically a job results folder).
1438
- verify : [bool | str], optional
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/folders/{folder_id}?teamId={self.workspace_id}"
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
- raise ValueError(f"{json.loads(response.content)['message']}")
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": "Results deleted successfully"}
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.70.1
3
+ Version: 2.72.0
4
4
  Summary: Python package for interacting with CloudOS
5
5
  Home-page: https://github.com/lifebit-ai/cloudos-cli
6
6
  Author: David Piñeyro
@@ -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,8 +0,0 @@
1
- """
2
- Functions and classes related to delete (results & workdir).
3
- """
4
-
5
- from .results import delete_job_results
6
-
7
-
8
- __all__ = ['results']
@@ -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