cloudos-cli 2.48.0__tar.gz → 2.50.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.48.0 → cloudos_cli-2.50.0}/PKG-INFO +6 -7
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/README.md +5 -6
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/__main__.py +49 -10
- cloudos_cli-2.50.0/cloudos_cli/_version.py +1 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/clos.py +168 -17
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/errors.py +15 -1
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/PKG-INFO +6 -7
- cloudos_cli-2.48.0/cloudos_cli/_version.py +0 -1
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/LICENSE +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/configure/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/configure/configure.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/datasets/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/datasets/datasets.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/import_wf/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/import_wf/import_wf.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/jobs/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/jobs/job.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/link/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/link/link.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/procurement/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/procurement/images.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/queue/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/queue/queue.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/array_job.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/cloud.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/details.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/last_wf.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/requests.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli/utils/resources.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/SOURCES.txt +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/dependency_links.txt +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/entry_points.txt +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/requires.txt +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/cloudos_cli.egg-info/top_level.txt +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/setup.cfg +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/setup.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/tests/__init__.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.0}/tests/functions_for_pytest.py +0 -0
- {cloudos_cli-2.48.0 → cloudos_cli-2.50.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.
|
|
3
|
+
Version: 2.50.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
|
|
778
|
+
#### Get a list of workspace jobs from a CloudOS
|
|
779
779
|
|
|
780
|
-
You can get a summary of
|
|
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
|
|
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
|
|
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
|
|
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
|
|
743
|
+
#### Get a list of workspace jobs from a CloudOS
|
|
744
744
|
|
|
745
|
-
You can get a summary of
|
|
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
|
|
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
|
|
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
|
|
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 \
|
|
@@ -9,6 +9,7 @@ from cloudos_cli.utils.errors import BadRequestException
|
|
|
9
9
|
import json
|
|
10
10
|
import time
|
|
11
11
|
import sys
|
|
12
|
+
import traceback
|
|
12
13
|
from ._version import __version__
|
|
13
14
|
from cloudos_cli.configure.configure import ConfigurationProfile
|
|
14
15
|
from rich.console import Console
|
|
@@ -36,12 +37,40 @@ CLOUDOS_URL = 'https://cloudos.lifebit.ai'
|
|
|
36
37
|
INIT_PROFILE = 'initialisingProfile'
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def handle_exception(show_traceback=False):
|
|
41
|
+
"""Custom exception handler for CloudOS CLI"""
|
|
42
|
+
def exception_handler(exc_type, exc_value, exc_traceback):
|
|
43
|
+
# Handle keyboard interrupt gracefully
|
|
44
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
if show_traceback:
|
|
48
|
+
# Show full traceback for debugging
|
|
49
|
+
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
|
50
|
+
else:
|
|
51
|
+
# Show only the error message
|
|
52
|
+
click.echo(click.style(f"Error: {exc_value}", fg='red'), err=True)
|
|
53
|
+
|
|
54
|
+
sys.exit(1)
|
|
55
|
+
|
|
56
|
+
return exception_handler
|
|
57
|
+
|
|
58
|
+
|
|
39
59
|
@click.group()
|
|
60
|
+
@click.option('--debug', is_flag=True, help='Show detailed error information and tracebacks')
|
|
40
61
|
@click.version_option(__version__)
|
|
41
62
|
@click.pass_context
|
|
42
|
-
def run_cloudos_cli(ctx):
|
|
63
|
+
def run_cloudos_cli(ctx, debug):
|
|
43
64
|
"""CloudOS python package: a package for interacting with CloudOS."""
|
|
44
65
|
ctx.ensure_object(dict)
|
|
66
|
+
ctx.obj['debug'] = debug
|
|
67
|
+
|
|
68
|
+
# Set up custom exception handling based on debug flag
|
|
69
|
+
if not debug:
|
|
70
|
+
sys.excepthook = handle_exception(show_traceback=False)
|
|
71
|
+
else:
|
|
72
|
+
sys.excepthook = handle_exception(show_traceback=True)
|
|
73
|
+
|
|
45
74
|
if ctx.invoked_subcommand not in ['datasets']:
|
|
46
75
|
print(run_cloudos_cli.__doc__ + '\n')
|
|
47
76
|
print('Version: ' + __version__ + '\n')
|
|
@@ -1176,17 +1205,17 @@ def job_details(ctx,
|
|
|
1176
1205
|
default='joblist',
|
|
1177
1206
|
required=False)
|
|
1178
1207
|
@click.option('--output-format',
|
|
1179
|
-
help='The desired file format (file extension) for the output. Default=csv.',
|
|
1208
|
+
help='The desired file format (file extension) for the output. For json option --all-fields will be automatically set to True. Default=csv.',
|
|
1180
1209
|
type=click.Choice(['csv', 'json'], case_sensitive=False),
|
|
1181
1210
|
default='csv')
|
|
1182
1211
|
@click.option('--all-fields',
|
|
1183
1212
|
help=('Whether to collect all available fields from jobs or ' +
|
|
1184
1213
|
'just the preconfigured selected fields. Only applicable ' +
|
|
1185
|
-
'when --output-format=csv'),
|
|
1214
|
+
'when --output-format=csv. Automatically enabled for json output.'),
|
|
1186
1215
|
is_flag=True)
|
|
1187
1216
|
@click.option('--last-n-jobs',
|
|
1188
|
-
help=("The number of last
|
|
1189
|
-
"retrieve all
|
|
1217
|
+
help=("The number of last workspace jobs to retrieve. You can use 'all' to " +
|
|
1218
|
+
"retrieve all workspace jobs. Default=30."),
|
|
1190
1219
|
default='30')
|
|
1191
1220
|
@click.option('--page',
|
|
1192
1221
|
help=('Response page to retrieve. If --last-n-jobs is set, then --page ' +
|
|
@@ -1221,7 +1250,7 @@ def list_jobs(ctx,
|
|
|
1221
1250
|
disable_ssl_verification,
|
|
1222
1251
|
ssl_cert,
|
|
1223
1252
|
profile):
|
|
1224
|
-
"""Collect
|
|
1253
|
+
"""Collect workspace jobs from a CloudOS workspace in CSV or JSON format."""
|
|
1225
1254
|
profile = profile or ctx.default_map['job']['list']['profile']
|
|
1226
1255
|
# Create a dictionary with required and non-required params
|
|
1227
1256
|
required_dict = {
|
|
@@ -1270,6 +1299,7 @@ def list_jobs(ctx,
|
|
|
1270
1299
|
except ValueError:
|
|
1271
1300
|
print("[ERROR] last-n-jobs value was not valid. Please use a positive int or 'all'")
|
|
1272
1301
|
raise
|
|
1302
|
+
|
|
1273
1303
|
my_jobs_r = cl.get_job_list(workspace_id, last_n_jobs, page, archived, verify_ssl)
|
|
1274
1304
|
if len(my_jobs_r) == 0:
|
|
1275
1305
|
if ctx.get_parameter_source('page') == click.core.ParameterSource.DEFAULT:
|
|
@@ -1281,15 +1311,14 @@ def list_jobs(ctx,
|
|
|
1281
1311
|
'using --page parameter.')
|
|
1282
1312
|
elif output_format == 'csv':
|
|
1283
1313
|
my_jobs = cl.process_job_list(my_jobs_r, all_fields)
|
|
1284
|
-
|
|
1285
|
-
print(f'\tJob list collected with a total of {my_jobs.shape[0]} jobs.')
|
|
1314
|
+
cl.save_job_list_to_csv(my_jobs, outfile)
|
|
1286
1315
|
elif output_format == 'json':
|
|
1287
1316
|
with open(outfile, 'w') as o:
|
|
1288
1317
|
o.write(json.dumps(my_jobs_r))
|
|
1289
1318
|
print(f'\tJob list collected with a total of {len(my_jobs_r)} jobs.')
|
|
1319
|
+
print(f'\tJob list saved to {outfile}')
|
|
1290
1320
|
else:
|
|
1291
1321
|
raise ValueError('Unrecognised output format. Please use one of [csv|json]')
|
|
1292
|
-
print(f'\tJob list saved to {outfile}')
|
|
1293
1322
|
|
|
1294
1323
|
|
|
1295
1324
|
@job.command('abort')
|
|
@@ -3913,4 +3942,14 @@ def reset_organisation_image(ctx,
|
|
|
3913
3942
|
|
|
3914
3943
|
|
|
3915
3944
|
if __name__ == "__main__":
|
|
3916
|
-
|
|
3945
|
+
try:
|
|
3946
|
+
run_cloudos_cli()
|
|
3947
|
+
except Exception as e:
|
|
3948
|
+
# Check if debug flag was passed (fallback for cases where Click doesn't handle it)
|
|
3949
|
+
debug_mode = '--debug' in sys.argv
|
|
3950
|
+
|
|
3951
|
+
if debug_mode:
|
|
3952
|
+
traceback.print_exc()
|
|
3953
|
+
else:
|
|
3954
|
+
click.echo(click.style(f"Error: {e}", fg='red'), err=True)
|
|
3955
|
+
sys.exit(1)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '2.50.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 = ['
|
|
434
|
-
'team',
|
|
435
|
+
COLUMNS = ['status',
|
|
435
436
|
'name',
|
|
436
|
-
'
|
|
437
|
-
'
|
|
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
|
-
'
|
|
443
|
-
'
|
|
444
|
-
'
|
|
445
|
-
'
|
|
446
|
-
'workflow.
|
|
447
|
-
'
|
|
448
|
-
'
|
|
449
|
-
'
|
|
450
|
-
'
|
|
451
|
-
'
|
|
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):
|
|
@@ -12,7 +12,21 @@ class BadRequestException(Exception):
|
|
|
12
12
|
The request variable returned that caused the error.
|
|
13
13
|
"""
|
|
14
14
|
def __init__(self, rv):
|
|
15
|
-
|
|
15
|
+
# Try to get the message from response body first
|
|
16
|
+
error_message = None
|
|
17
|
+
try:
|
|
18
|
+
response_body = rv.json()
|
|
19
|
+
error_message = response_body.get('message')
|
|
20
|
+
except (ValueError, AttributeError):
|
|
21
|
+
# Response is not JSON or doesn't have expected structure
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
# Prioritize message from response, fallback to reason
|
|
25
|
+
if error_message:
|
|
26
|
+
msg = "Server returned status {}. Message: {}".format(rv.status_code, error_message)
|
|
27
|
+
else:
|
|
28
|
+
msg = "Server returned status {}. Reason: {}".format(rv.status_code, rv.reason)
|
|
29
|
+
|
|
16
30
|
super(BadRequestException, self).__init__(msg)
|
|
17
31
|
self.rv = rv
|
|
18
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudos_cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.50.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
|
|
778
|
+
#### Get a list of workspace jobs from a CloudOS
|
|
779
779
|
|
|
780
|
-
You can get a summary of
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|