cloudos-cli 2.48.0__tar.gz → 2.49.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 (41) hide show
  1. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/PKG-INFO +6 -7
  2. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/README.md +5 -6
  3. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/__main__.py +8 -8
  4. cloudos_cli-2.49.0/cloudos_cli/_version.py +1 -0
  5. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/clos.py +168 -17
  6. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/PKG-INFO +6 -7
  7. cloudos_cli-2.48.0/cloudos_cli/_version.py +0 -1
  8. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/LICENSE +0 -0
  9. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/__init__.py +0 -0
  10. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/configure/__init__.py +0 -0
  11. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/configure/configure.py +0 -0
  12. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/datasets/__init__.py +0 -0
  13. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/datasets/datasets.py +0 -0
  14. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/import_wf/__init__.py +0 -0
  15. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/import_wf/import_wf.py +0 -0
  16. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/jobs/__init__.py +0 -0
  17. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/jobs/job.py +0 -0
  18. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/link/__init__.py +0 -0
  19. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/link/link.py +0 -0
  20. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/procurement/__init__.py +0 -0
  21. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/procurement/images.py +0 -0
  22. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/queue/__init__.py +0 -0
  23. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/queue/queue.py +0 -0
  24. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/__init__.py +0 -0
  25. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/array_job.py +0 -0
  26. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/cloud.py +0 -0
  27. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/details.py +0 -0
  28. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/errors.py +0 -0
  29. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/last_wf.py +0 -0
  30. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/requests.py +0 -0
  31. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli/utils/resources.py +0 -0
  32. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/SOURCES.txt +0 -0
  33. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
  34. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
  35. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/requires.txt +0 -0
  36. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/cloudos_cli.egg-info/top_level.txt +0 -0
  37. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/setup.cfg +0 -0
  38. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/setup.py +0 -0
  39. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/tests/__init__.py +0 -0
  40. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/tests/functions_for_pytest.py +0 -0
  41. {cloudos_cli-2.48.0 → cloudos_cli-2.49.0}/tests/test_cli_project_create.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.48.0
3
+ Version: 2.49.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
@@ -775,16 +775,15 @@ This file can later be used when running a job with `cloudos job run --job-confi
775
775
  > [!NOTE]
776
776
  > Job details can only be retrieved for a single user, cannot see other user's job details.
777
777
 
778
- #### Get a list of your jobs from a CloudOS workspace
778
+ #### Get a list of workspace jobs from a CloudOS
779
779
 
780
- You can get a summary of your last 30 submitted jobs (or your selected number of last jobs using `--last-n-jobs n`
781
- parameter) in two different formats:
780
+ You can get a summary of the workspace's last 30 submitted jobs (or a selected number of last jobs using `--last-n-jobs n` parameter) in two different formats:
782
781
 
783
782
  - CSV: this is a table with a minimum predefined set of columns by default, or all the
784
783
  available columns using the `--all-fields` argument.
785
- - JSON: all the available information from your jobs, in JSON format.
784
+ - JSON: all the available information from the workspace jobs, in JSON format (`--all-fields` is always enabled for this format).
786
785
 
787
- To get a list with your last 30 submitted jobs to a given workspace, in CSV format, use
786
+ To get a list with the workspace's last 30 submitted jobs, in CSV format, use
788
787
  the following command:
789
788
 
790
789
  ```bash
@@ -806,7 +805,7 @@ Executing list...
806
805
 
807
806
  In addition, a file named `joblist.csv` is created.
808
807
 
809
- To get the same information, but for all your jobs and in JSON format, use the following command:
808
+ To get the same information, but for all the workspace's jobs and in JSON format, use the following command:
810
809
 
811
810
  ```bash
812
811
  cloudos job list \
@@ -740,16 +740,15 @@ This file can later be used when running a job with `cloudos job run --job-confi
740
740
  > [!NOTE]
741
741
  > Job details can only be retrieved for a single user, cannot see other user's job details.
742
742
 
743
- #### Get a list of your jobs from a CloudOS workspace
743
+ #### Get a list of workspace jobs from a CloudOS
744
744
 
745
- You can get a summary of your last 30 submitted jobs (or your selected number of last jobs using `--last-n-jobs n`
746
- parameter) in two different formats:
745
+ You can get a summary of the workspace's last 30 submitted jobs (or a selected number of last jobs using `--last-n-jobs n` parameter) in two different formats:
747
746
 
748
747
  - CSV: this is a table with a minimum predefined set of columns by default, or all the
749
748
  available columns using the `--all-fields` argument.
750
- - JSON: all the available information from your jobs, in JSON format.
749
+ - JSON: all the available information from the workspace jobs, in JSON format (`--all-fields` is always enabled for this format).
751
750
 
752
- To get a list with your last 30 submitted jobs to a given workspace, in CSV format, use
751
+ To get a list with the workspace's last 30 submitted jobs, in CSV format, use
753
752
  the following command:
754
753
 
755
754
  ```bash
@@ -771,7 +770,7 @@ Executing list...
771
770
 
772
771
  In addition, a file named `joblist.csv` is created.
773
772
 
774
- To get the same information, but for all your jobs and in JSON format, use the following command:
773
+ To get the same information, but for all the workspace's jobs and in JSON format, use the following command:
775
774
 
776
775
  ```bash
777
776
  cloudos job list \
@@ -1176,17 +1176,17 @@ def job_details(ctx,
1176
1176
  default='joblist',
1177
1177
  required=False)
1178
1178
  @click.option('--output-format',
1179
- help='The desired file format (file extension) for the output. Default=csv.',
1179
+ help='The desired file format (file extension) for the output. For json option --all-fields will be automatically set to True. Default=csv.',
1180
1180
  type=click.Choice(['csv', 'json'], case_sensitive=False),
1181
1181
  default='csv')
1182
1182
  @click.option('--all-fields',
1183
1183
  help=('Whether to collect all available fields from jobs or ' +
1184
1184
  'just the preconfigured selected fields. Only applicable ' +
1185
- 'when --output-format=csv'),
1185
+ 'when --output-format=csv. Automatically enabled for json output.'),
1186
1186
  is_flag=True)
1187
1187
  @click.option('--last-n-jobs',
1188
- help=("The number of last user's jobs to retrieve. You can use 'all' to " +
1189
- "retrieve all user's jobs. Default=30."),
1188
+ help=("The number of last workspace jobs to retrieve. You can use 'all' to " +
1189
+ "retrieve all workspace jobs. Default=30."),
1190
1190
  default='30')
1191
1191
  @click.option('--page',
1192
1192
  help=('Response page to retrieve. If --last-n-jobs is set, then --page ' +
@@ -1221,7 +1221,7 @@ def list_jobs(ctx,
1221
1221
  disable_ssl_verification,
1222
1222
  ssl_cert,
1223
1223
  profile):
1224
- """Collect all your jobs from a CloudOS workspace in CSV format."""
1224
+ """Collect workspace jobs from a CloudOS workspace in CSV or JSON format."""
1225
1225
  profile = profile or ctx.default_map['job']['list']['profile']
1226
1226
  # Create a dictionary with required and non-required params
1227
1227
  required_dict = {
@@ -1270,6 +1270,7 @@ def list_jobs(ctx,
1270
1270
  except ValueError:
1271
1271
  print("[ERROR] last-n-jobs value was not valid. Please use a positive int or 'all'")
1272
1272
  raise
1273
+
1273
1274
  my_jobs_r = cl.get_job_list(workspace_id, last_n_jobs, page, archived, verify_ssl)
1274
1275
  if len(my_jobs_r) == 0:
1275
1276
  if ctx.get_parameter_source('page') == click.core.ParameterSource.DEFAULT:
@@ -1281,15 +1282,14 @@ def list_jobs(ctx,
1281
1282
  'using --page parameter.')
1282
1283
  elif output_format == 'csv':
1283
1284
  my_jobs = cl.process_job_list(my_jobs_r, all_fields)
1284
- my_jobs.to_csv(outfile, index=False)
1285
- print(f'\tJob list collected with a total of {my_jobs.shape[0]} jobs.')
1285
+ cl.save_job_list_to_csv(my_jobs, outfile)
1286
1286
  elif output_format == 'json':
1287
1287
  with open(outfile, 'w') as o:
1288
1288
  o.write(json.dumps(my_jobs_r))
1289
1289
  print(f'\tJob list collected with a total of {len(my_jobs_r)} jobs.')
1290
+ print(f'\tJob list saved to {outfile}')
1290
1291
  else:
1291
1292
  raise ValueError('Unrecognised output format. Please use one of [csv|json]')
1292
- print(f'\tJob list saved to {outfile}')
1293
1293
 
1294
1294
 
1295
1295
  @job.command('abort')
@@ -0,0 +1 @@
1
+ __version__ = '2.49.0'
@@ -11,6 +11,8 @@ from cloudos_cli.utils.errors import BadRequestException, JoBNotCompletedExcepti
11
11
  from cloudos_cli.utils.requests import retry_requests_get, retry_requests_post, retry_requests_put
12
12
  import pandas as pd
13
13
  from cloudos_cli.utils.last_wf import youngest_workflow_id_by_name
14
+ from datetime import datetime
15
+
14
16
 
15
17
  # GLOBAL VARS
16
18
  JOB_COMPLETED = 'completed'
@@ -430,28 +432,27 @@ class Cloudos:
430
432
  df : pandas.DataFrame
431
433
  A DataFrame with the requested columns from the jobs.
432
434
  """
433
- COLUMNS = ['_id',
434
- 'team',
435
+ COLUMNS = ['status',
435
436
  'name',
436
- 'parameters',
437
- 'status',
437
+ 'project.name',
438
+ 'user.name',
439
+ 'user.surname',
440
+ 'workflow.name',
441
+ '_id',
438
442
  'startTime',
439
443
  'endTime',
440
444
  'createdAt',
441
445
  'updatedAt',
442
- 'computeCostSpent',
443
- 'masterInstanceStorageCost',
444
- 'user.id',
445
- 'workflow._id',
446
- 'workflow.name',
447
- 'workflow.description',
448
- 'workflow.createdAt',
449
- 'workflow.updatedAt',
450
- 'workflow.workflowType',
451
- 'project._id',
452
- 'project.name',
453
- 'project.createdAt',
454
- 'project.updatedAt'
446
+ 'revision.commit',
447
+ 'realInstancesExecutionCost',
448
+ 'masterInstance.usedInstance.type',
449
+ 'storageMode',
450
+ 'workflow.repository.url',
451
+ 'nextflowVersion',
452
+ 'batch.enabled',
453
+ 'storageSizeInGb',
454
+ 'batch.jobQueue.id',
455
+ 'usesFusionFileSystem'
455
456
  ]
456
457
  df_full = pd.json_normalize(r)
457
458
  if df_full.empty:
@@ -462,6 +463,156 @@ class Cloudos:
462
463
  df = df_full.loc[:, COLUMNS]
463
464
  return df
464
465
 
466
+ def reorder_job_list(self, my_jobs_df, filename='my_jobs.csv'):
467
+ """Save a job list DataFrame to a CSV file with renamed and ordered columns.
468
+
469
+ Parameters
470
+ ----------
471
+ my_jobs_df : pandas.DataFrame
472
+ A DataFrame containing job information from process_job_list.
473
+ filename : str
474
+ The name of the file to save the DataFrame to. Default is 'my_jobs.csv'.
475
+
476
+ Returns
477
+ -------
478
+ None
479
+ Saves the DataFrame to a CSV file with renamed and ordered columns.
480
+ """
481
+ # Handle empty DataFrame
482
+ if my_jobs_df.empty:
483
+ print("Warning: DataFrame is empty. Creating empty CSV file.")
484
+ empty_df = pd.DataFrame()
485
+ empty_df.to_csv(filename, index=False)
486
+ return
487
+
488
+ # Create a copy to avoid modifying the original DataFrame
489
+ jobs_df = my_jobs_df.copy()
490
+
491
+ # 1. Fusion user.name and user.surname into user
492
+ if 'user.name' in jobs_df.columns and 'user.surname' in jobs_df.columns:
493
+ jobs_df['user'] = jobs_df.apply(
494
+ lambda row: f"{row.get('user.name', '')} {row.get('user.surname', '')}".strip()
495
+ if pd.notna(row.get('user.name')) or pd.notna(row.get('user.surname'))
496
+ else None, axis=1
497
+ )
498
+ # Remove original columns
499
+ jobs_df = jobs_df.drop(columns=['user.name', 'user.surname'], errors='ignore')
500
+
501
+ # 2. Convert time fields to human-readable format
502
+ time_columns = ['startTime', 'endTime', 'createdAt', 'updatedAt']
503
+ for col in time_columns:
504
+ if col in jobs_df.columns:
505
+ def format_time(x):
506
+ if pd.notna(x) and isinstance(x, str) and x:
507
+ try:
508
+ return datetime.fromisoformat(x.replace('Z', '+00:00')).strftime('%Y-%m-%d %H:%M:%S UTC')
509
+ except (ValueError, TypeError):
510
+ return x # Return original value if parsing fails
511
+ return None
512
+ jobs_df[col] = jobs_df[col].apply(format_time)
513
+
514
+ # 3. Format realInstancesExecutionCost (divide by 100, show 4 decimals)
515
+ if 'realInstancesExecutionCost' in jobs_df.columns:
516
+ def format_cost(x):
517
+ if pd.notna(x) and x != '' and x is not None:
518
+ try:
519
+ return f"{float(x) / 100:.4f}"
520
+ except (ValueError, TypeError):
521
+ return x # Return original value if conversion fails
522
+ return None
523
+ jobs_df['realInstancesExecutionCost'] = jobs_df['realInstancesExecutionCost'].apply(format_cost)
524
+
525
+ # 4. Calculate Run time (endTime - startTime)
526
+ if 'startTime' in jobs_df.columns and 'endTime' in jobs_df.columns:
527
+ def calculate_runtime(row):
528
+ start_time = row.get('startTime')
529
+ end_time = row.get('endTime')
530
+ if pd.notna(start_time) and pd.notna(end_time) and start_time and end_time:
531
+ # Use original times from the original DataFrame for calculation
532
+ original_start = my_jobs_df.iloc[row.name].get('startTime') if row.name < len(my_jobs_df) else start_time
533
+ original_end = my_jobs_df.iloc[row.name].get('endTime') if row.name < len(my_jobs_df) else end_time
534
+ if pd.notna(original_start) and pd.notna(original_end) and original_start and original_end:
535
+ try:
536
+ start_dt = datetime.fromisoformat(str(original_start).replace('Z', '+00:00'))
537
+ end_dt = datetime.fromisoformat(str(original_end).replace('Z', '+00:00'))
538
+ duration = end_dt - start_dt
539
+ # Format duration as hours:minutes:seconds
540
+ total_seconds = int(duration.total_seconds())
541
+ hours = total_seconds // 3600
542
+ minutes = (total_seconds % 3600) // 60
543
+ seconds = total_seconds % 60
544
+ if hours > 0:
545
+ return f"{hours}h {minutes}m {seconds}s"
546
+ elif minutes > 0:
547
+ return f"{minutes}m {seconds}s"
548
+ else:
549
+ return f"{seconds}s"
550
+ except (ValueError, TypeError):
551
+ return None
552
+ return None
553
+
554
+ jobs_df['Run time'] = jobs_df.apply(calculate_runtime, axis=1)
555
+
556
+ # 5. Format batch.enabled (True -> "Batch", else "N/A")
557
+ if 'batch.enabled' in jobs_df.columns:
558
+ jobs_df['batch.enabled'] = jobs_df['batch.enabled'].apply(
559
+ lambda x: "Batch" if x is True else "N/A"
560
+ )
561
+
562
+ # 6. Rename columns using the provided dictionary
563
+ column_name_mapping = {
564
+ "status": "Status",
565
+ "name": "Name",
566
+ "project.name": "Project",
567
+ "user": "Owner",
568
+ "workflow.name": "Pipeline",
569
+ "_id": "ID",
570
+ "createdAt": "Submit time",
571
+ "updatedAt": "End time",
572
+ "revision.commit": "Commit",
573
+ "realInstancesExecutionCost": "Cost",
574
+ "masterInstance.usedInstance.type": "Resources",
575
+ "storageMode": "Storage type",
576
+ "workflow.repository.url": "Pipeline url",
577
+ "nextflowVersion": "Nextflow version",
578
+ "batch.enabled": "Executor",
579
+ "storageSizeInGb": "Storage size",
580
+ "batch.jobQueue.id": "Job queue ID",
581
+ "usesFusionFileSystem": "Accelerated file staging"
582
+ }
583
+
584
+ # Rename columns that exist in the DataFrame
585
+ jobs_df = jobs_df.rename(columns=column_name_mapping)
586
+
587
+ # Remove the original startTime and endTime columns since we now have Submit time, End time, and Run time
588
+ jobs_df = jobs_df.drop(columns=['startTime', 'endTime'], errors='ignore')
589
+
590
+ # 7. Define the desired order of columns
591
+ desired_order = [
592
+ "Status", "Name", "Project", "Owner", "Pipeline", "ID",
593
+ "Submit time", "End time", "Run time", "Commit", "Cost",
594
+ "Resources", "Storage type", "Pipeline url",
595
+ "Nextflow version", "Executor", "Storage size", "Job queue ID",
596
+ "Accelerated file staging"
597
+ ]
598
+
599
+ # Reorder columns - only include columns that exist in the DataFrame
600
+ available_columns = [col for col in desired_order if col in jobs_df.columns]
601
+ # Add any remaining columns that aren't in the desired order
602
+ remaining_columns = [col for col in jobs_df.columns if col not in desired_order]
603
+ final_column_order = available_columns + remaining_columns
604
+
605
+ # Reorder the DataFrame
606
+ jobs_df = jobs_df[final_column_order]
607
+ return jobs_df
608
+
609
+ def save_job_list_to_csv(self, my_jobs_df, filename='my_jobs.csv'):
610
+ # Save to CSV
611
+ jobs_df = self.reorder_job_list(my_jobs_df, filename)
612
+ jobs_df.to_csv(filename, index=False)
613
+ print(f'\tJob list collected with a total of {len(jobs_df)} jobs.')
614
+ print(f'\tJob list saved to {filename}')
615
+
465
616
  def get_workflow_list(self, workspace_id, verify=True, get_all=True,
466
617
  page=1, page_size=10, max_page_size=100,
467
618
  archived_status=False):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudos_cli
3
- Version: 2.48.0
3
+ Version: 2.49.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
@@ -775,16 +775,15 @@ This file can later be used when running a job with `cloudos job run --job-confi
775
775
  > [!NOTE]
776
776
  > Job details can only be retrieved for a single user, cannot see other user's job details.
777
777
 
778
- #### Get a list of your jobs from a CloudOS workspace
778
+ #### Get a list of workspace jobs from a CloudOS
779
779
 
780
- You can get a summary of your last 30 submitted jobs (or your selected number of last jobs using `--last-n-jobs n`
781
- parameter) in two different formats:
780
+ You can get a summary of the workspace's last 30 submitted jobs (or a selected number of last jobs using `--last-n-jobs n` parameter) in two different formats:
782
781
 
783
782
  - CSV: this is a table with a minimum predefined set of columns by default, or all the
784
783
  available columns using the `--all-fields` argument.
785
- - JSON: all the available information from your jobs, in JSON format.
784
+ - JSON: all the available information from the workspace jobs, in JSON format (`--all-fields` is always enabled for this format).
786
785
 
787
- To get a list with your last 30 submitted jobs to a given workspace, in CSV format, use
786
+ To get a list with the workspace's last 30 submitted jobs, in CSV format, use
788
787
  the following command:
789
788
 
790
789
  ```bash
@@ -806,7 +805,7 @@ Executing list...
806
805
 
807
806
  In addition, a file named `joblist.csv` is created.
808
807
 
809
- To get the same information, but for all your jobs and in JSON format, use the following command:
808
+ To get the same information, but for all the workspace's jobs and in JSON format, use the following command:
810
809
 
811
810
  ```bash
812
811
  cloudos job list \
@@ -1 +0,0 @@
1
- __version__ = '2.48.0'
File without changes
File without changes
File without changes