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.
@@ -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 = "text/csv"
47
- MIME_XLS = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
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
- return VALID_OUTPUT_EXTENSIONS[file_extension]
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
- return VALID_INPUT_EXTENSIONS[file_extension]
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 _workflow_wait_until_finished(project_id, task_id, polling_interval):
261
- """Poll workflow status until workflow is finished and return status.
262
-
263
- :type project_id: string
264
- :type task_id: string
265
- :type polling_interval: int
266
- """
267
- while True:
268
- status = get_activity_status(project_id, task_id)
269
- # wait until isRunning is false
270
- if not status['isRunning']:
271
- break
272
- time.sleep(polling_interval)
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 all executed workflows are completed."
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
- autocompletion=completion.workflow_ids
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 ValueError(
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
- _workflow_echo_status(app, status)
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
- autocompletion=completion.workflow_io_input_files,
415
- help="From which file the input is taken: note that the maximum file size "
416
- "to upload is limited to a server configured value. If the workflow "
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
- autocompletion=completion.workflow_io_output_files,
427
- help="To which file the result is written to: use '-' in order to output "
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 can be ignored. Please note that the io "
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 (currently xml).",
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
- autocompletion=completion.workflow_io_ids
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
- autocompletion=completion.workflow_list_filter,
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
- autocompletion=completion.project_ids,
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
- autocompletion=completion.workflow_ids
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
- autocompletion=completion.workflow_ids
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
- autocompletion=completion.marshalling_plugins,
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
- autocompletion=completion.workspace_export_templates,
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
- autocompletion=completion.workspace_files,
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
- autocompletion=completion.marshalling_plugins,
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
- autocompletion=completion.workspace_files,
103
+ shell_complete=completion.workspace_files,
104
104
  type=click.Path(
105
105
  readable=True,
106
106
  allow_dash=False,