cmem-cmemc 25.5.0rc1__py3-none-any.whl → 26.1.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.
- cmem_cmemc/cli.py +11 -6
- cmem_cmemc/command.py +1 -1
- cmem_cmemc/command_group.py +59 -31
- cmem_cmemc/commands/acl.py +403 -26
- cmem_cmemc/commands/admin.py +10 -10
- cmem_cmemc/commands/client.py +12 -5
- cmem_cmemc/commands/config.py +106 -12
- cmem_cmemc/commands/dataset.py +163 -172
- cmem_cmemc/commands/file.py +509 -0
- cmem_cmemc/commands/graph.py +200 -72
- cmem_cmemc/commands/graph_imports.py +12 -5
- cmem_cmemc/commands/graph_insights.py +157 -53
- cmem_cmemc/commands/metrics.py +15 -9
- cmem_cmemc/commands/migration.py +12 -4
- cmem_cmemc/commands/package.py +548 -0
- cmem_cmemc/commands/project.py +157 -22
- cmem_cmemc/commands/python.py +9 -5
- cmem_cmemc/commands/query.py +119 -25
- cmem_cmemc/commands/scheduler.py +6 -4
- cmem_cmemc/commands/store.py +2 -1
- cmem_cmemc/commands/user.py +124 -24
- cmem_cmemc/commands/validation.py +15 -10
- cmem_cmemc/commands/variable.py +264 -61
- cmem_cmemc/commands/vocabulary.py +31 -17
- cmem_cmemc/commands/workflow.py +21 -11
- cmem_cmemc/completion.py +126 -109
- cmem_cmemc/context.py +40 -10
- cmem_cmemc/exceptions.py +8 -2
- cmem_cmemc/manual_helper/graph.py +2 -2
- cmem_cmemc/manual_helper/multi_page.py +5 -7
- cmem_cmemc/object_list.py +234 -7
- cmem_cmemc/placeholder.py +2 -2
- cmem_cmemc/string_processor.py +153 -4
- cmem_cmemc/title_helper.py +50 -0
- cmem_cmemc/utils.py +9 -8
- {cmem_cmemc-25.5.0rc1.dist-info → cmem_cmemc-26.1.0rc1.dist-info}/METADATA +7 -6
- cmem_cmemc-26.1.0rc1.dist-info/RECORD +62 -0
- {cmem_cmemc-25.5.0rc1.dist-info → cmem_cmemc-26.1.0rc1.dist-info}/WHEEL +1 -1
- cmem_cmemc/commands/resource.py +0 -220
- cmem_cmemc-25.5.0rc1.dist-info/RECORD +0 -61
- {cmem_cmemc-25.5.0rc1.dist-info → cmem_cmemc-26.1.0rc1.dist-info}/entry_points.txt +0 -0
- {cmem_cmemc-25.5.0rc1.dist-info → cmem_cmemc-26.1.0rc1.dist-info}/licenses/LICENSE +0 -0
cmem_cmemc/commands/graph.py
CHANGED
|
@@ -14,7 +14,8 @@ from xml.etree.ElementTree import ( # nosec
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
import click
|
|
17
|
-
from click import
|
|
17
|
+
from click import Context, UsageError
|
|
18
|
+
from click.shell_completion import CompletionItem
|
|
18
19
|
from cmem.cmempy.config import get_cmem_base_uri
|
|
19
20
|
from cmem.cmempy.dp.authorization import refresh
|
|
20
21
|
from cmem.cmempy.dp.proxy import graph as graph_api
|
|
@@ -30,9 +31,17 @@ from cmem_cmemc.commands.graph_imports import graph_imports_list, imports_group
|
|
|
30
31
|
from cmem_cmemc.commands.graph_insights import insights_group
|
|
31
32
|
from cmem_cmemc.commands.validation import validation_group
|
|
32
33
|
from cmem_cmemc.constants import UNKNOWN_GRAPH_ERROR
|
|
33
|
-
from cmem_cmemc.context import ApplicationContext
|
|
34
|
+
from cmem_cmemc.context import ApplicationContext, build_caption
|
|
35
|
+
from cmem_cmemc.exceptions import CmemcError
|
|
36
|
+
from cmem_cmemc.object_list import (
|
|
37
|
+
DirectMultiValuePropertyFilter,
|
|
38
|
+
DirectValuePropertyFilter,
|
|
39
|
+
Filter,
|
|
40
|
+
ObjectList,
|
|
41
|
+
)
|
|
34
42
|
from cmem_cmemc.parameter_types.path import ClickSmartPath
|
|
35
43
|
from cmem_cmemc.smart_path import SmartPath
|
|
44
|
+
from cmem_cmemc.string_processor import GraphLink
|
|
36
45
|
from cmem_cmemc.utils import (
|
|
37
46
|
RdfGraphData,
|
|
38
47
|
convert_uri_to_filename,
|
|
@@ -44,6 +53,70 @@ from cmem_cmemc.utils import (
|
|
|
44
53
|
)
|
|
45
54
|
|
|
46
55
|
|
|
56
|
+
def compare_access_value(ctx: Filter, object_value: str, filter_value: str) -> bool: # noqa: ARG001
|
|
57
|
+
"""Compare access values - writeable property is boolean string."""
|
|
58
|
+
if filter_value == "writeable":
|
|
59
|
+
return object_value == "True"
|
|
60
|
+
if filter_value == "readonly":
|
|
61
|
+
return object_value == "False"
|
|
62
|
+
raise UsageError(f"Invalid access filter value: {filter_value}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def compare_imported_by(ctx: Filter, object_iri: str, importing_graph_iri: str) -> bool: # noqa: ARG001
|
|
66
|
+
"""Check if object_iri is imported by importing_graph_iri."""
|
|
67
|
+
if importing_graph_iri not in get_graphs_as_dict():
|
|
68
|
+
raise CmemcError(UNKNOWN_GRAPH_ERROR.format(importing_graph_iri))
|
|
69
|
+
imported_graphs = get_graph_imports(importing_graph_iri)
|
|
70
|
+
return object_iri in imported_graphs
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_graphs_for_list(ctx: Context) -> list[dict]: # noqa: ARG001
|
|
74
|
+
"""Get graphs for object list."""
|
|
75
|
+
return get_graphs()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# Common filters used by both list and delete commands
|
|
79
|
+
_graph_common_filters = [
|
|
80
|
+
DirectValuePropertyFilter(
|
|
81
|
+
name="imported-by",
|
|
82
|
+
description="Filter graphs imported by the specified graph IRI.",
|
|
83
|
+
property_key="iri",
|
|
84
|
+
compare=compare_imported_by,
|
|
85
|
+
completion_method="values",
|
|
86
|
+
),
|
|
87
|
+
DirectMultiValuePropertyFilter(
|
|
88
|
+
name="iris",
|
|
89
|
+
description="Internal filter for multiple graph IRIs.",
|
|
90
|
+
property_key="iri",
|
|
91
|
+
),
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
# Access filter only for list command (not applicable for delete)
|
|
95
|
+
_graph_access_filter = DirectValuePropertyFilter(
|
|
96
|
+
name="access",
|
|
97
|
+
description="Filter graphs by access condition (readonly or writeable).",
|
|
98
|
+
property_key="writeable",
|
|
99
|
+
compare=compare_access_value,
|
|
100
|
+
fixed_completion=[
|
|
101
|
+
CompletionItem("readonly", help="Graphs which are NOT writeable by current user."),
|
|
102
|
+
CompletionItem("writeable", help="Graphs which ARE writeable by current user."),
|
|
103
|
+
],
|
|
104
|
+
fixed_completion_only=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
graph_list_obj = ObjectList(
|
|
108
|
+
name="graphs",
|
|
109
|
+
get_objects=get_graphs_for_list,
|
|
110
|
+
filters=[_graph_access_filter, *_graph_common_filters],
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
graph_delete_obj = ObjectList(
|
|
114
|
+
name="graphs",
|
|
115
|
+
get_objects=get_graphs_for_list,
|
|
116
|
+
filters=[*_graph_common_filters],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
47
120
|
def count_graph(graph_iri: str) -> int:
|
|
48
121
|
"""Count triples in a graph and return integer."""
|
|
49
122
|
query = "SELECT (COUNT(*) AS ?triples) " + " FROM <" + graph_iri + "> WHERE { ?s ?p ?o }" # noqa: S608
|
|
@@ -129,45 +202,13 @@ def _get_export_names(
|
|
|
129
202
|
_name_created = f"{Template(template).render(template_data)}{file_extension}"
|
|
130
203
|
_names[iri] = _name_created
|
|
131
204
|
if len(_names.values()) != len(set(_names.values())):
|
|
132
|
-
raise
|
|
205
|
+
raise CmemcError(
|
|
133
206
|
"The given template string produces a naming clash. "
|
|
134
207
|
"Please use a different template to produce unique names."
|
|
135
208
|
)
|
|
136
209
|
return _names
|
|
137
210
|
|
|
138
211
|
|
|
139
|
-
def _get_graphs_filtered(filter_name: str, filter_value: str) -> list[dict]:
|
|
140
|
-
"""Get graphs but filtered according to name and value."""
|
|
141
|
-
# not filtered means all graphs
|
|
142
|
-
graphs: list[dict]
|
|
143
|
-
if filter_name is None:
|
|
144
|
-
return get_graphs()
|
|
145
|
-
# check for correct filter names
|
|
146
|
-
possible_filter_names = ("access", "imported-by")
|
|
147
|
-
if filter_name not in possible_filter_names:
|
|
148
|
-
raise ClickException(
|
|
149
|
-
f"{filter_name} is an unknown filter name. " f"Use one of {possible_filter_names}."
|
|
150
|
-
)
|
|
151
|
-
# filter by access condition
|
|
152
|
-
if filter_name == "access":
|
|
153
|
-
if filter_value == "writeable":
|
|
154
|
-
graphs = get_graphs(writeable=True, readonly=False)
|
|
155
|
-
elif filter_value == "readonly":
|
|
156
|
-
graphs = get_graphs(writeable=False, readonly=True)
|
|
157
|
-
else:
|
|
158
|
-
raise ClickException("Filter access is either 'readonly' or 'writeable'.")
|
|
159
|
-
else:
|
|
160
|
-
# default is all graphs
|
|
161
|
-
graphs = get_graphs()
|
|
162
|
-
# filter by imported-by
|
|
163
|
-
if filter_name == "imported-by":
|
|
164
|
-
if filter_value not in get_graphs_as_dict():
|
|
165
|
-
raise ClickException(UNKNOWN_GRAPH_ERROR.format(filter_value))
|
|
166
|
-
imported_graphs = get_graph_imports(filter_value)
|
|
167
|
-
graphs = [_ for _ in graphs if _["iri"] in imported_graphs]
|
|
168
|
-
return graphs
|
|
169
|
-
|
|
170
|
-
|
|
171
212
|
def _add_imported_graphs(iris: list[str], all_graphs: dict) -> list[str]:
|
|
172
213
|
"""Get a list of graph IRIs extended with the imported graphs.
|
|
173
214
|
|
|
@@ -190,6 +231,49 @@ def _add_imported_graphs(iris: list[str], all_graphs: dict) -> list[str]:
|
|
|
190
231
|
return list(set(extended_list))
|
|
191
232
|
|
|
192
233
|
|
|
234
|
+
def _validate_graph_iris(iris: tuple[str, ...]) -> None:
|
|
235
|
+
"""Validate that all provided graph IRIs exist."""
|
|
236
|
+
if not iris:
|
|
237
|
+
return
|
|
238
|
+
all_graphs = get_graphs_as_dict()
|
|
239
|
+
for iri in iris:
|
|
240
|
+
if iri not in all_graphs:
|
|
241
|
+
raise CmemcError(UNKNOWN_GRAPH_ERROR.format(iri))
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _get_graphs_to_delete(
|
|
245
|
+
ctx: Context,
|
|
246
|
+
iris: tuple[str, ...],
|
|
247
|
+
all_: bool,
|
|
248
|
+
filter_: tuple[tuple[str, str], ...],
|
|
249
|
+
) -> list[dict]:
|
|
250
|
+
"""Get the list of graphs to delete based on selection method."""
|
|
251
|
+
if all_:
|
|
252
|
+
return get_graphs(writeable=True, readonly=False)
|
|
253
|
+
|
|
254
|
+
# Validate provided IRIs exist before proceeding
|
|
255
|
+
_validate_graph_iris(iris)
|
|
256
|
+
|
|
257
|
+
# Build filter list
|
|
258
|
+
filter_to_apply = list(filter_) if filter_ else []
|
|
259
|
+
|
|
260
|
+
# Add IRIs if provided (using internal multi-value filter)
|
|
261
|
+
if iris:
|
|
262
|
+
filter_to_apply.append(("iris", ",".join(iris)))
|
|
263
|
+
|
|
264
|
+
# Apply filters to writeable graphs only
|
|
265
|
+
writeable_graphs = get_graphs(writeable=True, readonly=False)
|
|
266
|
+
graphs = graph_delete_obj.apply_filters(
|
|
267
|
+
ctx=ctx, filter_=filter_to_apply, objects=writeable_graphs
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Validation: ensure we found graphs
|
|
271
|
+
if not graphs:
|
|
272
|
+
raise CmemcError("No graphs found matching the provided criteria.")
|
|
273
|
+
|
|
274
|
+
return graphs
|
|
275
|
+
|
|
276
|
+
|
|
193
277
|
def _check_and_extend_exported_graphs(
|
|
194
278
|
iris: list[str], all_flag: bool, imported_flag: bool, all_graphs: dict
|
|
195
279
|
) -> list[str]:
|
|
@@ -220,7 +304,7 @@ def _check_and_extend_exported_graphs(
|
|
|
220
304
|
)
|
|
221
305
|
for iri in iris:
|
|
222
306
|
if iri not in all_graphs:
|
|
223
|
-
raise
|
|
307
|
+
raise CmemcError(UNKNOWN_GRAPH_ERROR.format(iri))
|
|
224
308
|
if all_flag:
|
|
225
309
|
# in case --all is given,
|
|
226
310
|
# list of graphs is filled with all available graph IRIs
|
|
@@ -301,22 +385,16 @@ def tree_command(ctx: Context, all_: bool, raw: bool, id_only: bool, iris: list[
|
|
|
301
385
|
@click.option(
|
|
302
386
|
"--filter",
|
|
303
387
|
"filter_",
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
help=
|
|
308
|
-
"closure. "
|
|
309
|
-
"First parameter CHOICE can be 'access' or 'imported-by'. "
|
|
310
|
-
"The second parameter can be 'readonly' or 'writeable' in case "
|
|
311
|
-
"of 'access' or any readable graph in case of 'imported-by'.",
|
|
388
|
+
multiple=True,
|
|
389
|
+
type=(str, str),
|
|
390
|
+
shell_complete=graph_list_obj.complete_values,
|
|
391
|
+
help=graph_list_obj.get_filter_help_text(),
|
|
312
392
|
)
|
|
313
|
-
@click.
|
|
314
|
-
def list_command(
|
|
315
|
-
app: ApplicationContext, raw: bool, id_only: bool, filter_: tuple[str, str]
|
|
316
|
-
) -> None:
|
|
393
|
+
@click.pass_context
|
|
394
|
+
def list_command(ctx: Context, raw: bool, id_only: bool, filter_: tuple[tuple[str, str]]) -> None:
|
|
317
395
|
"""List accessible graphs."""
|
|
318
|
-
|
|
319
|
-
graphs =
|
|
396
|
+
app: ApplicationContext = ctx.obj
|
|
397
|
+
graphs = graph_list_obj.apply_filters(ctx=ctx, filter_=filter_)
|
|
320
398
|
|
|
321
399
|
if raw:
|
|
322
400
|
app.echo_info_json(graphs)
|
|
@@ -336,15 +414,19 @@ def list_command(
|
|
|
336
414
|
row = [
|
|
337
415
|
_["iri"],
|
|
338
416
|
graph_class,
|
|
339
|
-
_["
|
|
417
|
+
_["iri"],
|
|
340
418
|
]
|
|
341
419
|
table.append(row)
|
|
420
|
+
filtered = len(filter_) > 0
|
|
342
421
|
app.echo_info_table(
|
|
343
422
|
table,
|
|
344
423
|
headers=["Graph IRI", "Type", "Label"],
|
|
345
424
|
sort_column=2,
|
|
346
|
-
|
|
347
|
-
"
|
|
425
|
+
cell_processing={2: GraphLink()},
|
|
426
|
+
caption=build_caption(len(table), "graph", filtered=filtered),
|
|
427
|
+
empty_table_message="No graphs found for these filters."
|
|
428
|
+
if filtered
|
|
429
|
+
else "No graphs found. Use the `graph import` command to import a graph from a file, or "
|
|
348
430
|
"use the `admin store bootstrap` command to import the default graphs.",
|
|
349
431
|
)
|
|
350
432
|
|
|
@@ -371,7 +453,6 @@ def _write_graph_imports(ctx: Context, filename: str, iri: str) -> None:
|
|
|
371
453
|
imports_file.close()
|
|
372
454
|
|
|
373
455
|
|
|
374
|
-
# pylint: disable=too-many-arguments,too-many-locals
|
|
375
456
|
@click.command(cls=CmemcCommand, name="export")
|
|
376
457
|
@click.option("-a", "--all", "all_", is_flag=True, help="Export all readable graphs.")
|
|
377
458
|
@click.option(
|
|
@@ -475,7 +556,7 @@ def export_command( # noqa: C901, PLR0913
|
|
|
475
556
|
app.echo_debug("output is directory")
|
|
476
557
|
# pre-calculate all filenames with the template,
|
|
477
558
|
# in order to output errors on naming clashes as early as possible
|
|
478
|
-
extension =
|
|
559
|
+
extension = _get_file_extension_for_mime_type(mime_type)
|
|
479
560
|
_names = _get_export_names(
|
|
480
561
|
app, iris, template, f"{extension}.gz" if compress else f"{extension}"
|
|
481
562
|
)
|
|
@@ -551,7 +632,7 @@ def validate_input_path(input_path: str) -> None:
|
|
|
551
632
|
conflicting_files = [f for f in gz_files if f in files]
|
|
552
633
|
|
|
553
634
|
if conflicting_files:
|
|
554
|
-
raise
|
|
635
|
+
raise CmemcError(
|
|
555
636
|
f"The following RDF files (.ttl/.nt) have corresponding '.gz' files,"
|
|
556
637
|
f" which is not allowed: {', '.join(conflicting_files)}"
|
|
557
638
|
)
|
|
@@ -566,6 +647,26 @@ def _get_graph_supported_formats() -> dict[str, str]:
|
|
|
566
647
|
}
|
|
567
648
|
|
|
568
649
|
|
|
650
|
+
def _get_file_extension_for_mime_type(mime_type: str) -> str:
|
|
651
|
+
"""Get file extension for a MIME type with fallback mappings.
|
|
652
|
+
|
|
653
|
+
mimetypes.guess_extension() can return None on some systems (especially Windows)
|
|
654
|
+
for certain RDF MIME types. This function provides fallback extensions.
|
|
655
|
+
"""
|
|
656
|
+
# Try to use the system's mimetypes registry first
|
|
657
|
+
extension = mimetypes.guess_extension(mime_type, strict=False)
|
|
658
|
+
if extension is not None:
|
|
659
|
+
return extension
|
|
660
|
+
|
|
661
|
+
# Fallback mappings for RDF MIME types
|
|
662
|
+
mime_to_extension = {
|
|
663
|
+
"application/n-triples": ".nt",
|
|
664
|
+
"text/turtle": ".ttl",
|
|
665
|
+
"application/rdf+xml": ".rdf",
|
|
666
|
+
}
|
|
667
|
+
return mime_to_extension.get(mime_type, ".ttl")
|
|
668
|
+
|
|
669
|
+
|
|
569
670
|
def _get_buffer_and_content_type(
|
|
570
671
|
triple_file: str, app: ApplicationContext
|
|
571
672
|
) -> tuple[io.BytesIO, str, None | str]:
|
|
@@ -633,7 +734,7 @@ def _process_input_directory(input_path: str, iri: str) -> list[RdfGraphData]:
|
|
|
633
734
|
# in case a directory is the source AND IRI is given
|
|
634
735
|
graphs = []
|
|
635
736
|
for _ in _get_graph_supported_formats():
|
|
636
|
-
extension =
|
|
737
|
+
extension = _get_file_extension_for_mime_type(_)
|
|
637
738
|
graphs += [
|
|
638
739
|
RdfGraphData(str(file), iri, [])
|
|
639
740
|
for file in SmartPath(input_path).glob(f"*{extension}")
|
|
@@ -775,6 +876,12 @@ def import_command( # noqa: PLR0913
|
|
|
775
876
|
|
|
776
877
|
|
|
777
878
|
@click.command(cls=CmemcCommand, name="delete")
|
|
879
|
+
@click.argument(
|
|
880
|
+
"iris",
|
|
881
|
+
nargs=-1,
|
|
882
|
+
type=click.STRING,
|
|
883
|
+
shell_complete=completion.writable_graph_uris,
|
|
884
|
+
)
|
|
778
885
|
@click.option("-a", "--all", "all_", is_flag=True, help="Delete all writeable graphs.")
|
|
779
886
|
@click.option(
|
|
780
887
|
"--include-imports",
|
|
@@ -785,35 +892,56 @@ def import_command( # noqa: PLR0913
|
|
|
785
892
|
@click.option(
|
|
786
893
|
"--include-import-statements", is_flag=True, help="Delete import reference of deleted graphs"
|
|
787
894
|
)
|
|
788
|
-
@click.
|
|
789
|
-
"
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
895
|
+
@click.option(
|
|
896
|
+
"--filter",
|
|
897
|
+
"filter_",
|
|
898
|
+
multiple=True,
|
|
899
|
+
type=(str, str),
|
|
900
|
+
shell_complete=graph_delete_obj.complete_values,
|
|
901
|
+
help=graph_delete_obj.get_filter_help_text(),
|
|
794
902
|
)
|
|
795
903
|
@click.pass_context
|
|
796
|
-
def delete_command(
|
|
904
|
+
def delete_command( # noqa: PLR0913
|
|
797
905
|
ctx: Context,
|
|
906
|
+
iris: tuple[str, ...],
|
|
798
907
|
all_: bool,
|
|
799
908
|
include_imports: bool,
|
|
800
909
|
include_import_statements: bool,
|
|
801
|
-
|
|
910
|
+
filter_: tuple[tuple[str, str], ...],
|
|
802
911
|
) -> None:
|
|
803
912
|
"""Delete graph(s) from the store."""
|
|
804
913
|
app: ApplicationContext = ctx.obj
|
|
805
|
-
|
|
806
|
-
|
|
914
|
+
|
|
915
|
+
# Validation: require at least one selection method
|
|
916
|
+
if not iris and not all_ and not filter_:
|
|
917
|
+
raise UsageError(
|
|
918
|
+
"Either specify at least one graph IRI or use the --all or "
|
|
919
|
+
"--filter options to specify graphs for deletion."
|
|
920
|
+
)
|
|
921
|
+
|
|
922
|
+
# Get base list of graphs to delete using ObjectList filtering
|
|
923
|
+
graphs_to_delete = _get_graphs_to_delete(ctx, iris, all_, filter_)
|
|
924
|
+
iris_to_delete = [g["iri"] for g in graphs_to_delete]
|
|
925
|
+
|
|
926
|
+
# Handle --include-imports flag
|
|
927
|
+
if include_imports:
|
|
928
|
+
all_graphs = get_graphs_as_dict(writeable=True, readonly=False)
|
|
929
|
+
iris_to_delete = _add_imported_graphs(iris_to_delete, all_graphs)
|
|
930
|
+
|
|
931
|
+
# Remove duplicates and sort
|
|
932
|
+
iris_to_delete = sorted(set(iris_to_delete))
|
|
933
|
+
|
|
807
934
|
imports_to_be_deleted = []
|
|
808
|
-
count: int = len(
|
|
809
|
-
for current, iri in enumerate(
|
|
935
|
+
count: int = len(iris_to_delete)
|
|
936
|
+
for current, iri in enumerate(iris_to_delete, start=1):
|
|
937
|
+
current_string = str(current).zfill(len(str(count)))
|
|
810
938
|
imports_to_be_deleted += graph_imports_list.apply_filters(
|
|
811
939
|
ctx=ctx, filter_=[("to-graph", iri)]
|
|
812
940
|
)
|
|
813
941
|
|
|
814
|
-
app.echo_info(f"
|
|
942
|
+
app.echo_info(f"Delete graph {current_string}/{count}: {iri} ... ", nl=False)
|
|
815
943
|
graph_api.delete(iri)
|
|
816
|
-
app.echo_success("
|
|
944
|
+
app.echo_success("deleted")
|
|
817
945
|
# refresh access conditions in case of dropped AC graph
|
|
818
946
|
if iri == refresh.AUTHORIZATION_GRAPH_URI:
|
|
819
947
|
refresh.get()
|
|
@@ -823,7 +951,7 @@ def delete_command(
|
|
|
823
951
|
imports_cmd = graph.commands["imports"]
|
|
824
952
|
delete_cmd = imports_cmd.commands["delete"] # type: ignore[attr-defined]
|
|
825
953
|
for _ in imports_to_be_deleted:
|
|
826
|
-
if _["from_graph"] not in
|
|
954
|
+
if _["from_graph"] not in iris_to_delete:
|
|
827
955
|
ctx.invoke(delete_cmd, from_graph=_["from_graph"], to_graph=_["to_graph"])
|
|
828
956
|
|
|
829
957
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
5
|
import click
|
|
6
|
-
from click import Argument,
|
|
6
|
+
from click import Argument, Context, UsageError
|
|
7
7
|
from click.shell_completion import CompletionItem
|
|
8
8
|
from cmem.cmempy.dp.proxy.graph import get_graph_import_tree
|
|
9
9
|
from cmem.cmempy.queries import SparqlQuery
|
|
@@ -12,8 +12,10 @@ from treelib import Tree
|
|
|
12
12
|
from cmem_cmemc import completion
|
|
13
13
|
from cmem_cmemc.command import CmemcCommand
|
|
14
14
|
from cmem_cmemc.command_group import CmemcGroup
|
|
15
|
+
from cmem_cmemc.completion import suppress_completion_errors
|
|
15
16
|
from cmem_cmemc.constants import UNKNOWN_GRAPH_ERROR
|
|
16
|
-
from cmem_cmemc.context import ApplicationContext
|
|
17
|
+
from cmem_cmemc.context import ApplicationContext, build_caption
|
|
18
|
+
from cmem_cmemc.exceptions import CmemcError
|
|
17
19
|
from cmem_cmemc.object_list import DirectValuePropertyFilter, ObjectList
|
|
18
20
|
from cmem_cmemc.string_processor import GraphLink
|
|
19
21
|
from cmem_cmemc.title_helper import TitleHelper
|
|
@@ -183,7 +185,7 @@ def tree_command(
|
|
|
183
185
|
|
|
184
186
|
for iri in iris:
|
|
185
187
|
if iri not in graphs:
|
|
186
|
-
raise
|
|
188
|
+
raise CmemcError(UNKNOWN_GRAPH_ERROR.format(iri))
|
|
187
189
|
|
|
188
190
|
iris = sorted(iris, key=lambda x: graphs[x]["label"]["title"].lower())
|
|
189
191
|
|
|
@@ -276,13 +278,16 @@ def list_command(ctx: Context, raw: bool, filter_: tuple[str, str]) -> None:
|
|
|
276
278
|
to_graph = rf"\[missing: {to_graph}]"
|
|
277
279
|
table.append([from_graph, to_graph])
|
|
278
280
|
|
|
281
|
+
filtered = len(filters_to_apply) > 0
|
|
279
282
|
app.echo_info_table(
|
|
280
283
|
table,
|
|
281
284
|
headers=["From graph", "To graph"],
|
|
282
285
|
sort_column=0,
|
|
286
|
+
caption=build_caption(len(table), "import", filtered=filtered),
|
|
283
287
|
cell_processing={0: GraphLink(), 1: GraphLink()},
|
|
284
|
-
empty_table_message="No imports found.
|
|
285
|
-
|
|
288
|
+
empty_table_message="No imports found for these filters."
|
|
289
|
+
if filtered
|
|
290
|
+
else "No imports found. Use the `graph imports create` command to create a graph import.",
|
|
286
291
|
)
|
|
287
292
|
|
|
288
293
|
|
|
@@ -295,6 +300,7 @@ def _validate_graphs(from_graph: str | None, to_graph: str | None) -> None:
|
|
|
295
300
|
raise click.UsageError(f"To graph {to_graph} not found.")
|
|
296
301
|
|
|
297
302
|
|
|
303
|
+
@suppress_completion_errors
|
|
298
304
|
def _from_graph_uris(ctx: Context, param: Argument, incomplete: str) -> list[CompletionItem]:
|
|
299
305
|
"""Provide auto completion items for delete command from-graph argument"""
|
|
300
306
|
imports = get_imports_list(ctx)
|
|
@@ -306,6 +312,7 @@ def _from_graph_uris(ctx: Context, param: Argument, incomplete: str) -> list[Com
|
|
|
306
312
|
]
|
|
307
313
|
|
|
308
314
|
|
|
315
|
+
@suppress_completion_errors
|
|
309
316
|
def _to_graph_uris(ctx: Context, param: Argument, incomplete: str) -> list[CompletionItem]:
|
|
310
317
|
"""Provide auto completion items for create/delete command to-graph argument"""
|
|
311
318
|
from_graph = ctx.params["from_graph"]
|