cmem-cmemc 24.2.0rc2__py3-none-any.whl → 24.3.0rc1__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.
Files changed (51) hide show
  1. cmem_cmemc/__init__.py +7 -12
  2. cmem_cmemc/command.py +20 -0
  3. cmem_cmemc/command_group.py +70 -0
  4. cmem_cmemc/commands/__init__.py +0 -81
  5. cmem_cmemc/commands/acl.py +118 -62
  6. cmem_cmemc/commands/admin.py +46 -35
  7. cmem_cmemc/commands/client.py +2 -1
  8. cmem_cmemc/commands/config.py +3 -1
  9. cmem_cmemc/commands/dataset.py +27 -24
  10. cmem_cmemc/commands/graph.py +100 -16
  11. cmem_cmemc/commands/metrics.py +195 -79
  12. cmem_cmemc/commands/migration.py +265 -0
  13. cmem_cmemc/commands/project.py +62 -17
  14. cmem_cmemc/commands/python.py +57 -26
  15. cmem_cmemc/commands/query.py +23 -14
  16. cmem_cmemc/commands/resource.py +10 -2
  17. cmem_cmemc/commands/scheduler.py +10 -2
  18. cmem_cmemc/commands/store.py +118 -14
  19. cmem_cmemc/commands/user.py +8 -2
  20. cmem_cmemc/commands/validation.py +165 -78
  21. cmem_cmemc/commands/variable.py +10 -2
  22. cmem_cmemc/commands/vocabulary.py +48 -29
  23. cmem_cmemc/commands/workflow.py +86 -59
  24. cmem_cmemc/commands/workspace.py +27 -8
  25. cmem_cmemc/completion.py +185 -141
  26. cmem_cmemc/constants.py +2 -0
  27. cmem_cmemc/context.py +88 -42
  28. cmem_cmemc/manual_helper/graph.py +1 -0
  29. cmem_cmemc/manual_helper/multi_page.py +3 -1
  30. cmem_cmemc/migrations/__init__.py +1 -0
  31. cmem_cmemc/migrations/abc.py +84 -0
  32. cmem_cmemc/migrations/access_conditions_243.py +118 -0
  33. cmem_cmemc/migrations/bootstrap_data.py +30 -0
  34. cmem_cmemc/migrations/shapes_widget_integrations_243.py +194 -0
  35. cmem_cmemc/migrations/workspace_configurations.py +28 -0
  36. cmem_cmemc/object_list.py +53 -22
  37. cmem_cmemc/parameter_types/__init__.py +1 -0
  38. cmem_cmemc/parameter_types/path.py +69 -0
  39. cmem_cmemc/smart_path/__init__.py +94 -0
  40. cmem_cmemc/smart_path/clients/__init__.py +63 -0
  41. cmem_cmemc/smart_path/clients/http.py +65 -0
  42. cmem_cmemc/string_processor.py +77 -0
  43. cmem_cmemc/title_helper.py +41 -0
  44. cmem_cmemc/utils.py +114 -47
  45. {cmem_cmemc-24.2.0rc2.dist-info → cmem_cmemc-24.3.0rc1.dist-info}/LICENSE +1 -1
  46. cmem_cmemc-24.3.0rc1.dist-info/METADATA +89 -0
  47. cmem_cmemc-24.3.0rc1.dist-info/RECORD +53 -0
  48. {cmem_cmemc-24.2.0rc2.dist-info → cmem_cmemc-24.3.0rc1.dist-info}/WHEEL +1 -1
  49. cmem_cmemc-24.2.0rc2.dist-info/METADATA +0 -69
  50. cmem_cmemc-24.2.0rc2.dist-info/RECORD +0 -37
  51. {cmem_cmemc-24.2.0rc2.dist-info → cmem_cmemc-24.3.0rc1.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  """vocabularies commands for cmem command line interface."""
2
+
2
3
  import io
3
- from datetime import UTC, datetime
4
+ from datetime import datetime, timezone
4
5
  from re import match
5
6
  from urllib.parse import urlparse
6
7
 
@@ -21,8 +22,10 @@ from rdflib.plugins.parsers.notation3 import BadSyntax
21
22
  from six.moves.urllib.parse import quote
22
23
 
23
24
  from cmem_cmemc import completion
24
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
25
+ from cmem_cmemc.command import CmemcCommand
26
+ from cmem_cmemc.command_group import CmemcGroup
25
27
  from cmem_cmemc.context import ApplicationContext
28
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
26
29
 
27
30
  GET_ONTOLOGY_IRI_QUERY = """
28
31
  PREFIX owl: <http://www.w3.org/2002/07/owl#>
@@ -71,22 +74,29 @@ def _validate_vocabs_to_process(iris: tuple[str], filter_: str, all_flag: bool)
71
74
  list is without duplicates, and validated if they exist
72
75
  """
73
76
  if iris == () and not all_flag:
74
- raise ValueError(
77
+ raise click.UsageError(
75
78
  "Either specify at least one vocabulary IRI "
76
79
  "or use the --all option to process over all vocabularies."
77
80
  )
78
- vocabs_to_process = list(iris)
79
- all_vocabs = [_["iri"] for _ in get_vocabularies(filter_=filter_)]
81
+ all_vocabs = {_["iri"]: _ for _ in get_vocabularies()}
80
82
  if all_flag:
81
- # in case --all is given, all vocabs are processed
82
- vocabs_to_process = all_vocabs
83
- # avoid double removal
84
- vocabs_to_process = list(set(vocabs_to_process))
85
-
86
- # test if one of the vocabs does not exist
83
+ # in case --all is given, all installable / installed vocabs are processed
84
+ if filter_ == "installed": # uninstall command
85
+ return [_ for _ in all_vocabs if all_vocabs[_]["installed"]]
86
+ # install command
87
+ return [_ for _ in all_vocabs if not all_vocabs[_]["installed"]]
88
+
89
+ vocabs_to_process = list(set(iris)) # avoid double removal / installation
90
+ # test if one of the vocabs does not exist or is already installed / not installed
87
91
  for _ in vocabs_to_process:
88
- if _ not in all_vocabs:
89
- raise ValueError(f"Vocabulary {_} does not exist.")
92
+ # uninstall command
93
+ if filter_ == "installed" and (_ not in all_vocabs or not all_vocabs[_]["installed"]):
94
+ raise click.UsageError(f"Vocabulary {_} not installed.")
95
+ if filter_ == "installable": # install command
96
+ if _ not in all_vocabs:
97
+ raise click.UsageError(f"Vocabulary {_} does not exist.")
98
+ if all_vocabs[_]["installed"]:
99
+ raise click.UsageError(f"Vocabulary {_} already installed.")
90
100
  return vocabs_to_process
91
101
 
92
102
 
@@ -140,7 +150,7 @@ def _insert_catalog_entry(iri: str, prefix: str, namespace: str, label: str, lan
140
150
  iri=iri,
141
151
  prefix=prefix,
142
152
  namespace=namespace,
143
- date=datetime.now(tz=UTC).date(),
153
+ date=datetime.now(tz=timezone.utc).date(),
144
154
  label=label,
145
155
  language=language,
146
156
  description="vocabulary imported with cmemc",
@@ -150,7 +160,7 @@ def _insert_catalog_entry(iri: str, prefix: str, namespace: str, label: str, lan
150
160
 
151
161
 
152
162
  def _get_vocabulary_metadata_from_file(
153
- file: io.StringIO, namespace_given: bool = False
163
+ file: io.BytesIO, namespace_given: bool = False
154
164
  ) -> dict[str, str]:
155
165
  """Get potential graph iri and prefix/namespace from a turtle file."""
156
166
  metadata = {"iri": "", "prefix": "", "namespace": ""}
@@ -263,7 +273,13 @@ def list_command(app: ApplicationContext, id_only: bool, filter_: str, raw: bool
263
273
  except (KeyError, TypeError):
264
274
  label = _["vocabularyLabel"] if _["vocabularyLabel"] else "[no label given]"
265
275
  table.append((iri, label))
266
- app.echo_info_table(table, headers=["Vocabulary Graph IRI", "Label"], sort_column=1)
276
+ app.echo_info_table(
277
+ table,
278
+ headers=["Vocabulary Graph IRI", "Label"],
279
+ sort_column=1,
280
+ empty_table_message="No installed vocabularies found. "
281
+ "Use the `vocabulary install` command to install vocabulary from the catalog.",
282
+ )
267
283
 
268
284
 
269
285
  @click.command(cls=CmemcCommand, name="install")
@@ -315,10 +331,7 @@ def uninstall_command(app: ApplicationContext, iris: tuple[str], all_: bool) ->
315
331
  "FILE",
316
332
  required=True,
317
333
  shell_complete=completion.triple_files,
318
- type=click.Path(
319
- allow_dash=True,
320
- readable=True,
321
- ),
334
+ type=ClickSmartPath(allow_dash=True, readable=True, remote_okay=True),
322
335
  )
323
336
  @click.option(
324
337
  "--namespace",
@@ -345,19 +358,19 @@ def import_command(
345
358
  description which correctly uses the `vann:preferredNamespacePrefix` and
346
359
  `vann:preferredNamespaceUri` properties.
347
360
  """
348
- string_buffer = io.StringIO()
349
- with click.open_file(file, "rb") as file_handle:
350
- string_buffer.write(file_handle.read().decode("utf-8"))
351
- string_buffer.seek(0)
361
+ _buffer = io.BytesIO()
362
+ with ClickSmartPath.open(file) as file_handle:
363
+ _buffer.write(file_handle.read())
364
+ _buffer.seek(0)
352
365
 
353
366
  # fetch metadata
354
367
  if namespace != (None, None):
355
368
  _validate_namespace(app, namespace)
356
- meta_data = _get_vocabulary_metadata_from_file(string_buffer, True)
369
+ meta_data = _get_vocabulary_metadata_from_file(_buffer, True)
357
370
  meta_data["prefix"] = namespace[0] # type: ignore[assignment]
358
371
  meta_data["namespace"] = namespace[1] # type: ignore[assignment]
359
372
  else:
360
- meta_data = _get_vocabulary_metadata_from_file(string_buffer, False)
373
+ meta_data = _get_vocabulary_metadata_from_file(_buffer, False)
361
374
  iri = meta_data["iri"]
362
375
 
363
376
  success_message = "done"
@@ -368,8 +381,8 @@ def import_command(
368
381
  raise ValueError(f"Proposed graph {iri} does already exist.")
369
382
  app.echo_info(f"Import {file} as vocabulary to {iri} ... ", nl=False)
370
383
  # upload graph
371
- string_buffer.seek(0)
372
- graph_api.post_streamed(iri, string_buffer, replace=True)
384
+ _buffer.seek(0)
385
+ graph_api.post_streamed(iri, _buffer, replace=True)
373
386
 
374
387
  # resolve label
375
388
  resolved_label_object: dict = resolve([iri], graph=iri)[iri]
@@ -438,7 +451,13 @@ def cache_list_command(app: ApplicationContext, id_only: bool, raw: bool) -> Non
438
451
  for vocab in cache_["vocabularies"]:
439
452
  table = _transform_cache_to_table(vocab["classes"], table)
440
453
  table = _transform_cache_to_table(vocab["properties"], table)
441
- app.echo_info_table(table, headers=["IRI", "Type", "Label"], sort_column=0)
454
+ app.echo_info_table(
455
+ table,
456
+ headers=["IRI", "Type", "Label"],
457
+ sort_column=0,
458
+ empty_table_message="No cache entries found. "
459
+ "Use the `vocabulary install` command to install a vocabulary.",
460
+ )
442
461
 
443
462
 
444
463
  @click.group(cls=CmemcGroup)
@@ -1,14 +1,14 @@
1
1
  """workflow commands for cmem command line interface."""
2
+
2
3
  import re
3
4
  import sys
4
5
  import time
5
- from datetime import UTC, datetime
6
- from pathlib import Path
6
+ from datetime import datetime, timezone
7
7
 
8
8
  import click
9
9
  import timeago
10
10
  from click import UsageError
11
- from cmem.cmempy.workflow import get_resource_based_dataset_types, get_workflows
11
+ from cmem.cmempy.workflow import get_workflows
12
12
  from cmem.cmempy.workflow.workflow import (
13
13
  execute_workflow_io,
14
14
  get_workflow_editor_uri,
@@ -26,9 +26,12 @@ from requests import Response
26
26
  from requests.status_codes import codes
27
27
 
28
28
  from cmem_cmemc import completion
29
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
29
+ from cmem_cmemc.command import CmemcCommand
30
+ from cmem_cmemc.command_group import CmemcGroup
30
31
  from cmem_cmemc.commands.scheduler import scheduler
31
32
  from cmem_cmemc.context import ApplicationContext
33
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
34
+ from cmem_cmemc.smart_path import SmartPath as Path
32
35
 
33
36
  WORKFLOW_FILTER_TYPES = sorted(["project", "regex", "tag", "io"])
34
37
  WORKFLOW_LIST_FILTER_HELP_TEXT = (
@@ -40,40 +43,41 @@ WORKFLOW_LIST_FILTER_HELP_TEXT = (
40
43
  IO_WARNING_NO_RESULT = "The workflow was executed but produced no result."
41
44
  IO_WARNING_NO_OUTPUT_DEFINED = "The workflow was executed, a result was " "received but dropped."
42
45
 
43
- MIME_CSV = "application/x-plugin-csv"
44
- MIME_XLS = "application/x-plugin-excel"
45
- MIME_NT = "application/n-triples"
46
- MIME_JSON = "application/x-plugin-json"
47
- MIME_XML = "application/xml"
48
- MIME_FILE = "application/octet-stream"
49
- MIME_ZIP = "application/x-plugin-multiCsv"
50
- MIME_ALIGNMENT = "text/alignment"
51
- MIME_TEXT = "text/plain"
52
-
53
- VALID_OUTPUT_EXTENSIONS = {
54
- ".csv": MIME_CSV,
55
- ".xlsx": MIME_XLS,
56
- ".nt": MIME_NT,
57
- ".ttl": MIME_NT,
58
- ".json": MIME_JSON,
59
- ".xml": MIME_XML,
60
- }
61
46
 
62
- VALID_INPUT_EXTENSIONS = {
63
- ".csv": MIME_CSV,
64
- ".json": MIME_JSON,
65
- ".xml": MIME_XML,
66
- ".xlsx": MIME_XLS,
67
- ".file": MIME_FILE,
68
- ".zip": MIME_ZIP,
69
- ".txt": MIME_TEXT,
47
+ FILE_EXTENSIONS_TO_PLUGIN_ID = {
48
+ ".nt": "file",
49
+ ".ttl": "file",
50
+ ".csv": "csv",
51
+ ".json": "json",
52
+ ".xml": "xml",
53
+ ".txt": "text",
54
+ ".xlsx": "excel",
55
+ ".zip": "multiCsv",
70
56
  }
71
57
 
72
-
73
- def is_supported_mime_type(mime_type: str) -> bool:
74
- """Boolean to determine if a request is multipart or not"""
75
- supported_mime_types = get_resource_based_dataset_types()
76
- return any(supported_mime_type in mime_type for supported_mime_type in supported_mime_types)
58
+ # Derive valid extensions from FILE_EXTENSIONS_TO_PLUGIN_ID keys
59
+ VALID_EXTENSIONS = list(FILE_EXTENSIONS_TO_PLUGIN_ID.keys())
60
+ PLUGIN_MIME_TYPES = [f"application/x-plugin-{_}" for _ in FILE_EXTENSIONS_TO_PLUGIN_ID.values()]
61
+ # Define additional mime types for input and output
62
+ EXTRA_INPUT_MIME_TYPES = [
63
+ "application/json",
64
+ "application/xml",
65
+ "text/csv",
66
+ ]
67
+
68
+ EXTRA_OUTPUT_MIME_TYPES = [
69
+ "application/json",
70
+ "application/xml",
71
+ "application/n-triples",
72
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
73
+ "text/csv",
74
+ ]
75
+
76
+ STDOUT_UNSUPPORTED_MIME_TYPES = {
77
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "excel",
78
+ "application/x-plugin-excel": "excel",
79
+ "application/x-plugin-multiCsv": "ZIP",
80
+ }
77
81
 
78
82
 
79
83
  def _get_workflow_tag_labels(workflow_: dict) -> list:
@@ -185,9 +189,10 @@ def _io_check_request(info: dict, input_file: str, output_file: str, output_mime
185
189
  "This workflow has a defined output so you need to use the '-o' "
186
190
  "parameter to retrieve data from it."
187
191
  )
188
- if output_mimetype == MIME_XLS and output_file == "-":
192
+ if output_mimetype in STDOUT_UNSUPPORTED_MIME_TYPES and output_file == "-":
189
193
  raise ValueError(
190
- "Trying to output an excel document to stdout will fail.\n"
194
+ f"Trying to output an {STDOUT_UNSUPPORTED_MIME_TYPES[output_mimetype]} "
195
+ "file to stdout will fail.\n"
191
196
  "Please output to a regular file instead "
192
197
  "(workflow was not executed)."
193
198
  )
@@ -226,11 +231,9 @@ def _io_guess_output(output_file: str) -> str:
226
231
  if output_file == "-":
227
232
  raise ValueError("Output mime-type not guessable, please use the --output-mimetype option.")
228
233
  file_extension = Path(output_file).suffix
229
- if file_extension in VALID_OUTPUT_EXTENSIONS and is_supported_mime_type(
230
- VALID_OUTPUT_EXTENSIONS[file_extension]
231
- ):
232
- return VALID_OUTPUT_EXTENSIONS[file_extension]
233
- valid_extensions = ", ".join(VALID_OUTPUT_EXTENSIONS.keys())
234
+ if file_extension in VALID_EXTENSIONS:
235
+ return f"application/x-plugin-{FILE_EXTENSIONS_TO_PLUGIN_ID[file_extension]}"
236
+ valid_extensions = ", ".join(VALID_EXTENSIONS)
234
237
  raise ValueError(
235
238
  f"Files with the extension {file_extension} can not be generated. "
236
239
  f"Try one of {valid_extensions}"
@@ -242,11 +245,9 @@ def _io_guess_input(input_file: str) -> str:
242
245
  if input_file == "-":
243
246
  raise ValueError("Input mime-type not guessable, please use the --output-mimetype option.")
244
247
  file_extension = Path(input_file).suffix
245
- if file_extension in VALID_INPUT_EXTENSIONS and is_supported_mime_type(
246
- VALID_INPUT_EXTENSIONS[file_extension]
247
- ):
248
- return VALID_INPUT_EXTENSIONS[file_extension]
249
- valid_extensions = ", ".join(VALID_INPUT_EXTENSIONS.keys())
248
+ if file_extension in VALID_EXTENSIONS:
249
+ return f"application/x-plugin-{FILE_EXTENSIONS_TO_PLUGIN_ID[file_extension]}"
250
+ valid_extensions = ", ".join(VALID_EXTENSIONS)
250
251
  raise ValueError(
251
252
  f"Files with the extension {file_extension} can not be processed. "
252
253
  f"Try one of {valid_extensions}"
@@ -319,8 +320,8 @@ def _workflow_echo_status(app: ApplicationContext, status: dict) -> None:
319
320
  canceled only exists sometimes
320
321
  """
321
322
  # prepare human friendly relative time
322
- now = datetime.now(tz=UTC)
323
- stamp = datetime.fromtimestamp(status["lastUpdateTime"] / 1000, tz=UTC)
323
+ now = datetime.now(tz=timezone.utc)
324
+ stamp = datetime.fromtimestamp(status["lastUpdateTime"] / 1000, tz=timezone.utc)
324
325
  time_ago = timeago.format(stamp, now, "en")
325
326
  status_name = status["statusName"]
326
327
  status_message = status["message"]
@@ -338,9 +339,9 @@ def _workflow_echo_status(app: ApplicationContext, status: dict) -> None:
338
339
  f"statusName is {status_name}, expecting one of: " "Running, Canceling or Waiting."
339
340
  )
340
341
  # not running can be Idle or Finished
341
- if "failed" in status and status["failed"]:
342
+ if status.get("failed"):
342
343
  app.echo_error(message, nl=True, err=False)
343
- elif "cancelled" in status and status["cancelled"]:
344
+ elif status.get("cancelled"):
344
345
  app.echo_warning(message)
345
346
  elif status["statusName"] == "Idle":
346
347
  app.echo_info(message)
@@ -440,7 +441,7 @@ def execute_command( # noqa: PLR0913
440
441
  "--input",
441
442
  "-i",
442
443
  "input_file",
443
- type=click.Path(allow_dash=False, dir_okay=False, readable=True),
444
+ type=ClickSmartPath(allow_dash=False, dir_okay=False, readable=True),
444
445
  shell_complete=completion.workflow_io_input_files,
445
446
  help="From which file the input is taken. If the workflow "
446
447
  "has no defined variable input dataset, this option is not allowed.",
@@ -449,7 +450,7 @@ def execute_command( # noqa: PLR0913
449
450
  "--output",
450
451
  "-o",
451
452
  "output_file",
452
- type=click.Path(
453
+ type=ClickSmartPath(
453
454
  allow_dash=False,
454
455
  dir_okay=False,
455
456
  writable=True,
@@ -465,7 +466,13 @@ def execute_command( # noqa: PLR0913
465
466
  help="Which input format should be processed: If not given, cmemc will "
466
467
  "try to guess the mime type based on the file extension or will "
467
468
  "fail.",
468
- type=click.Choice([*list(VALID_INPUT_EXTENSIONS.values()), "guess"]),
469
+ type=click.Choice(
470
+ [
471
+ *PLUGIN_MIME_TYPES,
472
+ *EXTRA_INPUT_MIME_TYPES,
473
+ "guess",
474
+ ]
475
+ ),
469
476
  default="guess",
470
477
  )
471
478
  @click.option(
@@ -474,7 +481,13 @@ def execute_command( # noqa: PLR0913
474
481
  "try to guess the mime type based on the file extension or will "
475
482
  "fail. In case of an output to stdout, a default mime type "
476
483
  "will be used (JSON).",
477
- type=click.Choice([*list(VALID_OUTPUT_EXTENSIONS.values()), "guess"]),
484
+ type=click.Choice(
485
+ [
486
+ *PLUGIN_MIME_TYPES,
487
+ *EXTRA_OUTPUT_MIME_TYPES,
488
+ "guess",
489
+ ]
490
+ ),
478
491
  default="guess",
479
492
  )
480
493
  @click.option(
@@ -498,10 +511,19 @@ def io_command( # noqa: PLR0913
498
511
  ) -> None:
499
512
  """Execute a workflow with file input/output.
500
513
 
501
- With this command, you can execute a workflow that uses variable datasets
514
+ With this command, you can execute a workflow that uses replaceable datasets
502
515
  as input, output or for configuration. Use the input parameter to feed
503
516
  data into the workflow. Likewise, use output for retrieval of the workflow
504
- result. Workflows without a variable dataset will throw an error.
517
+ result. Workflows without a replaceable dataset will throw an error.
518
+
519
+ Note: Regarding the input dataset configuration - the following rules apply:
520
+ If autoconfig is enabled ('--autoconfig', the default), the dataset
521
+ configuration is guessed.
522
+ If autoconfig is disabled ('--no-autoconfig') and the type of the dataset
523
+ file is the same as the replaceable dataset in the workflow, the configuration
524
+ from this dataset is copied.
525
+ If autoconfig is disabled and the type of the dataset file is different from the
526
+ replaceable dataset in the workflow, the default config is used.
505
527
  """
506
528
  project_id, task_id = workflow_id.split(":")
507
529
  if output_file and output_mimetype == "guess":
@@ -525,7 +547,6 @@ def io_command( # noqa: PLR0913
525
547
  f"output_mime_type={output_mimetype}, "
526
548
  f"auto_config={autoconfig}"
527
549
  )
528
-
529
550
  response = execute_workflow_io(
530
551
  project_name=project_id,
531
552
  task_name=task_id,
@@ -597,7 +618,13 @@ def list_command(
597
618
  _["label"],
598
619
  ]
599
620
  table.append(row)
600
- app.echo_info_table(table, headers=["Workflow ID", "Label"], sort_column=1)
621
+ app.echo_info_table(
622
+ table,
623
+ headers=["Workflow ID", "Label"],
624
+ sort_column=1,
625
+ empty_table_message="No workflows found. "
626
+ "Open a project in the web interface and create a new workflow there.",
627
+ )
601
628
 
602
629
 
603
630
  @click.command(cls=CmemcCommand, name="status")
@@ -1,6 +1,6 @@
1
1
  """workspace commands for cmem command line interface."""
2
+
2
3
  import os
3
- from pathlib import Path
4
4
 
5
5
  import click
6
6
  from cmem.cmempy.workspace import reload_workspace
@@ -9,9 +9,12 @@ from cmem.cmempy.workspace.import_ import import_workspace
9
9
  from jinja2 import Template
10
10
 
11
11
  from cmem_cmemc import completion
12
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
12
+ from cmem_cmemc.command import CmemcCommand
13
+ from cmem_cmemc.command_group import CmemcGroup
13
14
  from cmem_cmemc.commands.python import python
14
15
  from cmem_cmemc.context import ApplicationContext
16
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
17
+ from cmem_cmemc.smart_path import SmartPath as Path
15
18
 
16
19
 
17
20
  @click.command(cls=CmemcCommand, name="export")
@@ -19,7 +22,12 @@ from cmem_cmemc.context import ApplicationContext
19
22
  "-o",
20
23
  "--overwrite",
21
24
  is_flag=True,
22
- help="Overwrite existing files. " "This is a dangerous option, so use it with care.",
25
+ hidden=True,
26
+ )
27
+ @click.option(
28
+ "--replace",
29
+ is_flag=True,
30
+ help="Replace existing files. This is a dangerous option, so use it with care.",
23
31
  )
24
32
  @click.option(
25
33
  "--type",
@@ -49,11 +57,16 @@ from cmem_cmemc.context import ApplicationContext
49
57
  "file",
50
58
  shell_complete=completion.workspace_files,
51
59
  required=False,
52
- type=click.Path(writable=True, allow_dash=False, dir_okay=False),
60
+ type=ClickSmartPath(writable=True, allow_dash=False, dir_okay=False),
53
61
  )
54
62
  @click.pass_obj
55
- def export_command(
56
- app: ApplicationContext, overwrite: bool, marshalling_plugin: str, template: str, file: str
63
+ def export_command( # noqa: PLR0913
64
+ app: ApplicationContext,
65
+ overwrite: bool,
66
+ replace: bool,
67
+ marshalling_plugin: str,
68
+ template: str,
69
+ file: str,
57
70
  ) -> None:
58
71
  """Export the complete workspace (all projects) to a ZIP file.
59
72
 
@@ -64,13 +77,19 @@ def export_command(
64
77
  The file name is optional and will be generated with by
65
78
  the template if absent.
66
79
  """
80
+ if overwrite:
81
+ replace = overwrite
82
+ app.echo_warning(
83
+ "The option --overwrite is deprecated and will be removed with the next major release."
84
+ " Please use the --replace option instead."
85
+ )
67
86
  if file is None:
68
87
  # prepare the template data and create the actual file incl. suffix
69
88
  template_data = app.get_template_data()
70
89
  file = Template(template).render(template_data) + ".zip"
71
90
  file = os.path.normpath(file)
72
91
  app.echo_info(f"Export workspace to {file} ... ", nl=False)
73
- if Path(file).exists() and overwrite is not True:
92
+ if Path(file).exists() and replace is not True:
74
93
  app.echo_error("file exists")
75
94
  else:
76
95
  # output directory is created lazy
@@ -95,7 +114,7 @@ def export_command(
95
114
  @click.argument(
96
115
  "file",
97
116
  shell_complete=completion.workspace_files,
98
- type=click.Path(readable=True, allow_dash=False, dir_okay=False),
117
+ type=ClickSmartPath(readable=True, allow_dash=False, dir_okay=False),
99
118
  )
100
119
  @click.pass_obj
101
120
  def import_command(app: ApplicationContext, file: str, marshalling_plugin: str) -> None: