cognite-neat 0.87.6__py3-none-any.whl → 0.88.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.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_version.py +1 -1
- cognite/neat/app/api/data_classes/rest.py +0 -19
- cognite/neat/app/api/explorer.py +6 -4
- cognite/neat/app/api/routers/crud.py +11 -21
- cognite/neat/app/api/routers/workflows.py +24 -94
- cognite/neat/graph/stores/_base.py +5 -0
- cognite/neat/rules/importers/_inference2rules.py +31 -35
- cognite/neat/workflows/steps/data_contracts.py +17 -43
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +28 -24
- cognite/neat/workflows/steps/lib/current/graph_loader.py +4 -21
- cognite/neat/workflows/steps/lib/current/graph_store.py +18 -134
- cognite/neat/workflows/steps_registry.py +5 -7
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/RECORD +17 -125
- cognite/neat/app/api/routers/core.py +0 -91
- cognite/neat/app/api/routers/data_exploration.py +0 -336
- cognite/neat/app/api/routers/rules.py +0 -203
- cognite/neat/legacy/__init__.py +0 -0
- cognite/neat/legacy/graph/__init__.py +0 -3
- cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44-dirty.xml +0 -20182
- cognite/neat/legacy/graph/examples/Knowledge-Graph-Nordic44.xml +0 -20163
- cognite/neat/legacy/graph/examples/__init__.py +0 -10
- cognite/neat/legacy/graph/examples/skos-capturing-sheet-wind-topics.xlsx +0 -0
- cognite/neat/legacy/graph/exceptions.py +0 -90
- cognite/neat/legacy/graph/extractors/__init__.py +0 -6
- cognite/neat/legacy/graph/extractors/_base.py +0 -14
- cognite/neat/legacy/graph/extractors/_dexpi.py +0 -44
- cognite/neat/legacy/graph/extractors/_graph_capturing_sheet.py +0 -403
- cognite/neat/legacy/graph/extractors/_mock_graph_generator.py +0 -361
- cognite/neat/legacy/graph/loaders/__init__.py +0 -23
- cognite/neat/legacy/graph/loaders/_asset_loader.py +0 -511
- cognite/neat/legacy/graph/loaders/_base.py +0 -67
- cognite/neat/legacy/graph/loaders/_exceptions.py +0 -85
- cognite/neat/legacy/graph/loaders/core/__init__.py +0 -0
- cognite/neat/legacy/graph/loaders/core/labels.py +0 -58
- cognite/neat/legacy/graph/loaders/core/models.py +0 -136
- cognite/neat/legacy/graph/loaders/core/rdf_to_assets.py +0 -1046
- cognite/neat/legacy/graph/loaders/core/rdf_to_relationships.py +0 -559
- cognite/neat/legacy/graph/loaders/rdf_to_dms.py +0 -309
- cognite/neat/legacy/graph/loaders/validator.py +0 -87
- cognite/neat/legacy/graph/models.py +0 -6
- cognite/neat/legacy/graph/stores/__init__.py +0 -13
- cognite/neat/legacy/graph/stores/_base.py +0 -400
- cognite/neat/legacy/graph/stores/_graphdb_store.py +0 -52
- cognite/neat/legacy/graph/stores/_memory_store.py +0 -43
- cognite/neat/legacy/graph/stores/_oxigraph_store.py +0 -151
- cognite/neat/legacy/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/legacy/graph/stores/_rdf_to_graph.py +0 -42
- cognite/neat/legacy/graph/transformations/__init__.py +0 -0
- cognite/neat/legacy/graph/transformations/entity_matcher.py +0 -101
- cognite/neat/legacy/graph/transformations/query_generator/__init__.py +0 -3
- cognite/neat/legacy/graph/transformations/query_generator/sparql.py +0 -575
- cognite/neat/legacy/graph/transformations/transformer.py +0 -322
- cognite/neat/legacy/rules/__init__.py +0 -0
- cognite/neat/legacy/rules/analysis.py +0 -231
- cognite/neat/legacy/rules/examples/Rules-Nordic44-to-graphql.xlsx +0 -0
- cognite/neat/legacy/rules/examples/Rules-Nordic44.xlsx +0 -0
- cognite/neat/legacy/rules/examples/__init__.py +0 -18
- cognite/neat/legacy/rules/examples/power-grid-containers.yaml +0 -124
- cognite/neat/legacy/rules/examples/power-grid-example.xlsx +0 -0
- cognite/neat/legacy/rules/examples/power-grid-model.yaml +0 -224
- cognite/neat/legacy/rules/examples/rules-template.xlsx +0 -0
- cognite/neat/legacy/rules/examples/sheet2cdf-transformation-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/skos-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/source-to-solution-mapping-rules.xlsx +0 -0
- cognite/neat/legacy/rules/examples/wind-energy.owl +0 -1511
- cognite/neat/legacy/rules/exceptions.py +0 -2972
- cognite/neat/legacy/rules/exporters/__init__.py +0 -20
- cognite/neat/legacy/rules/exporters/_base.py +0 -45
- cognite/neat/legacy/rules/exporters/_core/__init__.py +0 -5
- cognite/neat/legacy/rules/exporters/_core/rules2labels.py +0 -24
- cognite/neat/legacy/rules/exporters/_rules2dms.py +0 -885
- cognite/neat/legacy/rules/exporters/_rules2excel.py +0 -213
- cognite/neat/legacy/rules/exporters/_rules2graphql.py +0 -183
- cognite/neat/legacy/rules/exporters/_rules2ontology.py +0 -524
- cognite/neat/legacy/rules/exporters/_rules2pydantic_models.py +0 -748
- cognite/neat/legacy/rules/exporters/_rules2rules.py +0 -105
- cognite/neat/legacy/rules/exporters/_rules2triples.py +0 -38
- cognite/neat/legacy/rules/exporters/_validation.py +0 -146
- cognite/neat/legacy/rules/importers/__init__.py +0 -22
- cognite/neat/legacy/rules/importers/_base.py +0 -66
- cognite/neat/legacy/rules/importers/_dict2rules.py +0 -158
- cognite/neat/legacy/rules/importers/_dms2rules.py +0 -194
- cognite/neat/legacy/rules/importers/_graph2rules.py +0 -308
- cognite/neat/legacy/rules/importers/_json2rules.py +0 -39
- cognite/neat/legacy/rules/importers/_owl2rules/__init__.py +0 -3
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2classes.py +0 -239
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2metadata.py +0 -260
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2properties.py +0 -217
- cognite/neat/legacy/rules/importers/_owl2rules/_owl2rules.py +0 -290
- cognite/neat/legacy/rules/importers/_spreadsheet2rules.py +0 -45
- cognite/neat/legacy/rules/importers/_xsd2rules.py +0 -20
- cognite/neat/legacy/rules/importers/_yaml2rules.py +0 -39
- cognite/neat/legacy/rules/models/__init__.py +0 -5
- cognite/neat/legacy/rules/models/_base.py +0 -151
- cognite/neat/legacy/rules/models/raw_rules.py +0 -316
- cognite/neat/legacy/rules/models/rdfpath.py +0 -237
- cognite/neat/legacy/rules/models/rules.py +0 -1289
- cognite/neat/legacy/rules/models/tables.py +0 -9
- cognite/neat/legacy/rules/models/value_types.py +0 -118
- cognite/neat/legacy/workflows/examples/Export_DMS/workflow.yaml +0 -89
- cognite/neat/legacy/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +0 -152
- cognite/neat/legacy/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +0 -139
- cognite/neat/legacy/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +0 -270
- cognite/neat/legacy/workflows/examples/Import_DMS/workflow.yaml +0 -65
- cognite/neat/legacy/workflows/examples/Ontology_to_Data_Model/workflow.yaml +0 -116
- cognite/neat/legacy/workflows/examples/Validate_Rules/workflow.yaml +0 -67
- cognite/neat/legacy/workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
- cognite/neat/legacy/workflows/examples/Visualize_Data_Model_Using_Mock_Graph/workflow.yaml +0 -95
- cognite/neat/legacy/workflows/examples/Visualize_Semantic_Data_Model/workflow.yaml +0 -111
- cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +0 -270
- cognite/neat/workflows/migration/__init__.py +0 -0
- cognite/neat/workflows/migration/steps.py +0 -91
- cognite/neat/workflows/migration/wf_manifests.py +0 -33
- cognite/neat/workflows/steps/lib/legacy/__init__.py +0 -7
- cognite/neat/workflows/steps/lib/legacy/graph_contextualization.py +0 -82
- cognite/neat/workflows/steps/lib/legacy/graph_extractor.py +0 -746
- cognite/neat/workflows/steps/lib/legacy/graph_loader.py +0 -606
- cognite/neat/workflows/steps/lib/legacy/graph_store.py +0 -307
- cognite/neat/workflows/steps/lib/legacy/graph_transformer.py +0 -58
- cognite/neat/workflows/steps/lib/legacy/rules_exporter.py +0 -511
- cognite/neat/workflows/steps/lib/legacy/rules_importer.py +0 -612
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,575 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from collections import Counter
|
|
3
|
-
from collections.abc import Iterable
|
|
4
|
-
from typing import cast
|
|
5
|
-
|
|
6
|
-
from rdflib import Graph, Namespace
|
|
7
|
-
from rdflib.term import URIRef
|
|
8
|
-
|
|
9
|
-
from cognite.neat.constants import get_default_prefixes
|
|
10
|
-
from cognite.neat.legacy.rules.analysis import get_classes_with_properties
|
|
11
|
-
from cognite.neat.legacy.rules.models._base import Triple
|
|
12
|
-
from cognite.neat.legacy.rules.models.rdfpath import (
|
|
13
|
-
AllProperties,
|
|
14
|
-
AllReferences,
|
|
15
|
-
Hop,
|
|
16
|
-
SingleProperty,
|
|
17
|
-
Step,
|
|
18
|
-
TransformationRuleType,
|
|
19
|
-
Traversal,
|
|
20
|
-
parse_rule,
|
|
21
|
-
parse_traversal,
|
|
22
|
-
)
|
|
23
|
-
from cognite.neat.legacy.rules.models.rules import Rules
|
|
24
|
-
from cognite.neat.utils.rdf_ import remove_namespace_from_uri
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def _generate_prefix_header(prefixes: dict[str, Namespace] | None = None) -> str:
|
|
28
|
-
"""Generate prefix header which is added to SPARQL query and allows for shorten query statements
|
|
29
|
-
|
|
30
|
-
Parameters
|
|
31
|
-
----------
|
|
32
|
-
prefixes : dict
|
|
33
|
-
Dict containing prefix - namespace pairs, default PREFIXES
|
|
34
|
-
|
|
35
|
-
Returns
|
|
36
|
-
-------
|
|
37
|
-
str
|
|
38
|
-
Prefix header
|
|
39
|
-
"""
|
|
40
|
-
prefixes = prefixes if prefixes else get_default_prefixes()
|
|
41
|
-
return "".join(f"PREFIX {key}:<{value}>\n" for key, value in prefixes.items())
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def _get_predicate_id(
|
|
45
|
-
graph: Graph,
|
|
46
|
-
subject_type_id: str,
|
|
47
|
-
object_type_id: str,
|
|
48
|
-
prefixes: dict[str, Namespace] | None = None,
|
|
49
|
-
) -> URIRef:
|
|
50
|
-
"""Returns predicate (aka property) URI (i.e., ID) that connects subject and object
|
|
51
|
-
|
|
52
|
-
Parameters
|
|
53
|
-
----------
|
|
54
|
-
graph : Graph
|
|
55
|
-
Data model graph or data model instance (aka knowledge graph)
|
|
56
|
-
subject_type_id : str
|
|
57
|
-
ID of subject type (aka subject class)
|
|
58
|
-
object_type_id : str
|
|
59
|
-
ID of object type (aka object class)
|
|
60
|
-
prefixes : dict, optional
|
|
61
|
-
Dict containing prefix - namespace pairs, default PREFIXES
|
|
62
|
-
|
|
63
|
-
Returns
|
|
64
|
-
-------
|
|
65
|
-
URIRef
|
|
66
|
-
ID of predicate (aka property) connecting subject and object
|
|
67
|
-
"""
|
|
68
|
-
prefixes = prefixes if prefixes else get_default_prefixes()
|
|
69
|
-
query = """
|
|
70
|
-
|
|
71
|
-
SELECT ?predicateTypeID
|
|
72
|
-
WHERE {
|
|
73
|
-
?subjectInstanceID a subjectTypeID .
|
|
74
|
-
?objectInstanceID a objectTypeID .
|
|
75
|
-
?subjectInstanceID ?predicateTypeID ?objectInstanceID .
|
|
76
|
-
} LIMIT 1"""
|
|
77
|
-
|
|
78
|
-
query = query.replace("insertPrefixes", _generate_prefix_header(prefixes))
|
|
79
|
-
final_query = query.replace("subjectTypeID", subject_type_id).replace("objectTypeID", object_type_id)
|
|
80
|
-
res = list(cast(tuple, graph.query(final_query)))
|
|
81
|
-
|
|
82
|
-
if len(res) != 1:
|
|
83
|
-
raise ValueError("Subject and Object must have exactly 1 relation!")
|
|
84
|
-
|
|
85
|
-
return res[0][0]
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def _get_direct_mapping_triples(subject, predicate) -> list[Triple]:
|
|
89
|
-
return [Triple(subject=subject, predicate=predicate, object="?object")]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def _get_all_references_mapping_triples(object) -> list[Triple]:
|
|
93
|
-
return [Triple(subject="?subject", predicate="a", object=object)]
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def _get_entire_object_mapping(subject) -> list[Triple]:
|
|
97
|
-
return [Triple(subject=subject, predicate="*")]
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def _get_hop_triples(graph, path: Hop, prefixes) -> list[Triple]:
|
|
101
|
-
triples = [Triple(subject="?subject", predicate="a", object=path.class_.id)]
|
|
102
|
-
previous_step = Step(class_=path.class_, direction="origin")
|
|
103
|
-
|
|
104
|
-
# add triples for all steps until destination
|
|
105
|
-
for current_step in path.traversal:
|
|
106
|
-
sub_entity, obj_entity = (
|
|
107
|
-
(current_step, previous_step) if current_step.direction == "source" else (previous_step, current_step)
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
predicate = _get_predicate_id(graph, sub_entity.class_.id, obj_entity.class_.id, prefixes)
|
|
111
|
-
|
|
112
|
-
# if this is first step after origin
|
|
113
|
-
if previous_step.class_.id == path.class_.id:
|
|
114
|
-
if current_step.direction == "source":
|
|
115
|
-
sub, obj = f"?{sub_entity.class_.suffix}ID", "?subject"
|
|
116
|
-
else:
|
|
117
|
-
sub, obj = "?subject", f"?{obj_entity.class_.suffix}ID"
|
|
118
|
-
|
|
119
|
-
# Any other step after hoping from origin to first step
|
|
120
|
-
else:
|
|
121
|
-
sub = f"?{sub_entity.class_.suffix}ID"
|
|
122
|
-
obj = f"?{obj_entity.class_.suffix}ID"
|
|
123
|
-
|
|
124
|
-
triples.append(Triple(subject=sub, predicate=predicate, object=obj))
|
|
125
|
-
previous_step = current_step
|
|
126
|
-
|
|
127
|
-
if previous_step.property:
|
|
128
|
-
triples.extend(
|
|
129
|
-
[
|
|
130
|
-
Triple(
|
|
131
|
-
subject=f"?{previous_step.class_.suffix}ID",
|
|
132
|
-
predicate="a",
|
|
133
|
-
object=previous_step.class_.id,
|
|
134
|
-
),
|
|
135
|
-
Triple(
|
|
136
|
-
subject=f"?{previous_step.class_.suffix}ID",
|
|
137
|
-
predicate=previous_step.property.id,
|
|
138
|
-
object="?object",
|
|
139
|
-
),
|
|
140
|
-
Triple(
|
|
141
|
-
subject="?predicate",
|
|
142
|
-
predicate="a",
|
|
143
|
-
object=previous_step.property.id,
|
|
144
|
-
),
|
|
145
|
-
]
|
|
146
|
-
)
|
|
147
|
-
else:
|
|
148
|
-
if previous_step.direction == "source":
|
|
149
|
-
triples[-1].subject = "?object"
|
|
150
|
-
else:
|
|
151
|
-
triples[-1].object = "?object"
|
|
152
|
-
triples.append(Triple(subject="?object", predicate="a", object=previous_step.class_.id))
|
|
153
|
-
|
|
154
|
-
return triples
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def _get_path_triples(graph: Graph, traversal: Traversal, prefixes: dict[str, Namespace] | None = None) -> list[Triple]:
|
|
158
|
-
"""Creates triples subject-predicate-object from declarative graph traversal path
|
|
159
|
-
|
|
160
|
-
Parameters
|
|
161
|
-
----------
|
|
162
|
-
graph : Graph
|
|
163
|
-
Data model graph or data model instance (aka knowledge graph)
|
|
164
|
-
traversal : Traversal
|
|
165
|
-
Parsed traversal path in declarative form
|
|
166
|
-
prefixes : dict, optional
|
|
167
|
-
Dict containing prefix - namespace pairs, default PREFIXES
|
|
168
|
-
|
|
169
|
-
Returns
|
|
170
|
-
-------
|
|
171
|
-
list
|
|
172
|
-
List of triples to be consumed by SPARQL query builder
|
|
173
|
-
"""
|
|
174
|
-
match traversal:
|
|
175
|
-
case SingleProperty():
|
|
176
|
-
return _get_direct_mapping_triples(traversal.class_.id, traversal.property.id)
|
|
177
|
-
case AllProperties():
|
|
178
|
-
return _get_entire_object_mapping(traversal.class_.id)
|
|
179
|
-
case AllReferences():
|
|
180
|
-
return _get_all_references_mapping_triples(traversal.class_.id)
|
|
181
|
-
case Hop():
|
|
182
|
-
return _get_hop_triples(graph, traversal, prefixes)
|
|
183
|
-
case _:
|
|
184
|
-
raise ValueError("Incorrectly set traversal path!")
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
BASIC_SPARQL_QUERY_TEMPLATE = """insertPrefixes
|
|
188
|
-
|
|
189
|
-
SELECT DISTINCT ?subject ?predicate ?object
|
|
190
|
-
WHERE {
|
|
191
|
-
query_insertions
|
|
192
|
-
}"""
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE = """insertPrefixes
|
|
196
|
-
|
|
197
|
-
SELECT DISTINCT ?subject ?predicate ?object {
|
|
198
|
-
{SELECT DISTINCT ?object ?predicate {
|
|
199
|
-
query_insertions
|
|
200
|
-
BIND(dct:identifier AS ?predicate)}}
|
|
201
|
-
|
|
202
|
-
BIND(?object AS ?subject)
|
|
203
|
-
|
|
204
|
-
}"""
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
# This template does not work with in-memory graph, so it has been replaced with the one above
|
|
208
|
-
REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE_OLD = """insertPrefixes
|
|
209
|
-
|
|
210
|
-
SELECT DISTINCT ?subject ?predicate ?object
|
|
211
|
-
WHERE {
|
|
212
|
-
query_insertions
|
|
213
|
-
{
|
|
214
|
-
BIND(?object AS ?subject)
|
|
215
|
-
BIND(dct:identifier AS ?predicate)
|
|
216
|
-
}
|
|
217
|
-
}"""
|
|
218
|
-
|
|
219
|
-
SINGLE_PROPERTY_SPARQL_QUERY_TEMPLATE = """insertPrefixes
|
|
220
|
-
|
|
221
|
-
SELECT DISTINCT ?subject ?predicate ?object
|
|
222
|
-
WHERE {
|
|
223
|
-
query_insertions
|
|
224
|
-
{
|
|
225
|
-
BIND(property_insertion AS ?predicate)
|
|
226
|
-
}
|
|
227
|
-
}"""
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def _generate_all_properties_query_statement(subject: str, query_template: str = BASIC_SPARQL_QUERY_TEMPLATE) -> str:
|
|
231
|
-
query_insertions = "\n".join([f"\t\t?subject a {subject} .", "\t\t?subject ?predicate ?object ."])
|
|
232
|
-
|
|
233
|
-
return query_template.replace("query_insertions", query_insertions)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _generate_all_references_query_statement(
|
|
237
|
-
object: str, query_template: str = REFERENCES_ONLY_SPARQL_QUERY_TEMPLATE
|
|
238
|
-
) -> str:
|
|
239
|
-
query_insertions = "\n".join([f"\t\t?object a {object} ."])
|
|
240
|
-
|
|
241
|
-
return query_template.replace("query_insertions", query_insertions)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def _generate_single_property_query_statement(
|
|
245
|
-
subject: str,
|
|
246
|
-
predicate: str,
|
|
247
|
-
query_template: str = SINGLE_PROPERTY_SPARQL_QUERY_TEMPLATE,
|
|
248
|
-
) -> str:
|
|
249
|
-
query_insertions = "\n".join([f"\t\t?subject a {subject} .", f"\t\t?subject {predicate} ?object ."])
|
|
250
|
-
|
|
251
|
-
return query_template.replace("query_insertions", query_insertions).replace("property_insertion", predicate)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def _generate_hop_query_statement(triples: list[Triple], query_template: str = BASIC_SPARQL_QUERY_TEMPLATE) -> str:
|
|
255
|
-
terminal_triplet = triples[-1]
|
|
256
|
-
|
|
257
|
-
query_insertions = "".join(
|
|
258
|
-
f" {triple.subject} {triple.predicate} {triple.object} .\n" for triple in triples[:-1]
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
# Creating terminal query statement based on whether we are query for specific
|
|
262
|
-
# property of an object (first option) or object ID
|
|
263
|
-
if terminal_triplet.subject == "?predicate":
|
|
264
|
-
query_insertions += f" BIND({terminal_triplet.object} AS ?predicate)\n"
|
|
265
|
-
else:
|
|
266
|
-
query_insertions += (
|
|
267
|
-
f" {terminal_triplet.subject} {terminal_triplet.predicate} {terminal_triplet.object} .\n"
|
|
268
|
-
)
|
|
269
|
-
query_insertions += " BIND(dct:relation AS ?predicate)\n"
|
|
270
|
-
|
|
271
|
-
return query_template.replace("query_insertions", query_insertions)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def build_sparql_query(
|
|
275
|
-
graph: Graph,
|
|
276
|
-
traversal_path: str | Traversal,
|
|
277
|
-
prefixes: dict[str, Namespace] | None = None,
|
|
278
|
-
insert_prefixes: bool = False,
|
|
279
|
-
) -> str:
|
|
280
|
-
"""Builds SPARQL query based on declarative traversal path
|
|
281
|
-
|
|
282
|
-
Parameters
|
|
283
|
-
----------
|
|
284
|
-
graph : Graph
|
|
285
|
-
Data model graph or data model instance (aka knowledge graph)
|
|
286
|
-
traversal_path : str
|
|
287
|
-
String representing graph traversal path in declarative form
|
|
288
|
-
prefixes : dict, optional
|
|
289
|
-
Dict containing prefix - namespace pairs, default PREFIXES
|
|
290
|
-
|
|
291
|
-
Returns
|
|
292
|
-
-------
|
|
293
|
-
str
|
|
294
|
-
SPARQL query
|
|
295
|
-
"""
|
|
296
|
-
prefixes = prefixes if prefixes else get_default_prefixes()
|
|
297
|
-
traversal = parse_traversal(traversal_path) if isinstance(traversal_path, str) else traversal_path
|
|
298
|
-
triples = _get_path_triples(graph, traversal, prefixes)
|
|
299
|
-
|
|
300
|
-
if isinstance(traversal, AllProperties):
|
|
301
|
-
query = _generate_all_properties_query_statement(cast(str, triples[0].subject))
|
|
302
|
-
elif isinstance(traversal, AllReferences) and isinstance(triples[0].object, str):
|
|
303
|
-
query = _generate_all_references_query_statement(triples[0].object)
|
|
304
|
-
elif isinstance(traversal, SingleProperty):
|
|
305
|
-
query = _generate_single_property_query_statement(
|
|
306
|
-
cast(str, triples[0].subject), cast(str, triples[0].predicate)
|
|
307
|
-
)
|
|
308
|
-
elif isinstance(traversal, Hop):
|
|
309
|
-
query = _generate_hop_query_statement(triples)
|
|
310
|
-
else:
|
|
311
|
-
raise ValueError("Not Supported!")
|
|
312
|
-
|
|
313
|
-
# Replacing long URIs with short form using their prefixes
|
|
314
|
-
for prefix, URI in prefixes.items():
|
|
315
|
-
query = query.replace(URI, f"{prefix}:")
|
|
316
|
-
|
|
317
|
-
return query.replace(
|
|
318
|
-
"insertPrefixes\n\n",
|
|
319
|
-
_generate_prefix_header(prefixes) if insert_prefixes else "",
|
|
320
|
-
)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def compress_uri(uri: URIRef, prefixes: dict) -> str:
|
|
324
|
-
"""Compresses URI to prefix:entity_id
|
|
325
|
-
|
|
326
|
-
Parameters
|
|
327
|
-
----------
|
|
328
|
-
uri : URIRef
|
|
329
|
-
URI of entity
|
|
330
|
-
prefixes : dict
|
|
331
|
-
Dictionary of prefixes
|
|
332
|
-
|
|
333
|
-
Returns
|
|
334
|
-
-------
|
|
335
|
-
str
|
|
336
|
-
Compressed URI or original URI if no prefix is found
|
|
337
|
-
"""
|
|
338
|
-
return next(
|
|
339
|
-
(
|
|
340
|
-
f"{prefix}:{uri.replace(namespace, '')}"
|
|
341
|
-
for prefix, namespace in prefixes.items()
|
|
342
|
-
if uri.startswith(namespace)
|
|
343
|
-
),
|
|
344
|
-
uri,
|
|
345
|
-
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def _hop2property_path(graph: Graph, hop: Hop, prefixes: dict[str, Namespace]) -> str:
|
|
349
|
-
"""Converts hop to property path string
|
|
350
|
-
|
|
351
|
-
Parameters
|
|
352
|
-
----------
|
|
353
|
-
graph : Graph
|
|
354
|
-
Graph containing instances of classes
|
|
355
|
-
hop : Hop
|
|
356
|
-
Hop to convert
|
|
357
|
-
prefixes : dict[str, Namespace]
|
|
358
|
-
Dictionary of prefixes to use for compression and predicate quering
|
|
359
|
-
|
|
360
|
-
Returns
|
|
361
|
-
-------
|
|
362
|
-
str
|
|
363
|
-
Property path string for hop traversal (e.g. ^rdf:type/rdfs:subClassOf)
|
|
364
|
-
"""
|
|
365
|
-
|
|
366
|
-
# setting previous step to origin, as we are starting from there
|
|
367
|
-
previous_step = Step(class_=hop.class_, direction="origin")
|
|
368
|
-
|
|
369
|
-
# add triples for all steps until destination
|
|
370
|
-
property_path = ""
|
|
371
|
-
for current_step in hop.traversal:
|
|
372
|
-
sub_entity, obj_entity = (
|
|
373
|
-
(current_step, previous_step) if current_step.direction == "source" else (previous_step, current_step)
|
|
374
|
-
)
|
|
375
|
-
|
|
376
|
-
predicate_raw = _get_predicate_id(graph, sub_entity.class_.id, obj_entity.class_.id, prefixes)
|
|
377
|
-
|
|
378
|
-
predicate = compress_uri(predicate_raw, prefixes)
|
|
379
|
-
|
|
380
|
-
predicate = f"^{predicate}" if current_step.direction == "source" else predicate
|
|
381
|
-
property_path += f"{predicate}/"
|
|
382
|
-
|
|
383
|
-
previous_step = current_step
|
|
384
|
-
|
|
385
|
-
if previous_step.property:
|
|
386
|
-
return property_path + previous_step.property.id
|
|
387
|
-
else:
|
|
388
|
-
# removing "/" at the end of property path if there is no property at the end
|
|
389
|
-
return property_path[:-1]
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
def build_construct_query(
|
|
393
|
-
graph: Graph,
|
|
394
|
-
class_: str,
|
|
395
|
-
transformation_rules: Rules,
|
|
396
|
-
properties_optional: bool = True,
|
|
397
|
-
class_instances: list[URIRef] | None = None,
|
|
398
|
-
) -> str:
|
|
399
|
-
"""Builds CONSTRUCT query for given class and rules and optionally filters by class instances
|
|
400
|
-
|
|
401
|
-
Parameters
|
|
402
|
-
----------
|
|
403
|
-
graph : Graph
|
|
404
|
-
Graph containing instances of classes
|
|
405
|
-
class_ : str
|
|
406
|
-
ID of class for which class_instance we want to query
|
|
407
|
-
transformation_rules : TransformationRules
|
|
408
|
-
Transformation rules to use for query generation
|
|
409
|
-
properties_optional : bool, optional
|
|
410
|
-
Whether to make all properties optional, default True
|
|
411
|
-
class_instances : list[URIRef], optional
|
|
412
|
-
List of class instances to filter by, default None (no filter return all instances)
|
|
413
|
-
|
|
414
|
-
Returns
|
|
415
|
-
-------
|
|
416
|
-
str
|
|
417
|
-
CONSTRUCT query
|
|
418
|
-
|
|
419
|
-
Notes
|
|
420
|
-
-----
|
|
421
|
-
Construct query is far less unforgiving than SELECT query, in sense that it will not return
|
|
422
|
-
anything if one of the properties that define "shape" of the class instance is missing.
|
|
423
|
-
This is the reason why there is option to make all properties optional, so that query will
|
|
424
|
-
return all instances that have at least one property defined.
|
|
425
|
-
|
|
426
|
-
"""
|
|
427
|
-
|
|
428
|
-
query_template = "CONSTRUCT {graph_template\n}\n\nWHERE {graph_pattern\ninsert_filter}"
|
|
429
|
-
query_template = _add_filter(class_instances, query_template)
|
|
430
|
-
|
|
431
|
-
templates, patterns = _to_construct_triples(graph, class_, transformation_rules, properties_optional)
|
|
432
|
-
|
|
433
|
-
graph_template = "\n ".join(_triples2sparql_statement(templates))
|
|
434
|
-
graph_pattern = "\n ".join(_triples2sparql_statement(patterns))
|
|
435
|
-
|
|
436
|
-
return query_template.replace("graph_template", graph_template).replace("graph_pattern", graph_pattern)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
def _add_filter(class_instances, query_template):
|
|
440
|
-
if class_instances:
|
|
441
|
-
class_instances_formatted = [f"<{instance}>" for instance in class_instances]
|
|
442
|
-
query_template = query_template.replace(
|
|
443
|
-
"insert_filter",
|
|
444
|
-
f"\n\nFILTER (?subject IN ({', '.join(class_instances_formatted)}))",
|
|
445
|
-
)
|
|
446
|
-
else:
|
|
447
|
-
query_template = query_template.replace("insert_filter", "")
|
|
448
|
-
return query_template
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
def _triples2sparql_statement(triples: list[Triple]):
|
|
452
|
-
return [
|
|
453
|
-
(
|
|
454
|
-
f"OPTIONAL {{ {triple.subject} {triple.predicate} {triple.object} . }}"
|
|
455
|
-
if triple.optional
|
|
456
|
-
else f"{triple.subject} {triple.predicate} {triple.object} ."
|
|
457
|
-
)
|
|
458
|
-
for triple in triples
|
|
459
|
-
]
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
def _to_construct_triples(
|
|
463
|
-
graph: Graph,
|
|
464
|
-
class_: str,
|
|
465
|
-
transformation_rules: Rules,
|
|
466
|
-
properties_optional: bool = True,
|
|
467
|
-
) -> tuple[list[Triple], list[Triple]]:
|
|
468
|
-
"""Converts class definition to CONSTRUCT triples which are used to generate CONSTRUCT query
|
|
469
|
-
|
|
470
|
-
Parameters
|
|
471
|
-
----------
|
|
472
|
-
graph : Graph
|
|
473
|
-
Graph containing instances of classes
|
|
474
|
-
class_ : str
|
|
475
|
-
ID of class for which class_instance we want to query
|
|
476
|
-
transformation_rules : TransformationRules
|
|
477
|
-
Transformation rules to use for query generation
|
|
478
|
-
|
|
479
|
-
Returns
|
|
480
|
-
-------
|
|
481
|
-
tuple[list[Triple],list[Triple]]
|
|
482
|
-
Tuple of triples that define graph template and graph pattern parts of CONSTRUCT query
|
|
483
|
-
"""
|
|
484
|
-
# TODO: Add handling of UNIONs in rules
|
|
485
|
-
|
|
486
|
-
templates = []
|
|
487
|
-
patterns = []
|
|
488
|
-
|
|
489
|
-
class_ids = []
|
|
490
|
-
for property_ in get_classes_with_properties(transformation_rules)[class_]:
|
|
491
|
-
if property_.rule_type != TransformationRuleType.rdfpath or property_.skip_rule:
|
|
492
|
-
continue
|
|
493
|
-
if not isinstance(property_.rule, str):
|
|
494
|
-
raise ValueError("Rule must be string!")
|
|
495
|
-
traversal = parse_rule(property_.rule, property_.rule_type).traversal
|
|
496
|
-
|
|
497
|
-
if isinstance(traversal, Traversal):
|
|
498
|
-
class_ids.append(traversal.class_.id)
|
|
499
|
-
|
|
500
|
-
graph_template_triple = Triple(
|
|
501
|
-
subject="?subject",
|
|
502
|
-
predicate=f"{transformation_rules.metadata.prefix}:{property_.property_id}",
|
|
503
|
-
object=f'?{re.sub(r"[^_a-zA-Z0-9/_]", "_", str(property_.property_id).lower())}',
|
|
504
|
-
optional=False,
|
|
505
|
-
)
|
|
506
|
-
templates.append(graph_template_triple)
|
|
507
|
-
|
|
508
|
-
# AllReferences should not be "optional" since we are creating their values
|
|
509
|
-
# by binding them to certain property
|
|
510
|
-
if isinstance(traversal, AllReferences):
|
|
511
|
-
graph_pattern_triple = Triple(
|
|
512
|
-
subject="BIND(?subject",
|
|
513
|
-
predicate="AS",
|
|
514
|
-
object=f"{graph_template_triple.object})",
|
|
515
|
-
optional=False,
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
elif isinstance(traversal, SingleProperty):
|
|
519
|
-
graph_pattern_triple = Triple(
|
|
520
|
-
subject=graph_template_triple.subject,
|
|
521
|
-
predicate=traversal.property.id,
|
|
522
|
-
object=graph_template_triple.object,
|
|
523
|
-
optional=True if properties_optional else not property_.is_mandatory,
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
elif isinstance(traversal, Hop):
|
|
527
|
-
graph_pattern_triple = Triple(
|
|
528
|
-
subject="?subject",
|
|
529
|
-
predicate=_hop2property_path(graph, traversal, transformation_rules.prefixes),
|
|
530
|
-
object=graph_template_triple.object,
|
|
531
|
-
optional=True if properties_optional else not property_.is_mandatory,
|
|
532
|
-
)
|
|
533
|
-
else:
|
|
534
|
-
continue
|
|
535
|
-
|
|
536
|
-
patterns.append(graph_pattern_triple)
|
|
537
|
-
|
|
538
|
-
# add first triple for graph pattern stating type of object
|
|
539
|
-
patterns.insert(
|
|
540
|
-
0,
|
|
541
|
-
Triple(
|
|
542
|
-
subject="?subject",
|
|
543
|
-
predicate="a",
|
|
544
|
-
object=_most_occurring_element(class_ids),
|
|
545
|
-
optional=False,
|
|
546
|
-
),
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
return templates, patterns
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
def _most_occurring_element(list_of_elements: list):
|
|
553
|
-
counts = Counter(list_of_elements)
|
|
554
|
-
return counts.most_common(1)[0][0]
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
def triples2dictionary(triples: Iterable[tuple[URIRef, URIRef, str | URIRef]]) -> dict[URIRef, dict[str, list[str]]]:
|
|
558
|
-
"""Converts list of triples to dictionary"""
|
|
559
|
-
dictionary: dict[URIRef, dict[str, list[str]]] = {}
|
|
560
|
-
for triple in triples:
|
|
561
|
-
id_: str
|
|
562
|
-
property_: str
|
|
563
|
-
value: str
|
|
564
|
-
uri: URIRef
|
|
565
|
-
id_, property_, value = remove_namespace_from_uri(*triple) # type: ignore[misc]
|
|
566
|
-
uri = triple[0]
|
|
567
|
-
|
|
568
|
-
if uri not in dictionary:
|
|
569
|
-
dictionary[uri] = {"external_id": [id_]}
|
|
570
|
-
|
|
571
|
-
if property_ not in dictionary[uri]:
|
|
572
|
-
dictionary[uri][property_] = [value]
|
|
573
|
-
else:
|
|
574
|
-
dictionary[uri][property_].append(value)
|
|
575
|
-
return dictionary
|