cognite-neat 0.87.4__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/extractors/_classic_cdf/_assets.py +8 -2
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -2
- cognite/neat/graph/loaders/_base.py +17 -12
- cognite/neat/graph/loaders/_rdf2asset.py +223 -58
- cognite/neat/graph/loaders/_rdf2dms.py +1 -1
- cognite/neat/graph/stores/_base.py +5 -0
- cognite/neat/rules/analysis/_asset.py +31 -1
- cognite/neat/rules/importers/_inference2rules.py +31 -35
- cognite/neat/rules/models/information/_rules.py +1 -1
- 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.4.dist-info → cognite_neat-0.88.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.87.4.dist-info → cognite_neat-0.88.0.dist-info}/RECORD +24 -132
- 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.4.dist-info → cognite_neat-0.88.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.87.4.dist-info → cognite_neat-0.88.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.87.4.dist-info → cognite_neat-0.88.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
from typing import cast
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
from rdflib import OWL, Graph
|
|
6
|
-
|
|
7
|
-
from cognite.neat.utils.rdf_ import remove_namespace_from_uri
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def parse_owl_classes(graph: Graph, make_compliant: bool = False, language: str = "en") -> pd.DataFrame:
|
|
11
|
-
"""Parse owl classes from graph to pandas dataframe.
|
|
12
|
-
|
|
13
|
-
Args:
|
|
14
|
-
graph: Graph containing owl classes
|
|
15
|
-
make_compliant: Flag for generating compliant classes, by default False
|
|
16
|
-
language: Language to use for parsing, by default "en"
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
Dataframe containing owl classes
|
|
20
|
-
|
|
21
|
-
!!! note "make_compliant"
|
|
22
|
-
If `make_compliant` is set to True, in presence of errors, default values will be used instead.
|
|
23
|
-
This makes the method very opinionated, but results in a compliant classes.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
query = """
|
|
27
|
-
SELECT ?class ?name ?description ?parentClass ?deprecated ?deprecationDate
|
|
28
|
-
?replacedBy ?source ?sourceEntity ?match ?comment
|
|
29
|
-
WHERE {
|
|
30
|
-
?class a owl:Class .
|
|
31
|
-
OPTIONAL {?class rdfs:subClassOf ?parentClass }.
|
|
32
|
-
OPTIONAL {?class rdfs:label ?name }.
|
|
33
|
-
OPTIONAL {?class rdfs:comment ?description} .
|
|
34
|
-
OPTIONAL {?class owl:deprecated ?deprecated} .
|
|
35
|
-
FILTER (!isBlank(?class))
|
|
36
|
-
FILTER (!bound(?parentClass) || !isBlank(?parentClass))
|
|
37
|
-
FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
|
|
38
|
-
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
39
|
-
}
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
# create raw dataframe
|
|
43
|
-
|
|
44
|
-
raw_df = _parse_raw_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
|
|
45
|
-
if raw_df.empty:
|
|
46
|
-
return pd.concat([raw_df, pd.DataFrame([len(raw_df) * [""]])], ignore_index=True)
|
|
47
|
-
|
|
48
|
-
# group values and clean up
|
|
49
|
-
processed_df = _clean_up_classes(raw_df)
|
|
50
|
-
|
|
51
|
-
# make compliant
|
|
52
|
-
if make_compliant:
|
|
53
|
-
processed_df = make_classes_compliant(processed_df)
|
|
54
|
-
|
|
55
|
-
# Make Parent Class list elements into string joined with comma
|
|
56
|
-
processed_df["Parent Class"] = processed_df["Parent Class"].apply(
|
|
57
|
-
lambda x: ", ".join(x) if isinstance(x, list) and x else None
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
return processed_df
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _parse_raw_dataframe(query_results: list[tuple]) -> pd.DataFrame:
|
|
64
|
-
df = pd.DataFrame(
|
|
65
|
-
query_results,
|
|
66
|
-
columns=[
|
|
67
|
-
"Class",
|
|
68
|
-
"Name",
|
|
69
|
-
"Description",
|
|
70
|
-
"Parent Class",
|
|
71
|
-
"Deprecated",
|
|
72
|
-
"Deprecation Date",
|
|
73
|
-
"Replaced By",
|
|
74
|
-
"Source",
|
|
75
|
-
"Source Entity Name",
|
|
76
|
-
"Match",
|
|
77
|
-
"Comment",
|
|
78
|
-
],
|
|
79
|
-
)
|
|
80
|
-
if df.empty:
|
|
81
|
-
return df
|
|
82
|
-
|
|
83
|
-
# # remove NaNs
|
|
84
|
-
df.replace(np.nan, "", regex=True, inplace=True)
|
|
85
|
-
|
|
86
|
-
df.Source = df.Class
|
|
87
|
-
df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x))
|
|
88
|
-
df["Source Entity Name"] = df.Class
|
|
89
|
-
df["Match"] = len(df) * ["exact"]
|
|
90
|
-
df["Parent Class"] = df["Parent Class"].apply(lambda x: remove_namespace_from_uri(x))
|
|
91
|
-
|
|
92
|
-
return df
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def _clean_up_classes(df: pd.DataFrame) -> pd.DataFrame:
|
|
96
|
-
clean_list = [
|
|
97
|
-
{
|
|
98
|
-
"Class": class_,
|
|
99
|
-
"Name": group_df["Name"].unique()[0],
|
|
100
|
-
"Description": "\n".join(list(group_df.Description.unique())),
|
|
101
|
-
"Parent Class": ", ".join(list(group_df["Parent Class"].unique())),
|
|
102
|
-
"Deprecated": group_df.Deprecated.unique()[0],
|
|
103
|
-
"Deprecation Date": group_df["Deprecation Date"].unique()[0],
|
|
104
|
-
"Replaced By": group_df["Replaced By"].unique()[0],
|
|
105
|
-
"Source": group_df["Source"].unique()[0],
|
|
106
|
-
"Source Entity Name": group_df["Name"].unique()[0],
|
|
107
|
-
"Match Type": group_df["Match"].unique()[0],
|
|
108
|
-
"Comment": group_df["Comment"].unique()[0],
|
|
109
|
-
}
|
|
110
|
-
for class_, group_df in df.groupby("Class")
|
|
111
|
-
]
|
|
112
|
-
|
|
113
|
-
df = pd.DataFrame(clean_list)
|
|
114
|
-
|
|
115
|
-
# bring NaNs back
|
|
116
|
-
df.replace("", None, inplace=True)
|
|
117
|
-
|
|
118
|
-
# split Parent Class column back into list
|
|
119
|
-
df["Parent Class"] = df["Parent Class"].apply(lambda x: x.split(", ") if isinstance(x, str) else None)
|
|
120
|
-
|
|
121
|
-
return df
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def make_classes_compliant(classes: pd.DataFrame) -> pd.DataFrame:
|
|
125
|
-
"""Make classes compliant.
|
|
126
|
-
|
|
127
|
-
Returns:
|
|
128
|
-
Dataframe containing compliant classes
|
|
129
|
-
|
|
130
|
-
!!! note "About the compliant classes"
|
|
131
|
-
The compliant classes are based on the OWL base ontology, but adapted to NEAT and use in CDF.
|
|
132
|
-
One thing to note is that this method would not be able to fix issues with class ids which
|
|
133
|
-
are not compliant with the CDF naming convention. For example, if a class id contains a space,
|
|
134
|
-
starts with a number, etc. This will cause issues when trying to create the class in CDF.
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
-
# Replace empty or non-string values in "Match Type" column with "exact"
|
|
138
|
-
classes["Match Type"] = classes["Match Type"].fillna("exact")
|
|
139
|
-
classes["Match Type"] = classes["Match Type"].apply(
|
|
140
|
-
lambda x: "exact" if not isinstance(x, str) or len(x) == 0 else x
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Replace empty or non-string values in "Comment" column with a default value
|
|
144
|
-
classes["Comment"] = classes["Comment"].fillna("Imported from Ontology by NEAT")
|
|
145
|
-
classes["Comment"] = classes["Comment"].apply(
|
|
146
|
-
lambda x: "Imported from Ontology by NEAT" if not isinstance(x, str) or len(x) == 0 else x
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
# Replace empty or non-boolean values in "Deprecated" column with False
|
|
150
|
-
classes["Deprecated"] = classes["Deprecated"].fillna(False)
|
|
151
|
-
classes["Deprecated"] = classes["Deprecated"].apply(lambda x: False if not isinstance(x, bool) else x)
|
|
152
|
-
|
|
153
|
-
# Add _object_property_class, _data_type_property_class, _thing_class to the dataframe
|
|
154
|
-
classes = pd.concat(
|
|
155
|
-
[classes, pd.DataFrame([_object_property_class(), _data_type_property_class(), _thing_class()])],
|
|
156
|
-
ignore_index=True,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
# Reduce length of elements in the "Description" column to 1024 characters
|
|
160
|
-
classes["Description"] = classes["Description"].apply(lambda x: x[:1024] if isinstance(x, str) else None)
|
|
161
|
-
|
|
162
|
-
# Add missing parent classes to the dataframe
|
|
163
|
-
classes = pd.concat(
|
|
164
|
-
[classes, pd.DataFrame(_add_parent_class(classes))],
|
|
165
|
-
ignore_index=True,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
return classes
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def _object_property_class() -> dict:
|
|
172
|
-
return {
|
|
173
|
-
"Class": "ObjectProperty",
|
|
174
|
-
"Name": None,
|
|
175
|
-
"Description": "The class of object properties.",
|
|
176
|
-
"Parent Class": None,
|
|
177
|
-
"Source": OWL.ObjectProperty,
|
|
178
|
-
"Match Type": "exact",
|
|
179
|
-
"Comment": "Added by NEAT based on owl:ObjectProperty but adapted to NEAT and use in CDF.",
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
def _data_type_property_class() -> dict:
|
|
184
|
-
return {
|
|
185
|
-
"Class": "DatatypeProperty",
|
|
186
|
-
"Name": None,
|
|
187
|
-
"Description": "The class of data properties.",
|
|
188
|
-
"Parent Class": None,
|
|
189
|
-
"Source": OWL.DatatypeProperty,
|
|
190
|
-
"Match Type": "exact",
|
|
191
|
-
"Comment": "Added by NEAT based on owl:DatatypeProperty but adapted to NEAT and use in CDF.",
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def _thing_class() -> dict:
|
|
196
|
-
return {
|
|
197
|
-
"Class": "ThingContainer",
|
|
198
|
-
"Name": None,
|
|
199
|
-
"Description": "The class of holding class individuals.",
|
|
200
|
-
"Parent Class": None,
|
|
201
|
-
"Source": OWL.Thing,
|
|
202
|
-
"Match Type": "exact",
|
|
203
|
-
"Comment": (
|
|
204
|
-
"Added by NEAT. "
|
|
205
|
-
"Imported from OWL base ontology, it is meant for use as a default"
|
|
206
|
-
" value type for object properties which miss a declared range."
|
|
207
|
-
),
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
def _add_parent_class(df: pd.DataFrame) -> list[dict]:
|
|
212
|
-
parent_set = {
|
|
213
|
-
item
|
|
214
|
-
for sublist in df["Parent Class"].tolist()
|
|
215
|
-
if sublist
|
|
216
|
-
for item in sublist
|
|
217
|
-
if item != "" and item is not None
|
|
218
|
-
}
|
|
219
|
-
class_set = set(df["Class"].tolist())
|
|
220
|
-
|
|
221
|
-
rows = []
|
|
222
|
-
for missing_parent_class in parent_set.difference(class_set):
|
|
223
|
-
rows += [
|
|
224
|
-
{
|
|
225
|
-
"Class": missing_parent_class,
|
|
226
|
-
"Name": None,
|
|
227
|
-
"Description": None,
|
|
228
|
-
"Parent Class": None,
|
|
229
|
-
"Source": None,
|
|
230
|
-
"Match Type": None,
|
|
231
|
-
"Comment": (
|
|
232
|
-
"Added by NEAT. "
|
|
233
|
-
"This is a parent class that is missing in the ontology. "
|
|
234
|
-
"It is added by NEAT to make the ontology compliant with CDF."
|
|
235
|
-
),
|
|
236
|
-
}
|
|
237
|
-
]
|
|
238
|
-
|
|
239
|
-
return rows
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
import pandas as pd
|
|
5
|
-
from rdflib import Graph, Namespace
|
|
6
|
-
|
|
7
|
-
from cognite.neat.legacy.rules.models.rules import (
|
|
8
|
-
cdf_space_compliance_regex,
|
|
9
|
-
data_model_id_compliance_regex,
|
|
10
|
-
prefix_compliance_regex,
|
|
11
|
-
version_compliance_regex,
|
|
12
|
-
)
|
|
13
|
-
from cognite.neat.utils.collection_ import remove_none_elements_from_set
|
|
14
|
-
from cognite.neat.utils.rdf_ import convert_rdflib_content
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def parse_owl_metadata(graph: Graph, make_compliant: bool = False) -> pd.DataFrame:
|
|
18
|
-
"""Parse owl metadata from graph to pandas dataframe.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
graph: Graph containing owl metadata
|
|
22
|
-
make_compliant: Flag for generating compliant metadata, by default False
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Dataframe containing owl metadata
|
|
26
|
-
|
|
27
|
-
!!! note "make_compliant"
|
|
28
|
-
If `make_compliant` is set to True, in presence of errors, default values will be used instead.
|
|
29
|
-
This makes the method very opinionated, but results in a compliant metadata.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"""
|
|
33
|
-
# TODO: Move dataframe to dict representation
|
|
34
|
-
|
|
35
|
-
query = """SELECT ?namespace ?prefix ?dataModelName ?cdfSpaceName ?version ?isCurrentVersion
|
|
36
|
-
?created ?updated ?title ?description ?creator ?contributor ?rights ?license
|
|
37
|
-
WHERE {
|
|
38
|
-
?namespace a owl:Ontology .
|
|
39
|
-
OPTIONAL {?namespace owl:versionInfo ?version }.
|
|
40
|
-
OPTIONAL {?namespace dcterms:creator ?creator }.
|
|
41
|
-
OPTIONAL {?namespace dcterms:title|rdfs:label|skos:prefLabel ?title }.
|
|
42
|
-
OPTIONAL {?namespace dcterms:contributor ?contributor }.
|
|
43
|
-
OPTIONAL {?namespace dcterms:modified ?updated }.
|
|
44
|
-
OPTIONAL {?namespace dcterms:created ?created }.
|
|
45
|
-
OPTIONAL {?namespace dcterms:description ?description }.
|
|
46
|
-
|
|
47
|
-
OPTIONAL {?namespace dcterms:rights|dc:rights ?rights }.
|
|
48
|
-
|
|
49
|
-
OPTIONAL {?namespace dcterms:license|dc:license ?license }.
|
|
50
|
-
FILTER (!isBlank(?namespace))
|
|
51
|
-
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
52
|
-
FILTER (!bound(?title) || LANG(?title) = "" || LANGMATCHES(LANG(?title), "en"))
|
|
53
|
-
}
|
|
54
|
-
"""
|
|
55
|
-
|
|
56
|
-
results = [{item for item in sublist} for sublist in list(zip(*graph.query(query), strict=True))]
|
|
57
|
-
|
|
58
|
-
clean_list = convert_rdflib_content(
|
|
59
|
-
{
|
|
60
|
-
"namespace": Namespace(results[0].pop()),
|
|
61
|
-
"prefix": results[1].pop(),
|
|
62
|
-
"dataModelName": results[2].pop(),
|
|
63
|
-
"cdfSpaceName": results[3].pop(),
|
|
64
|
-
"version": results[4].pop(),
|
|
65
|
-
"isCurrentVersion": results[5].pop(),
|
|
66
|
-
"created": results[6].pop(),
|
|
67
|
-
"updated": results[7].pop(),
|
|
68
|
-
"title": results[8].pop(),
|
|
69
|
-
"description": results[9].pop(),
|
|
70
|
-
"creator": (
|
|
71
|
-
", ".join(remove_none_elements_from_set(results[10]))
|
|
72
|
-
if remove_none_elements_from_set(results[10])
|
|
73
|
-
else None
|
|
74
|
-
),
|
|
75
|
-
"contributor": (
|
|
76
|
-
", ".join(remove_none_elements_from_set(results[11]))
|
|
77
|
-
if remove_none_elements_from_set(results[11])
|
|
78
|
-
else None
|
|
79
|
-
),
|
|
80
|
-
"rights": results[12].pop(),
|
|
81
|
-
"license": results[13].pop(),
|
|
82
|
-
}
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
if make_compliant:
|
|
86
|
-
clean_list.pop("created")
|
|
87
|
-
return pd.DataFrame(list(make_metadata_compliant(clean_list).items()), columns=["Key", "Value"])
|
|
88
|
-
|
|
89
|
-
return pd.DataFrame(list(clean_list.items()), columns=["Key", "Value"])
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def make_metadata_compliant(metadata: dict) -> dict:
|
|
93
|
-
"""Attempts to fix errors in metadata, otherwise defaults to values that will pass validation.
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
metadata: Dictionary containing metadata
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
Dictionary containing metadata with fixed errors
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
metadata = fix_namespace(metadata, default=Namespace("http://purl.org/cognite/neat#"))
|
|
103
|
-
metadata = fix_prefix(metadata)
|
|
104
|
-
metadata = fix_dataModelName(metadata)
|
|
105
|
-
metadata = fix_cdfSpaceName(metadata)
|
|
106
|
-
metadata = fix_version(metadata)
|
|
107
|
-
metadata = fix_isCurrentVersion(metadata)
|
|
108
|
-
metadata = fix_date(metadata, date_type="created", default=datetime.datetime.now().replace(microsecond=0))
|
|
109
|
-
metadata = fix_date(metadata, date_type="updated", default=datetime.datetime.now().replace(microsecond=0))
|
|
110
|
-
metadata = fix_title(metadata)
|
|
111
|
-
metadata = fix_description(metadata)
|
|
112
|
-
metadata = fix_author(metadata, "creator")
|
|
113
|
-
metadata = fix_author(metadata, "contributor", "Cognite")
|
|
114
|
-
metadata = fix_rights(metadata)
|
|
115
|
-
metadata = fix_license(metadata)
|
|
116
|
-
|
|
117
|
-
return metadata
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def fix_license(metadata: dict, default: str = "Unknown license") -> dict:
|
|
121
|
-
if license := metadata.get("license", None):
|
|
122
|
-
if not isinstance(license, str):
|
|
123
|
-
metadata["license"] = default
|
|
124
|
-
elif isinstance(license, str) and len(license) == 0:
|
|
125
|
-
metadata["license"] = default
|
|
126
|
-
else:
|
|
127
|
-
metadata["license"] = default
|
|
128
|
-
return metadata
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def fix_rights(metadata: dict, default: str = "Unknown rights") -> dict:
|
|
132
|
-
if rights := metadata.get("rights", None):
|
|
133
|
-
if not isinstance(rights, str):
|
|
134
|
-
metadata["rights"] = default
|
|
135
|
-
elif isinstance(rights, str) and len(rights) == 0:
|
|
136
|
-
metadata["rights"] = default
|
|
137
|
-
else:
|
|
138
|
-
metadata["rights"] = default
|
|
139
|
-
return metadata
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def fix_author(metadata: dict, author_type: str = "creator", default: str = "NEAT") -> dict:
|
|
143
|
-
if author := metadata.get(author_type, None):
|
|
144
|
-
if not isinstance(author, str) or isinstance(author, list):
|
|
145
|
-
metadata[author_type] = default
|
|
146
|
-
elif isinstance(author, str) and len(author) == 0:
|
|
147
|
-
metadata[author_type] = default
|
|
148
|
-
else:
|
|
149
|
-
metadata[author_type] = default
|
|
150
|
-
return metadata
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def fix_description(metadata: dict, default: str = "This model has been inferred from OWL ontology") -> dict:
|
|
154
|
-
if description := metadata.get("description", None):
|
|
155
|
-
if not isinstance(description, str) or len(description) == 0:
|
|
156
|
-
metadata["description"] = default
|
|
157
|
-
elif isinstance(description, str) and len(description) > 1024:
|
|
158
|
-
metadata["description"] = metadata["description"][:1024]
|
|
159
|
-
else:
|
|
160
|
-
metadata["description"] = default
|
|
161
|
-
return metadata
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def fix_cdfSpaceName(metadata: dict, default: str = "playground") -> dict:
|
|
165
|
-
if space := metadata.get("cdfSpaceName", None):
|
|
166
|
-
if not isinstance(space, str) or not re.match(cdf_space_compliance_regex, space):
|
|
167
|
-
metadata["cdfSpaceName"] = default
|
|
168
|
-
else:
|
|
169
|
-
metadata["cdfSpaceName"] = default
|
|
170
|
-
return metadata
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
def fix_dataModelName(metadata: dict, default: str = "neat") -> dict:
|
|
174
|
-
if data_model_name := metadata.get("dataModelName", None):
|
|
175
|
-
if not isinstance(data_model_name, str) or not re.match(data_model_id_compliance_regex, data_model_name):
|
|
176
|
-
metadata["dataModelName"] = default
|
|
177
|
-
else:
|
|
178
|
-
metadata["dataModelName"] = default
|
|
179
|
-
return metadata
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def fix_prefix(metadata: dict, default: str = "neat") -> dict:
|
|
183
|
-
if prefix := metadata.get("prefix", None):
|
|
184
|
-
if not isinstance(prefix, str) or not re.match(prefix_compliance_regex, prefix):
|
|
185
|
-
metadata["prefix"] = default
|
|
186
|
-
else:
|
|
187
|
-
metadata["prefix"] = default
|
|
188
|
-
return metadata
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def fix_namespace(metadata: dict, default: Namespace) -> dict:
|
|
192
|
-
if namespace := metadata.get("namespace", None):
|
|
193
|
-
if not isinstance(namespace, Namespace):
|
|
194
|
-
try:
|
|
195
|
-
metadata["namespace"] = Namespace(namespace)
|
|
196
|
-
except Exception:
|
|
197
|
-
metadata["namespace"] = default
|
|
198
|
-
else:
|
|
199
|
-
metadata["namespace"] = default
|
|
200
|
-
|
|
201
|
-
return metadata
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
def fix_date(
|
|
205
|
-
metadata: dict,
|
|
206
|
-
date_type: str,
|
|
207
|
-
default: datetime.datetime,
|
|
208
|
-
) -> dict:
|
|
209
|
-
if date := metadata.get(date_type, None):
|
|
210
|
-
try:
|
|
211
|
-
if isinstance(date, datetime.datetime):
|
|
212
|
-
pass
|
|
213
|
-
elif isinstance(date, datetime.date):
|
|
214
|
-
metadata[date_type] = datetime.datetime.combine(metadata[date_type], datetime.datetime.min.time())
|
|
215
|
-
elif isinstance(date, str):
|
|
216
|
-
metadata[date_type] = datetime.datetime.strptime(metadata[date_type], "%Y-%m-%dT%H:%M:%SZ")
|
|
217
|
-
else:
|
|
218
|
-
metadata[date_type] = default
|
|
219
|
-
except Exception:
|
|
220
|
-
metadata[date_type] = default
|
|
221
|
-
else:
|
|
222
|
-
metadata[date_type] = default
|
|
223
|
-
|
|
224
|
-
return metadata
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def fix_version(metadata: dict, default: str = "1.0.0") -> dict:
|
|
228
|
-
if version := metadata.get("version", None):
|
|
229
|
-
if not re.match(version_compliance_regex, version):
|
|
230
|
-
metadata["version"] = default
|
|
231
|
-
else:
|
|
232
|
-
metadata["version"] = default
|
|
233
|
-
|
|
234
|
-
return metadata
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
def fix_isCurrentVersion(metadata: dict, default: bool = True) -> dict:
|
|
238
|
-
if isCurrentVersion := metadata.get("isCurrentVersion", None):
|
|
239
|
-
if not isinstance(isCurrentVersion, bool):
|
|
240
|
-
metadata["isCurrentVersion"] = default
|
|
241
|
-
else:
|
|
242
|
-
metadata["isCurrentVersion"] = default
|
|
243
|
-
|
|
244
|
-
return metadata
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
def fix_title(metadata: dict, default: str = "OWL Inferred Data Model") -> dict:
|
|
248
|
-
if title := metadata.get("title", None):
|
|
249
|
-
if not isinstance(title, str):
|
|
250
|
-
metadata["title"] = default
|
|
251
|
-
elif isinstance(title, str) and len(title) == 0:
|
|
252
|
-
metadata["title"] = default
|
|
253
|
-
elif isinstance(title, str) and len(title) > 255:
|
|
254
|
-
metadata["title"] = metadata["title"][:255]
|
|
255
|
-
else:
|
|
256
|
-
pass
|
|
257
|
-
else:
|
|
258
|
-
metadata["title"] = default
|
|
259
|
-
|
|
260
|
-
return metadata
|