cmem-cmemc 24.2.0rc2__py3-none-any.whl → 24.3.0rc2__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 +160 -19
  11. cmem_cmemc/commands/metrics.py +195 -79
  12. cmem_cmemc/commands/migration.py +267 -0
  13. cmem_cmemc/commands/project.py +62 -17
  14. cmem_cmemc/commands/python.py +56 -25
  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 +190 -140
  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 +122 -0
  33. cmem_cmemc/migrations/bootstrap_data.py +28 -0
  34. cmem_cmemc/migrations/shapes_widget_integrations_243.py +274 -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 +83 -0
  43. cmem_cmemc/title_helper.py +41 -0
  44. cmem_cmemc/utils.py +100 -45
  45. {cmem_cmemc-24.2.0rc2.dist-info → cmem_cmemc-24.3.0rc2.dist-info}/LICENSE +1 -1
  46. cmem_cmemc-24.3.0rc2.dist-info/METADATA +89 -0
  47. cmem_cmemc-24.3.0rc2.dist-info/RECORD +53 -0
  48. {cmem_cmemc-24.2.0rc2.dist-info → cmem_cmemc-24.3.0rc2.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.0rc2.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,9 @@
1
1
  """DataIntegration project commands for the cmem command line interface."""
2
+
2
3
  import os
4
+ import pathlib
3
5
  import shutil
4
6
  import tempfile
5
- from pathlib import Path
6
7
  from zipfile import ZipFile
7
8
 
8
9
  import click
@@ -29,9 +30,12 @@ from cmem.cmempy.workspace.projects.project import (
29
30
  from jinja2 import Template
30
31
 
31
32
  from cmem_cmemc import completion
32
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
33
+ from cmem_cmemc.command import CmemcCommand
34
+ from cmem_cmemc.command_group import CmemcGroup
33
35
  from cmem_cmemc.commands.variable import variable
34
36
  from cmem_cmemc.context import ApplicationContext
37
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
38
+ from cmem_cmemc.smart_path import SmartPath as Path
35
39
 
36
40
 
37
41
  def _validate_projects_to_process(project_ids: tuple[str], all_flag: bool) -> list[str]:
@@ -137,7 +141,13 @@ def list_command(app: ApplicationContext, raw: bool, id_only: bool) -> None:
137
141
  _["metaData"]["label"],
138
142
  ]
139
143
  table.append(row)
140
- app.echo_info_table(table, headers=["Project ID", "Label"], sort_column=1)
144
+ app.echo_info_table(
145
+ table,
146
+ headers=["Project ID", "Label"],
147
+ sort_column=1,
148
+ empty_table_message="No projects found. "
149
+ "Use the `project create` command to create a new project.",
150
+ )
141
151
 
142
152
 
143
153
  @click.command(cls=CmemcCommand, name="delete")
@@ -283,13 +293,18 @@ def create_command(
283
293
  "-o",
284
294
  "--overwrite",
285
295
  is_flag=True,
286
- help="Overwrite existing files. This is a dangerous option, " "so use it with care.",
296
+ hidden=True,
297
+ )
298
+ @click.option(
299
+ "--replace",
300
+ is_flag=True,
301
+ help="Replace existing files. This is a dangerous option, so use it with care.",
287
302
  )
288
303
  @click.option(
289
304
  "--output-dir",
290
305
  default=".",
291
306
  show_default=True,
292
- type=click.Path(writable=True, file_okay=False),
307
+ type=ClickSmartPath(writable=True, file_okay=False),
293
308
  help="The base directory, where the project files will be created. "
294
309
  "If this directory does not exist, it will be silently created.",
295
310
  )
@@ -334,6 +349,7 @@ def export_command( # noqa: PLR0913
334
349
  all_: bool,
335
350
  project_ids: tuple[str],
336
351
  overwrite: bool,
352
+ replace: bool,
337
353
  marshalling_plugin: str,
338
354
  template: str,
339
355
  output_dir: str,
@@ -356,6 +372,13 @@ def export_command( # noqa: PLR0913
356
372
  Example: cmemc config list | parallel -I% cmemc -c % project export --all
357
373
  -t "dump/{{connection}}/{{date}}-{{id}}.project"
358
374
  """
375
+ if overwrite:
376
+ replace = overwrite
377
+ app.echo_warning(
378
+ "The option --overwrite is deprecated and will be removed with the next major release."
379
+ " Please use the --replace option instead."
380
+ )
381
+
359
382
  if help_types:
360
383
  _show_type_list(app)
361
384
  return
@@ -384,12 +407,12 @@ def export_command( # noqa: PLR0913
384
407
  + get_extension_by_plugin(marshalling_plugin)
385
408
  )
386
409
  # join with given output directory and normalize full path
387
- export_path = os.path.normpath(Path(output_dir) / local_name)
410
+ export_path = os.path.normpath(str(Path(output_dir) / local_name))
388
411
 
389
412
  app.echo_info(
390
413
  f"Export project {current}/{count}: " f"{project_id} to {export_path} ... ", nl=False
391
414
  )
392
- if Path(export_path).exists() and overwrite is not True:
415
+ if Path(export_path).exists() and replace is not True:
393
416
  app.echo_error("target file or directory exists")
394
417
  continue
395
418
 
@@ -418,7 +441,9 @@ def export_command( # noqa: PLR0913
418
441
  @click.argument(
419
442
  "path",
420
443
  shell_complete=completion.project_files,
421
- type=click.Path(allow_dash=False, dir_okay=True, readable=True, exists=True),
444
+ type=ClickSmartPath(
445
+ allow_dash=False, dir_okay=True, readable=True, exists=True, remote_okay=True
446
+ ),
422
447
  )
423
448
  @click.argument(
424
449
  "project_id",
@@ -431,20 +456,34 @@ def export_command( # noqa: PLR0913
431
456
  "-o",
432
457
  "--overwrite",
433
458
  is_flag=True,
434
- help="Overwrite an existing project. This is a dangerous option, " "so use it with care.",
459
+ hidden=True,
460
+ )
461
+ @click.option(
462
+ "--replace",
463
+ is_flag=True,
464
+ help="Replace an existing project. This is a dangerous option, so use it with care.",
435
465
  )
436
466
  @click.pass_obj
437
- def import_command(app: ApplicationContext, path: str, project_id: str, overwrite: bool) -> None:
467
+ def import_command(
468
+ app: ApplicationContext, path: str, project_id: str, overwrite: bool, replace: bool
469
+ ) -> None:
438
470
  """Import a project from a file or directory.
439
471
 
440
472
  Example: cmemc project import my_project.zip my_project
441
473
  """
474
+ if overwrite:
475
+ replace = overwrite
476
+ app.echo_warning(
477
+ "The option --overwrite is deprecated and will be removed with the next major release."
478
+ " Please use the --replace option instead."
479
+ )
480
+
442
481
  all_projects = get_projects()
443
- if project_id and not overwrite and project_id in ([_["name"] for _ in all_projects]):
482
+ if project_id and not replace and project_id in ([_["name"] for _ in all_projects]):
444
483
  raise ValueError(f"Project {project_id} is already there.")
445
484
 
446
485
  if Path(path).is_dir():
447
- if not Path(Path(path) / "config.xml").is_file():
486
+ if not (Path(path) / "config.xml").is_file():
448
487
  # fail early if directory is not an export
449
488
  raise ValueError(f"Directory {path} seems not to be a export directory.")
450
489
 
@@ -453,17 +492,25 @@ def import_command(app: ApplicationContext, path: str, project_id: str, overwrit
453
492
  app.echo_info("zipping ... ", nl=False)
454
493
  with tempfile.NamedTemporaryFile() as _:
455
494
  shutil.make_archive(
456
- _.name, "zip", base_dir=Path(path).name, root_dir=Path(path).parent.absolute()
495
+ _.name,
496
+ "zip",
497
+ base_dir=pathlib.Path(path).name,
498
+ root_dir=str(Path(path).parent.absolute()),
457
499
  )
458
500
  # make_archive adds a .zip automatically ...
459
501
  uploaded_file = _.name + ".zip"
460
502
  app.echo_debug(f"Uploaded file is {uploaded_file}")
461
503
  else:
462
504
  app.echo_info(f"Import file {path} to project {project_id} ... ", nl=False)
463
- uploaded_file = path
505
+ with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as _:
506
+ with ClickSmartPath.open(path) as _buffer:
507
+ _.write(_buffer.read())
508
+ uploaded_file = _.name
464
509
 
465
510
  # upload file and get validation report
466
511
  validation_response = upload_project(uploaded_file)
512
+ # Remove the temporary file
513
+ pathlib.Path.unlink(pathlib.Path(uploaded_file))
467
514
  if "errorMessage" in validation_response:
468
515
  raise ValueError(validation_response["errorMessage"])
469
516
  import_id = validation_response["projectImportId"]
@@ -476,9 +523,7 @@ def import_command(app: ApplicationContext, path: str, project_id: str, overwrit
476
523
 
477
524
  # start import of project from upload using import ID as a reference
478
525
  # this fails if project_id already exists
479
- import_from_upload_start(
480
- import_id=import_id, project_id=project_id, overwrite_existing=overwrite
481
- )
526
+ import_from_upload_start(import_id=import_id, project_id=project_id, overwrite_existing=replace)
482
527
 
483
528
  # loop until "success" boolean is in status response
484
529
  status = import_from_upload_status(import_id)
@@ -1,4 +1,5 @@
1
1
  """DataIntegration python management commands."""
2
+
2
3
  import sys
3
4
  from dataclasses import asdict
4
5
  from re import match
@@ -15,8 +16,10 @@ from cmem.cmempy.workspace.python import (
15
16
  )
16
17
 
17
18
  from cmem_cmemc import completion
18
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
19
+ from cmem_cmemc.command import CmemcCommand
20
+ from cmem_cmemc.command_group import CmemcGroup
19
21
  from cmem_cmemc.context import ApplicationContext
22
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
20
23
  from cmem_cmemc.utils import get_published_packages
21
24
 
22
25
 
@@ -27,16 +30,14 @@ def _get_package_id(module_name: str) -> str:
27
30
 
28
31
  def _looks_like_a_package(package: str) -> bool:
29
32
  """Check if a string looks like a package requirement string."""
30
- if match(r"^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*((==|<=|>=|>|<).*)?$", package):
31
- return True
32
- return False
33
+ return bool(match("^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*((==|<=|>=|>|<).*)?$", package))
33
34
 
34
35
 
35
36
  @click.command(cls=CmemcCommand, name="install")
36
37
  @click.argument(
37
38
  "PACKAGE",
38
39
  shell_complete=completion.installable_packages,
39
- type=click.Path(readable=True, allow_dash=False, dir_okay=False),
40
+ type=ClickSmartPath(readable=True, allow_dash=False, dir_okay=False),
40
41
  )
41
42
  @click.pass_obj
42
43
  def install_command(app: ApplicationContext, package: str) -> None:
@@ -49,33 +50,48 @@ def install_command(app: ApplicationContext, package: str) -> None:
49
50
  .tar.gz file, by uploading a build distribution .whl file, or by
50
51
  specifying a package name, i.e., a pip requirement specifier with a
51
52
  package name available on pypi.org (e.g. `requests==2.27.1`).
53
+
54
+ Note: The tab-completion of this command lists only public packages from
55
+ pypi.org and not from additional or changed python package repositories you
56
+ may have configured on the server.
52
57
  """
53
58
  app.echo_info(f"Install package {package} ... ", nl=False)
54
59
  try:
55
- response = install_package_by_file(package_file=package)
56
- except FileNotFoundError as error:
60
+ install_response = install_package_by_file(package_file=package)
61
+ except FileNotFoundError as not_found_error:
57
62
  if not _looks_like_a_package(package):
58
63
  raise ValueError(
59
64
  f"{package} does not look like a package name or requirement "
60
65
  "string, and a file with this name also does not exists."
61
- ) from error
62
- response = install_package_by_name(package_name=package)
66
+ ) from not_found_error
67
+ install_response = install_package_by_name(package_name=package)
63
68
 
64
69
  # DI >= 24.1 has a combine 'output' key, before 24.1 'standardOutput' and 'errorOutput' existed
65
- output: list[str] = []
66
- output.extend(response.get("output", "").splitlines())
67
- output.extend(response.get("standardOutput", "").splitlines())
68
- output.extend(response.get("errorOutput", "").splitlines())
69
- for output_line in output:
70
- app.echo_debug(output_line)
71
-
72
- if response["success"]:
70
+ install_output: list[str] = []
71
+ install_output.extend(install_response.get("output", "").splitlines())
72
+ install_output.extend(install_response.get("standardOutput", "").splitlines())
73
+ install_output.extend(install_response.get("errorOutput", "").splitlines())
74
+ app.echo_debug(install_output)
75
+
76
+ update_plugin_response = update_plugins()
77
+ app.echo_debug(f"Updated Plugins: {update_plugin_response!s}")
78
+ update_errors = update_plugin_response.get("errors", [])
79
+ if install_response["success"] is True and len(update_errors) == 0:
73
80
  app.echo_success("done")
74
- else:
75
- app.echo_error("error")
76
- for output_line in output:
77
- app.echo_error(output_line)
78
- app.echo_debug("Updated Plugins: " + str(update_plugins()))
81
+ return
82
+
83
+ # something went wrong
84
+ app.echo_error("error")
85
+ app.echo_error(install_output, prepend_line=True)
86
+ for update_error in update_errors:
87
+ app.echo_error(
88
+ f"Error while updating the plugins of "
89
+ f"{update_error.get('packageName')}: {update_error.get('errorMessage')} "
90
+ f"({update_error.get('errorType')})",
91
+ prepend_line=True,
92
+ )
93
+ app.echo_error(update_error.get("stackTrace"))
94
+ sys.exit(1)
79
95
 
80
96
 
81
97
  @click.command(cls=CmemcCommand, name="uninstall")
@@ -168,7 +184,10 @@ def list_command(app: ApplicationContext, raw: bool, id_only: bool, available: b
168
184
  (_.name, _.version, str(_.published)[:10], _.description) for _ in published_packages
169
185
  ]
170
186
  app.echo_info_table(
171
- table_published, headers=["Name", "Version", "Published", "Description"], sort_column=0
187
+ table_published,
188
+ headers=["Name", "Version", "Published", "Description"],
189
+ sort_column=0,
190
+ empty_table_message="No available python packages found.",
172
191
  )
173
192
  return
174
193
 
@@ -183,7 +202,13 @@ def list_command(app: ApplicationContext, raw: bool, id_only: bool, available: b
183
202
  return
184
203
 
185
204
  table_installed = [(_["name"], _["version"]) for _ in installed_packages]
186
- app.echo_info_table(table_installed, headers=["Name", "Version"], sort_column=0)
205
+ app.echo_info_table(
206
+ table_installed,
207
+ headers=["Name", "Version"],
208
+ sort_column=0,
209
+ empty_table_message="No installed python packages found."
210
+ "Most likely, this is due to a wrong deployment.",
211
+ )
187
212
 
188
213
 
189
214
  @click.command(cls=CmemcCommand, name="list-plugins")
@@ -232,7 +257,13 @@ def list_plugins_command(
232
257
  )
233
258
  for _ in sorted(plugins, key=lambda k: k["id"].lower())
234
259
  ]
235
- app.echo_info_table(table, headers=["ID", "Package ID", "Type", "Label"], sort_column=0)
260
+ app.echo_info_table(
261
+ table,
262
+ headers=["ID", "Package ID", "Type", "Label"],
263
+ sort_column=0,
264
+ empty_table_message="No plugin plugins found. "
265
+ "Use the `admin workspace python install` command to install python packages with plugins.",
266
+ )
236
267
  if "error" in raw_output:
237
268
  app.echo_error(raw_output["error"])
238
269
 
@@ -1,9 +1,9 @@
1
1
  """query commands for cmem command line interface."""
2
+
2
3
  import json
3
4
  import sys
4
5
  from hashlib import sha1
5
6
  from json import JSONDecodeError, load
6
- from pathlib import Path
7
7
  from shutil import get_terminal_size
8
8
  from time import sleep, time
9
9
  from uuid import uuid4
@@ -11,11 +11,17 @@ from uuid import uuid4
11
11
  import click
12
12
  from click import UsageError
13
13
  from click.shell_completion import CompletionItem
14
- from cmem.cmempy.queries import QUERY_CATALOG, SparqlQuery, cancel_query, get_query_status
14
+ from cmem.cmempy.queries import (
15
+ QueryCatalog,
16
+ SparqlQuery,
17
+ cancel_query,
18
+ get_query_status,
19
+ )
15
20
  from requests import HTTPError
16
21
 
17
22
  from cmem_cmemc import completion
18
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
23
+ from cmem_cmemc.command import CmemcCommand
24
+ from cmem_cmemc.command_group import CmemcGroup
19
25
  from cmem_cmemc.context import ApplicationContext
20
26
  from cmem_cmemc.object_list import (
21
27
  DirectListPropertyFilter,
@@ -25,6 +31,8 @@ from cmem_cmemc.object_list import (
25
31
  compare_int_greater_than,
26
32
  compare_regex,
27
33
  )
34
+ from cmem_cmemc.parameter_types.path import ClickSmartPath
35
+ from cmem_cmemc.smart_path import SmartPath as Path
28
36
  from cmem_cmemc.utils import extract_error_message, struct_to_table
29
37
 
30
38
  QUERY_FILTER_TYPES = sorted(["graph", "status", "slower-than", "type", "regex", "trace-id", "user"])
@@ -51,7 +59,7 @@ class ReplayStatistics:
51
59
  query_count: int = 0
52
60
  error_count: int = 0
53
61
  query_average: float
54
- catalog = QUERY_CATALOG
62
+ catalog = QueryCatalog()
55
63
  app: ApplicationContext
56
64
 
57
65
  def __init__(self, app: ApplicationContext, label: str | None = None):
@@ -161,11 +169,12 @@ class ReplayStatistics:
161
169
 
162
170
  def create_output(self) -> dict:
163
171
  """Create the structure for the output commands."""
164
- output = {}
165
- for key, value in vars(self).items():
166
- if key not in ("current_loop_key", "app"):
167
- # ignore some object vars on output
168
- output[key] = value
172
+ # create dict from object but ignore some internal vars on output
173
+ output = {
174
+ key: value
175
+ for (key, value) in dict(vars(self)).items()
176
+ if key not in ("current_loop_key", "app")
177
+ }
169
178
  loop_average = 0
170
179
  loop_minimum = None
171
180
  loop_maximum = None
@@ -331,7 +340,7 @@ def list_command(app: ApplicationContext, id_only: bool) -> None:
331
340
  Outputs a list of query URIs which can be used as reference for
332
341
  the query execute command.
333
342
  """
334
- queries = QUERY_CATALOG.get_queries().items()
343
+ queries = QueryCatalog().get_queries().items()
335
344
  if id_only:
336
345
  # sort dict by short_url - https://docs.python.org/3/howto/sorting.html
337
346
  for _, sparql_query in sorted(queries, key=lambda k: k[1].short_url.lower()):
@@ -443,7 +452,7 @@ def execute_command( # noqa: PLR0913
443
452
  app.echo_debug("Parameter: " + str(placeholder))
444
453
  for file_or_uri in queries:
445
454
  app.echo_debug(f"Start of execution: {file_or_uri} with " f"placeholder {placeholder}")
446
- executed_query = QUERY_CATALOG.get_query(file_or_uri, placeholder=placeholder)
455
+ executed_query = QueryCatalog().get_query(file_or_uri, placeholder=placeholder)
447
456
  if executed_query is None:
448
457
  raise ValueError(f"{file_or_uri} is neither a (readable) file nor a query URI.")
449
458
  app.echo_debug(
@@ -486,7 +495,7 @@ def open_command(app: ApplicationContext, queries: tuple[str, ...]) -> None:
486
495
  opening multiple browser tabs.
487
496
  """
488
497
  for file_or_uri in queries:
489
- opened_query = QUERY_CATALOG.get_query(file_or_uri)
498
+ opened_query = QueryCatalog().get_query(file_or_uri)
490
499
  if opened_query is None:
491
500
  raise ValueError(f"{file_or_uri} is neither a (readable) file nor a query URI.")
492
501
  open_query_uri = opened_query.get_editor_url()
@@ -575,7 +584,7 @@ def status_command(
575
584
  "REPLAY_FILE",
576
585
  required=True,
577
586
  shell_complete=completion.replay_files,
578
- type=click.Path(exists=True, allow_dash=False, readable=True, dir_okay=False),
587
+ type=ClickSmartPath(exists=True, allow_dash=False, readable=True, dir_okay=False),
579
588
  )
580
589
  @click.option("--raw", is_flag=True, help="Output the execution statistic as raw JSON.")
581
590
  @click.option(
@@ -603,7 +612,7 @@ def status_command(
603
612
  "of a successful command execution. The output can be stdout "
604
613
  "('-') - in this case, the execution statistic output is "
605
614
  "oppressed.",
606
- type=click.Path(exists=False, allow_dash=True, writable=True, dir_okay=False),
615
+ type=ClickSmartPath(exists=False, allow_dash=True, writable=True, dir_okay=False),
607
616
  )
608
617
  @click.option("--run-label", type=click.STRING, help="Optional label of this replay run.")
609
618
  @click.pass_obj
@@ -1,4 +1,5 @@
1
1
  """DataIntegration dataset resource commands for cmemc."""
2
+
2
3
  import re
3
4
 
4
5
  import click
@@ -10,7 +11,8 @@ from cmem.cmempy.workspace.projects.resources.resource import (
10
11
  )
11
12
 
12
13
  from cmem_cmemc import completion
13
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
14
+ from cmem_cmemc.command import CmemcCommand
15
+ from cmem_cmemc.command_group import CmemcGroup
14
16
  from cmem_cmemc.context import ApplicationContext
15
17
  from cmem_cmemc.utils import split_task_id, struct_to_table
16
18
 
@@ -91,7 +93,13 @@ def list_command(
91
93
  _["size"],
92
94
  ]
93
95
  table.append(row)
94
- app.echo_info_table(table, headers=headers, sort_column=0)
96
+ app.echo_info_table(
97
+ table,
98
+ headers=headers,
99
+ sort_column=0,
100
+ empty_table_message="No dataset resources found. "
101
+ "Use the `dataset create` command to create a new file based dataset.",
102
+ )
95
103
 
96
104
 
97
105
  @click.command(cls=CmemcCommand, name="delete")
@@ -1,4 +1,5 @@
1
1
  """DataIntegration scheduler commands for the cmem command line interface."""
2
+
2
3
  from typing import Any
3
4
 
4
5
  import click
@@ -9,7 +10,8 @@ from cmem.cmempy.workspace.search import list_items
9
10
  from cmem.cmempy.workspace.tasks import get_task, patch_parameter
10
11
 
11
12
  from cmem_cmemc import completion
12
- from cmem_cmemc.commands import CmemcCommand, CmemcGroup
13
+ from cmem_cmemc.command import CmemcCommand
14
+ from cmem_cmemc.command_group import CmemcGroup
13
15
  from cmem_cmemc.context import ApplicationContext
14
16
  from cmem_cmemc.utils import split_task_id, struct_to_table
15
17
 
@@ -114,7 +116,13 @@ def list_command(app: ApplicationContext, raw: bool, id_only: bool) -> None:
114
116
  ]
115
117
  table.append(row)
116
118
  # sort output by label - https://docs.python.org/3/howto/sorting.html
117
- app.echo_info_table(table, headers=headers, sort_column=1)
119
+ app.echo_info_table(
120
+ table,
121
+ headers=headers,
122
+ sort_column=1,
123
+ empty_table_message="Now workflow scheduler found. "
124
+ "Open a project in the web interface and create a new workflow scheduler there.",
125
+ )
118
126
 
119
127
 
120
128
  @click.command(cls=CmemcCommand, name="inspect")