cmem-cmemc 23.1.3__py3-none-any.whl → 23.3.0__py3-none-any.whl
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.
- cmem/cmemc/cli/__init__.py +9 -3
- cmem/cmemc/cli/_cmemc.zsh +44 -0
- cmem/cmemc/cli/commands/__init__.py +3 -0
- cmem/cmemc/cli/commands/admin.py +3 -1
- cmem/cmemc/cli/commands/client.py +173 -0
- cmem/cmemc/cli/commands/config.py +1 -1
- cmem/cmemc/cli/commands/dataset.py +161 -70
- cmem/cmemc/cli/commands/graph.py +10 -10
- cmem/cmemc/cli/commands/metrics.py +3 -3
- cmem/cmemc/cli/commands/project.py +90 -27
- cmem/cmemc/cli/commands/python.py +110 -29
- cmem/cmemc/cli/commands/query.py +6 -6
- cmem/cmemc/cli/commands/resource.py +5 -5
- cmem/cmemc/cli/commands/scheduler.py +4 -4
- cmem/cmemc/cli/commands/store.py +34 -31
- cmem/cmemc/cli/commands/user.py +27 -10
- cmem/cmemc/cli/commands/variable.py +364 -0
- cmem/cmemc/cli/commands/vocabulary.py +5 -5
- cmem/cmemc/cli/commands/workflow.py +118 -55
- cmem/cmemc/cli/commands/workspace.py +5 -5
- cmem/cmemc/cli/completion.py +393 -154
- cmem/cmemc/cli/context.py +20 -3
- cmem/cmemc/cli/manual_helper/multi_page.py +11 -6
- cmem/cmemc/cli/utils.py +80 -0
- {cmem_cmemc-23.1.3.dist-info → cmem_cmemc-23.3.0.dist-info}/METADATA +6 -8
- cmem_cmemc-23.3.0.dist-info/RECORD +35 -0
- {cmem_cmemc-23.1.3.dist-info → cmem_cmemc-23.3.0.dist-info}/WHEEL +1 -1
- cmem_cmemc-23.1.3.dist-info/RECORD +0 -32
- {cmem_cmemc-23.1.3.dist-info → cmem_cmemc-23.3.0.dist-info}/LICENSE +0 -0
- {cmem_cmemc-23.1.3.dist-info → cmem_cmemc-23.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -8,15 +8,16 @@ import time
|
|
|
8
8
|
import click
|
|
9
9
|
|
|
10
10
|
import timeago
|
|
11
|
+
from click import UsageError
|
|
11
12
|
|
|
12
13
|
from cmem.cmemc.cli import completion
|
|
13
14
|
from cmem.cmemc.cli.commands import CmemcCommand, CmemcGroup
|
|
14
15
|
from cmem.cmemc.cli.commands.scheduler import scheduler
|
|
15
|
-
from cmem.cmempy.workflow import get_workflows
|
|
16
|
+
from cmem.cmempy.workflow import get_workflows, get_resource_based_dataset_types
|
|
16
17
|
from cmem.cmempy.workflow.workflow import (
|
|
17
18
|
execute_workflow_io,
|
|
18
19
|
get_workflow_editor_uri,
|
|
19
|
-
get_workflows_io
|
|
20
|
+
get_workflows_io,
|
|
20
21
|
)
|
|
21
22
|
from cmem.cmempy.workspace.activities import (
|
|
22
23
|
ACTIVITY_TYPE_EXECUTE_DEFAULTWORKFLOW,
|
|
@@ -31,6 +32,7 @@ from cmem.cmempy.workspace.activities.taskactivity import (
|
|
|
31
32
|
)
|
|
32
33
|
from cmem.cmempy.workspace.projects.project import get_projects
|
|
33
34
|
from cmem.cmempy.workspace.search import list_items
|
|
35
|
+
from cmem.cmemc.cli.context import ApplicationContext
|
|
34
36
|
|
|
35
37
|
WORKFLOW_FILTER_TYPES = sorted(['project', 'regex', 'tag', 'io'])
|
|
36
38
|
WORKFLOW_LIST_FILTER_HELP_TEXT = (
|
|
@@ -43,11 +45,15 @@ IO_WARNING_NO_RESULT = "The workflow was executed but produced no result."
|
|
|
43
45
|
IO_WARNING_NO_OUTPUT_DEFINED = "The workflow was executed, a result was "\
|
|
44
46
|
"received but dropped."
|
|
45
47
|
|
|
46
|
-
MIME_CSV = "
|
|
47
|
-
MIME_XLS = "application/
|
|
48
|
+
MIME_CSV = "application/x-plugin-csv"
|
|
49
|
+
MIME_XLS = "application/x-plugin-excel"
|
|
48
50
|
MIME_NT = "application/n-triples"
|
|
49
|
-
MIME_JSON = "application/json"
|
|
51
|
+
MIME_JSON = "application/x-plugin-json"
|
|
50
52
|
MIME_XML = "application/xml"
|
|
53
|
+
MIME_FILE = "application/octet-stream"
|
|
54
|
+
MIME_ZIP = "application/x-plugin-multiCsv"
|
|
55
|
+
MIME_ALIGNMENT = "text/alignment"
|
|
56
|
+
MIME_TEXT = "text/plain"
|
|
51
57
|
|
|
52
58
|
VALID_OUTPUT_EXTENSIONS = {
|
|
53
59
|
".csv": MIME_CSV,
|
|
@@ -61,10 +67,23 @@ VALID_OUTPUT_EXTENSIONS = {
|
|
|
61
67
|
VALID_INPUT_EXTENSIONS = {
|
|
62
68
|
".csv": MIME_CSV,
|
|
63
69
|
".json": MIME_JSON,
|
|
64
|
-
".xml": MIME_XML
|
|
70
|
+
".xml": MIME_XML,
|
|
71
|
+
".xlsx": MIME_XLS,
|
|
72
|
+
".file": MIME_FILE,
|
|
73
|
+
".zip": MIME_ZIP,
|
|
74
|
+
".txt": MIME_TEXT,
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
|
|
78
|
+
def is_supported_mime_type(mime_type: str) -> bool:
|
|
79
|
+
"""Boolean to determine if a request is multipart or not"""
|
|
80
|
+
supported_mime_types = get_resource_based_dataset_types()
|
|
81
|
+
for supported_mime_type in supported_mime_types:
|
|
82
|
+
if supported_mime_type in mime_type:
|
|
83
|
+
return True
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
|
|
68
87
|
def _get_workflow_tag_labels(workflow_) -> list:
|
|
69
88
|
"""Output a list of tag labels from a single workflow."""
|
|
70
89
|
tag_labels = []
|
|
@@ -226,7 +245,8 @@ def _io_guess_output(output_file):
|
|
|
226
245
|
return None
|
|
227
246
|
_, file_extension = os.path.splitext(output_file)
|
|
228
247
|
if file_extension in VALID_OUTPUT_EXTENSIONS:
|
|
229
|
-
|
|
248
|
+
if is_supported_mime_type(VALID_OUTPUT_EXTENSIONS[file_extension]):
|
|
249
|
+
return VALID_OUTPUT_EXTENSIONS[file_extension]
|
|
230
250
|
valid_extensions = ', '.join(VALID_OUTPUT_EXTENSIONS.keys())
|
|
231
251
|
raise ValueError(
|
|
232
252
|
f"Files with the extension {file_extension} can not be generated. "
|
|
@@ -240,7 +260,8 @@ def _io_guess_input(input_file):
|
|
|
240
260
|
return None
|
|
241
261
|
_, file_extension = os.path.splitext(input_file)
|
|
242
262
|
if file_extension in VALID_INPUT_EXTENSIONS:
|
|
243
|
-
|
|
263
|
+
if is_supported_mime_type(VALID_INPUT_EXTENSIONS[file_extension]):
|
|
264
|
+
return VALID_INPUT_EXTENSIONS[file_extension]
|
|
244
265
|
valid_extensions = ', '.join(VALID_INPUT_EXTENSIONS.keys())
|
|
245
266
|
raise ValueError(
|
|
246
267
|
f"Files with the extension {file_extension} can not be processed. "
|
|
@@ -257,19 +278,54 @@ def _workflows_get_ids():
|
|
|
257
278
|
return ids
|
|
258
279
|
|
|
259
280
|
|
|
260
|
-
def
|
|
261
|
-
"""
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
281
|
+
def _get_progress_bar_info(status):
|
|
282
|
+
"""get the workflow message from status response with colors."""
|
|
283
|
+
if status is None:
|
|
284
|
+
return ""
|
|
285
|
+
if status["isRunning"]:
|
|
286
|
+
return click.style(status["message"], fg="yellow")
|
|
287
|
+
if status["concreteStatus"] == "Failed":
|
|
288
|
+
return click.style(status["message"], fg="red")
|
|
289
|
+
return click.style(status["message"], fg="green")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _workflow_wait_until_finished(
|
|
293
|
+
app: ApplicationContext,
|
|
294
|
+
project_id: str,
|
|
295
|
+
task_id: str,
|
|
296
|
+
polling_interval: int,
|
|
297
|
+
log_progress: bool
|
|
298
|
+
):
|
|
299
|
+
"""Poll workflow status until workflow is finished and return status."""
|
|
300
|
+
progress = 0
|
|
301
|
+
if log_progress:
|
|
302
|
+
app.echo_success(message="", nl=True)
|
|
303
|
+
with click.progressbar(
|
|
304
|
+
show_eta=False,
|
|
305
|
+
length=100,
|
|
306
|
+
item_show_func=_get_progress_bar_info,
|
|
307
|
+
) as progress_bar:
|
|
308
|
+
while True:
|
|
309
|
+
status = get_activity_status(project_id, task_id)
|
|
310
|
+
if progress is not status['progress']:
|
|
311
|
+
progress_bar.update(
|
|
312
|
+
n_steps=status['progress'] - progress,
|
|
313
|
+
current_item=status
|
|
314
|
+
)
|
|
315
|
+
progress = status['progress']
|
|
316
|
+
app.echo_debug(f"{status['statusName']}({status['message']})")
|
|
317
|
+
# wait until isRunning is false
|
|
318
|
+
if not status['isRunning']:
|
|
319
|
+
break
|
|
320
|
+
time.sleep(polling_interval)
|
|
321
|
+
else:
|
|
322
|
+
while True:
|
|
323
|
+
status = get_activity_status(project_id, task_id)
|
|
324
|
+
app.echo_debug(f"{status['statusName']}({status['message']})")
|
|
325
|
+
# wait until isRunning is false
|
|
326
|
+
if not status['isRunning']:
|
|
327
|
+
break
|
|
328
|
+
time.sleep(polling_interval)
|
|
273
329
|
return status
|
|
274
330
|
|
|
275
331
|
|
|
@@ -317,6 +373,7 @@ def _workflow_echo_status(app, status):
|
|
|
317
373
|
app.echo_success(message)
|
|
318
374
|
|
|
319
375
|
|
|
376
|
+
# pylint: disable=too-many-arguments
|
|
320
377
|
@click.command(cls=CmemcCommand, name="execute")
|
|
321
378
|
@click.option(
|
|
322
379
|
"-a", "--all", "all_",
|
|
@@ -326,7 +383,12 @@ def _workflow_echo_status(app, status):
|
|
|
326
383
|
@click.option(
|
|
327
384
|
"--wait",
|
|
328
385
|
is_flag=True,
|
|
329
|
-
help="Wait until
|
|
386
|
+
help="Wait until workflows are completed."
|
|
387
|
+
)
|
|
388
|
+
@click.option(
|
|
389
|
+
"--progress",
|
|
390
|
+
is_flag=True,
|
|
391
|
+
help="Wait until workflows are completed and show a progress bar."
|
|
330
392
|
)
|
|
331
393
|
@click.option(
|
|
332
394
|
"--polling-interval",
|
|
@@ -340,10 +402,10 @@ def _workflow_echo_status(app, status):
|
|
|
340
402
|
"workflow_ids",
|
|
341
403
|
nargs=-1,
|
|
342
404
|
type=click.STRING,
|
|
343
|
-
|
|
405
|
+
shell_complete=completion.workflow_ids
|
|
344
406
|
)
|
|
345
407
|
@click.pass_obj
|
|
346
|
-
def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
408
|
+
def execute_command(app, all_, wait, polling_interval, workflow_ids, progress):
|
|
347
409
|
"""Execute workflow(s).
|
|
348
410
|
|
|
349
411
|
With this command, you can start one or more workflows at the same time or
|
|
@@ -358,16 +420,16 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
358
420
|
polls the status of a workflow until it is finished. In case of an error of
|
|
359
421
|
a workflow, the next workflow is not started.
|
|
360
422
|
"""
|
|
361
|
-
all_workflow_ids = _workflows_get_ids()
|
|
362
423
|
if workflow_ids == () and not all_:
|
|
363
424
|
raise ValueError("Either specify at least one workflow or use the"
|
|
364
425
|
+ " --all option to execute all workflows.")
|
|
426
|
+
all_workflow_ids = _workflows_get_ids()
|
|
365
427
|
if all_:
|
|
366
428
|
workflow_ids = all_workflow_ids
|
|
367
429
|
|
|
368
430
|
for workflow_id in workflow_ids:
|
|
369
431
|
if workflow_id not in all_workflow_ids:
|
|
370
|
-
raise
|
|
432
|
+
raise UsageError(
|
|
371
433
|
f"Workflow '{workflow_id}' is not available."
|
|
372
434
|
)
|
|
373
435
|
project_id, task_id = workflow_id.split(":")
|
|
@@ -375,7 +437,7 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
375
437
|
|
|
376
438
|
# before we start, we fetch the status
|
|
377
439
|
status = get_activity_status(project_id, task_id)
|
|
378
|
-
if not wait:
|
|
440
|
+
if not wait and not progress:
|
|
379
441
|
if status['isRunning']:
|
|
380
442
|
# in case of a running workflow, we only output status
|
|
381
443
|
app.echo_info('Already Running')
|
|
@@ -384,7 +446,7 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
384
446
|
start_task_activity(project_id, task_id)
|
|
385
447
|
app.echo_info('Started')
|
|
386
448
|
else:
|
|
387
|
-
# in case of --wait, we poll the status until finished
|
|
449
|
+
# in case of --wait or --progress, we poll the status until finished
|
|
388
450
|
if status['isRunning']:
|
|
389
451
|
# in case of a running workflow, we only output status
|
|
390
452
|
app.echo_info('Already Running ... ', nl=False)
|
|
@@ -393,10 +455,11 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
393
455
|
app.echo_info('Started ... ', nl=False)
|
|
394
456
|
|
|
395
457
|
status = _workflow_wait_until_finished(
|
|
396
|
-
project_id, task_id, polling_interval
|
|
458
|
+
app, project_id, task_id, polling_interval, progress
|
|
397
459
|
)
|
|
398
460
|
# when we have a Finished status, we output it
|
|
399
|
-
|
|
461
|
+
if not progress:
|
|
462
|
+
_workflow_echo_status(app, status)
|
|
400
463
|
# in case of failure, the following workflows are not executed
|
|
401
464
|
if status['failed']:
|
|
402
465
|
sys.exit(1)
|
|
@@ -411,10 +474,9 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
411
474
|
dir_okay=False,
|
|
412
475
|
readable=True
|
|
413
476
|
),
|
|
414
|
-
|
|
415
|
-
help="From which file the input is taken
|
|
416
|
-
"
|
|
417
|
-
"has no defined variable input dataset, this can be ignored."
|
|
477
|
+
shell_complete=completion.workflow_io_input_files,
|
|
478
|
+
help="From which file the input is taken. If the workflow "
|
|
479
|
+
"has no defined variable input dataset, this option is not allowed."
|
|
418
480
|
)
|
|
419
481
|
@click.option(
|
|
420
482
|
"--output", "-o", "output_file",
|
|
@@ -423,25 +485,18 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
423
485
|
dir_okay=False,
|
|
424
486
|
writable=True,
|
|
425
487
|
),
|
|
426
|
-
|
|
427
|
-
help="To which file the result is written to
|
|
488
|
+
shell_complete=completion.workflow_io_output_files,
|
|
489
|
+
help="To which file the result is written to. Use '-' in order to output "
|
|
428
490
|
"the result to stdout. If the workflow has no defined variable "
|
|
429
|
-
"output dataset, this
|
|
491
|
+
"output dataset, this option is not allowed. Please note that the io "
|
|
430
492
|
"command will not warn you on overwriting existing output files."
|
|
431
493
|
)
|
|
432
494
|
@click.option(
|
|
433
495
|
"--input-mimetype",
|
|
434
496
|
help="Which input format should be processed: If not given, cmemc will "
|
|
435
497
|
"try to guess the mime type based on the file extension or will "
|
|
436
|
-
"fail",
|
|
437
|
-
type=click.Choice(
|
|
438
|
-
[
|
|
439
|
-
"guess",
|
|
440
|
-
"application/xml",
|
|
441
|
-
"application/json",
|
|
442
|
-
"text/csv"
|
|
443
|
-
]
|
|
444
|
-
),
|
|
498
|
+
"fail.",
|
|
499
|
+
type=click.Choice(list(VALID_INPUT_EXTENSIONS.values()) + ["guess"]),
|
|
445
500
|
default="guess"
|
|
446
501
|
)
|
|
447
502
|
@click.option(
|
|
@@ -449,10 +504,8 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
449
504
|
help="Which output format should be requested: If not given, cmemc will "
|
|
450
505
|
"try to guess the mime type based on the file extension or will "
|
|
451
506
|
"fail. In case of an output to stdout, a default mime type "
|
|
452
|
-
"will be used (
|
|
453
|
-
type=click.Choice(
|
|
454
|
-
["guess", MIME_XML, MIME_JSON, MIME_NT, MIME_XLS, MIME_CSV]
|
|
455
|
-
),
|
|
507
|
+
"will be used (JSON).",
|
|
508
|
+
type=click.Choice(list(VALID_OUTPUT_EXTENSIONS.values()) + ["guess"]),
|
|
456
509
|
default="guess"
|
|
457
510
|
)
|
|
458
511
|
@click.option(
|
|
@@ -466,7 +519,7 @@ def execute_command(app, all_, wait, polling_interval, workflow_ids):
|
|
|
466
519
|
@click.argument(
|
|
467
520
|
"workflow_id",
|
|
468
521
|
type=click.STRING,
|
|
469
|
-
|
|
522
|
+
shell_complete=completion.workflow_io_ids
|
|
470
523
|
)
|
|
471
524
|
@click.pass_obj
|
|
472
525
|
def io_command(
|
|
@@ -496,6 +549,16 @@ def io_command(
|
|
|
496
549
|
output_mimetype=output_mimetype
|
|
497
550
|
)
|
|
498
551
|
|
|
552
|
+
app.echo_debug(
|
|
553
|
+
f"On workflow io execution:"
|
|
554
|
+
f"project_name={project_id}, "
|
|
555
|
+
f"task_name={task_id}, "
|
|
556
|
+
f"input_file={input_file}, "
|
|
557
|
+
f"input_mime_type={input_mimetype}, "
|
|
558
|
+
f"output_mime_type={output_mimetype}, "
|
|
559
|
+
f"auto_config={autoconfig}"
|
|
560
|
+
)
|
|
561
|
+
|
|
499
562
|
response = execute_workflow_io(
|
|
500
563
|
project_name=project_id,
|
|
501
564
|
task_name=task_id,
|
|
@@ -529,7 +592,7 @@ def io_command(
|
|
|
529
592
|
"--filter", "filter_",
|
|
530
593
|
type=(str, str),
|
|
531
594
|
multiple=True,
|
|
532
|
-
|
|
595
|
+
shell_complete=completion.workflow_list_filter,
|
|
533
596
|
help=WORKFLOW_LIST_FILTER_HELP_TEXT,
|
|
534
597
|
)
|
|
535
598
|
@click.option(
|
|
@@ -582,7 +645,7 @@ def list_command(app, filter_, raw, id_only):
|
|
|
582
645
|
@click.option(
|
|
583
646
|
"--project", "project_id",
|
|
584
647
|
type=click.STRING,
|
|
585
|
-
|
|
648
|
+
shell_complete=completion.project_ids,
|
|
586
649
|
help="The project, from which you want to list the workflows. "
|
|
587
650
|
"Project IDs can be listed with the 'project list' command."
|
|
588
651
|
)
|
|
@@ -600,7 +663,7 @@ def list_command(app, filter_, raw, id_only):
|
|
|
600
663
|
"workflow_ids",
|
|
601
664
|
nargs=-1,
|
|
602
665
|
type=click.STRING,
|
|
603
|
-
|
|
666
|
+
shell_complete=completion.workflow_ids
|
|
604
667
|
)
|
|
605
668
|
@click.pass_obj
|
|
606
669
|
def status_command(app, project_id, raw, _filter, workflow_ids):
|
|
@@ -625,7 +688,7 @@ def status_command(app, project_id, raw, _filter, workflow_ids):
|
|
|
625
688
|
@click.argument(
|
|
626
689
|
"workflow_id",
|
|
627
690
|
type=click.STRING,
|
|
628
|
-
|
|
691
|
+
shell_complete=completion.workflow_ids
|
|
629
692
|
)
|
|
630
693
|
@click.pass_obj
|
|
631
694
|
def open_command(app, workflow_id):
|
|
@@ -26,7 +26,7 @@ from cmem.cmempy.workspace.import_ import import_workspace
|
|
|
26
26
|
default="xmlZip",
|
|
27
27
|
show_default=True,
|
|
28
28
|
type=click.STRING,
|
|
29
|
-
|
|
29
|
+
shell_complete=completion.marshalling_plugins,
|
|
30
30
|
help="Type of the exported workspace file."
|
|
31
31
|
)
|
|
32
32
|
@click.option(
|
|
@@ -34,7 +34,7 @@ from cmem.cmempy.workspace.import_ import import_workspace
|
|
|
34
34
|
default="{{date}}-{{connection}}.workspace",
|
|
35
35
|
show_default=True,
|
|
36
36
|
type=click.STRING,
|
|
37
|
-
|
|
37
|
+
shell_complete=completion.workspace_export_templates,
|
|
38
38
|
help="Template for the export file name. "
|
|
39
39
|
"Possible placeholders are (Jinja2): "
|
|
40
40
|
"{{connection}} (from the --connection option) and "
|
|
@@ -44,7 +44,7 @@ from cmem.cmempy.workspace.import_ import import_workspace
|
|
|
44
44
|
)
|
|
45
45
|
@click.argument(
|
|
46
46
|
"file",
|
|
47
|
-
|
|
47
|
+
shell_complete=completion.workspace_files,
|
|
48
48
|
required=False,
|
|
49
49
|
type=click.Path(
|
|
50
50
|
writable=True,
|
|
@@ -95,12 +95,12 @@ def export_command(app, overwrite, marshalling_plugin, template, file):
|
|
|
95
95
|
default="xmlZip",
|
|
96
96
|
show_default=True,
|
|
97
97
|
type=click.STRING,
|
|
98
|
-
|
|
98
|
+
shell_complete=completion.marshalling_plugins,
|
|
99
99
|
help="Type of the exported workspace file."
|
|
100
100
|
)
|
|
101
101
|
@click.argument(
|
|
102
102
|
"file",
|
|
103
|
-
|
|
103
|
+
shell_complete=completion.workspace_files,
|
|
104
104
|
type=click.Path(
|
|
105
105
|
readable=True,
|
|
106
106
|
allow_dash=False,
|