cognite-neat 0.87.6__py3-none-any.whl → 0.88.1__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/configuration.py +1 -1
- cognite/neat/app/api/routers/crud.py +11 -21
- cognite/neat/app/api/routers/workflows.py +24 -94
- cognite/neat/app/ui/neat-app/build/asset-manifest.json +7 -7
- cognite/neat/app/ui/neat-app/build/index.html +1 -1
- cognite/neat/app/ui/neat-app/build/static/css/{main.38a62222.css → main.72e3d92e.css} +2 -2
- cognite/neat/app/ui/neat-app/build/static/css/main.72e3d92e.css.map +1 -0
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js +3 -0
- cognite/neat/app/ui/neat-app/build/static/js/{main.ec7f72e2.js.LICENSE.txt → main.5a52cf09.js.LICENSE.txt} +0 -9
- cognite/neat/app/ui/neat-app/build/static/js/main.5a52cf09.js.map +1 -0
- cognite/neat/config.py +44 -27
- cognite/neat/exceptions.py +6 -0
- cognite/neat/graph/extractors/_classic_cdf/_assets.py +21 -73
- cognite/neat/graph/extractors/_classic_cdf/_base.py +102 -0
- cognite/neat/graph/extractors/_classic_cdf/_events.py +46 -42
- cognite/neat/graph/extractors/_classic_cdf/_files.py +41 -45
- cognite/neat/graph/extractors/_classic_cdf/_labels.py +75 -52
- cognite/neat/graph/extractors/_classic_cdf/_relationships.py +49 -27
- cognite/neat/graph/extractors/_classic_cdf/_sequences.py +47 -50
- cognite/neat/graph/extractors/_classic_cdf/_timeseries.py +47 -49
- cognite/neat/graph/queries/_base.py +22 -29
- cognite/neat/graph/queries/_shared.py +1 -1
- cognite/neat/graph/stores/_base.py +24 -11
- cognite/neat/graph/transformers/_rdfpath.py +3 -2
- cognite/neat/issues.py +8 -0
- cognite/neat/rules/exporters/_rules2ontology.py +28 -20
- cognite/neat/rules/exporters/_validation.py +15 -21
- cognite/neat/rules/importers/_inference2rules.py +31 -35
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +3 -7
- cognite/neat/rules/importers/_spreadsheet2rules.py +30 -27
- cognite/neat/rules/issues/dms.py +20 -0
- cognite/neat/rules/issues/importing.py +15 -0
- cognite/neat/rules/issues/ontology.py +298 -0
- cognite/neat/rules/issues/spreadsheet.py +48 -0
- cognite/neat/rules/issues/tables.py +72 -0
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/_types/_field.py +9 -19
- cognite/neat/rules/models/information/_rules.py +5 -4
- cognite/neat/utils/rdf_.py +17 -9
- cognite/neat/utils/regex_patterns.py +52 -0
- 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.1.dist-info}/METADATA +2 -6
- cognite_neat-0.88.1.dist-info/RECORD +209 -0
- 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/app/ui/neat-app/build/static/css/main.38a62222.css.map +0 -1
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js +0 -3
- cognite/neat/app/ui/neat-app/build/static/js/main.ec7f72e2.js.map +0 -1
- cognite/neat/graph/stores/_oxrdflib.py +0 -247
- 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/rules/exceptions.py +0 -2972
- cognite/neat/rules/models/_types/_base.py +0 -16
- cognite/neat/workflows/examples/Export_Rules_to_Ontology/workflow.yaml +0 -152
- cognite/neat/workflows/examples/Extract_DEXPI_Graph_and_Export_Rules/workflow.yaml +0 -139
- cognite/neat/workflows/examples/Extract_RDF_Graph_and_Generate_Assets/workflow.yaml +0 -270
- cognite/neat/workflows/examples/Ontology_to_Data_Model/workflow.yaml +0 -116
- 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/RECORD +0 -319
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.87.6.dist-info → cognite_neat-0.88.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
from typing import cast
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
from rdflib import Graph
|
|
6
|
-
|
|
7
|
-
from cognite.neat.utils.rdf_ import remove_namespace_from_uri
|
|
8
|
-
|
|
9
|
-
from ._owl2classes import _data_type_property_class, _object_property_class, _thing_class
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def parse_owl_properties(graph: Graph, make_compliant: bool = False, language: str = "en") -> pd.DataFrame:
|
|
13
|
-
"""Get all properties from the OWL ontology
|
|
14
|
-
|
|
15
|
-
Parameters
|
|
16
|
-
----------
|
|
17
|
-
graph : Graph
|
|
18
|
-
Graph to query
|
|
19
|
-
parsing_config : dict, optional
|
|
20
|
-
Configuration for parsing the dataframe, by default None
|
|
21
|
-
|
|
22
|
-
Returns
|
|
23
|
-
-------
|
|
24
|
-
pd.DataFrame
|
|
25
|
-
Dataframe with columns: class, property, name, ...
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
query = """
|
|
29
|
-
|
|
30
|
-
SELECT ?class ?property ?name ?description ?type ?minCount ?maxCount
|
|
31
|
-
?deprecated ?deprecationDate ?replacedBy ?source ?sourceEntity
|
|
32
|
-
?match ?comment ?propertyType
|
|
33
|
-
WHERE {
|
|
34
|
-
?property a ?propertyType.
|
|
35
|
-
FILTER (?propertyType IN (owl:ObjectProperty, owl:DatatypeProperty ) )
|
|
36
|
-
OPTIONAL {?property rdfs:domain ?class }.
|
|
37
|
-
OPTIONAL {?property rdfs:range ?type }.
|
|
38
|
-
OPTIONAL {?property rdfs:label ?name }.
|
|
39
|
-
OPTIONAL {?property rdfs:comment ?description} .
|
|
40
|
-
OPTIONAL {?property owl:maxCardinality ?maxCount} .
|
|
41
|
-
OPTIONAL {?property owl:minCardinality ?minCount} .
|
|
42
|
-
FILTER (!isBlank(?property))
|
|
43
|
-
FILTER (!bound(?type) || !isBlank(?type))
|
|
44
|
-
FILTER (!bound(?class) || !isBlank(?class))
|
|
45
|
-
FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
|
|
46
|
-
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
47
|
-
OPTIONAL {?property owl:deprecated ?deprecated} .
|
|
48
|
-
}
|
|
49
|
-
"""
|
|
50
|
-
|
|
51
|
-
raw_df = _parse_raw_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
|
|
52
|
-
if raw_df.empty:
|
|
53
|
-
return pd.concat([raw_df, pd.DataFrame([len(raw_df) * [""]])], ignore_index=True)
|
|
54
|
-
|
|
55
|
-
# group values and clean up
|
|
56
|
-
processed_df = _clean_up_properties(raw_df)
|
|
57
|
-
|
|
58
|
-
# make compliant
|
|
59
|
-
if make_compliant:
|
|
60
|
-
processed_df = make_properties_compliant(processed_df)
|
|
61
|
-
|
|
62
|
-
# drop column _property_type, which was a helper column:
|
|
63
|
-
processed_df.drop(columns=["_property_type"], inplace=True)
|
|
64
|
-
|
|
65
|
-
return processed_df
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def _parse_raw_dataframe(query_results: list[tuple]) -> pd.DataFrame:
|
|
69
|
-
df = pd.DataFrame(
|
|
70
|
-
query_results,
|
|
71
|
-
columns=[
|
|
72
|
-
"Class",
|
|
73
|
-
"Property",
|
|
74
|
-
"Name",
|
|
75
|
-
"Description",
|
|
76
|
-
"Type",
|
|
77
|
-
"Min Count",
|
|
78
|
-
"Max Count",
|
|
79
|
-
"Deprecated",
|
|
80
|
-
"Deprecation Date",
|
|
81
|
-
"Replaced By",
|
|
82
|
-
"Source",
|
|
83
|
-
"Source Entity Name",
|
|
84
|
-
"Match Type",
|
|
85
|
-
"Comment",
|
|
86
|
-
"_property_type",
|
|
87
|
-
],
|
|
88
|
-
)
|
|
89
|
-
if df.empty:
|
|
90
|
-
return df
|
|
91
|
-
|
|
92
|
-
df.replace(np.nan, "", regex=True, inplace=True)
|
|
93
|
-
|
|
94
|
-
df.Source = df.Property
|
|
95
|
-
df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x))
|
|
96
|
-
df.Property = df.Property.apply(lambda x: remove_namespace_from_uri(x))
|
|
97
|
-
df.Type = df.Type.apply(lambda x: remove_namespace_from_uri(x))
|
|
98
|
-
df["Source Entity Name"] = df.Property
|
|
99
|
-
df["Match Type"] = len(df) * ["exact"]
|
|
100
|
-
df["_property_type"] = df["_property_type"].apply(lambda x: remove_namespace_from_uri(x))
|
|
101
|
-
|
|
102
|
-
return df
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def _clean_up_properties(df: pd.DataFrame) -> pd.DataFrame:
|
|
106
|
-
class_grouped_dfs = df.groupby("Class")
|
|
107
|
-
|
|
108
|
-
clean_list = []
|
|
109
|
-
|
|
110
|
-
for class_, class_grouped_df in class_grouped_dfs:
|
|
111
|
-
property_grouped_dfs = class_grouped_df.groupby("Property")
|
|
112
|
-
for property_, property_grouped_df in property_grouped_dfs:
|
|
113
|
-
clean_list += [
|
|
114
|
-
{
|
|
115
|
-
"Class": class_,
|
|
116
|
-
"Property": property_,
|
|
117
|
-
"Name": property_grouped_df["Name"].unique()[0],
|
|
118
|
-
"Description": "\n".join(list(property_grouped_df.Description.unique()))[:1024],
|
|
119
|
-
"Type": property_grouped_df.Type.unique()[0],
|
|
120
|
-
"Min Count": property_grouped_df["Min Count"].unique()[0],
|
|
121
|
-
"Max Count": property_grouped_df["Max Count"].unique()[0],
|
|
122
|
-
"Deprecated": property_grouped_df.Deprecated.unique()[0],
|
|
123
|
-
"Deprecation Date": property_grouped_df["Deprecation Date"].unique()[0],
|
|
124
|
-
"Replaced By": property_grouped_df["Replaced By"].unique()[0],
|
|
125
|
-
"Source": property_grouped_df["Source"].unique()[0],
|
|
126
|
-
"Source Entity Name": property_grouped_df["Source Entity Name"].unique()[0],
|
|
127
|
-
"Match Type": property_grouped_df["Match Type"].unique()[0],
|
|
128
|
-
"Comment": property_grouped_df["Comment"].unique()[0],
|
|
129
|
-
"_property_type": property_grouped_df["_property_type"].unique()[0],
|
|
130
|
-
}
|
|
131
|
-
]
|
|
132
|
-
|
|
133
|
-
df = pd.DataFrame(clean_list)
|
|
134
|
-
df.replace("", None, inplace=True)
|
|
135
|
-
|
|
136
|
-
return df
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def make_properties_compliant(properties: pd.DataFrame) -> pd.DataFrame:
|
|
140
|
-
# default to None if "Min Count" is not specified
|
|
141
|
-
properties["Min Count"] = properties["Min Count"].apply(lambda x: None if not isinstance(x, int) or x == "" else x)
|
|
142
|
-
|
|
143
|
-
# default to None if "Max Count" is not specified
|
|
144
|
-
properties["Max Count"] = properties["Max Count"].apply(lambda x: 1 if not isinstance(x, int) or x == "" else x)
|
|
145
|
-
|
|
146
|
-
# Replace empty or non-string values in "Match Type" column with "exact"
|
|
147
|
-
properties["Match Type"] = properties["Match Type"].fillna("exact")
|
|
148
|
-
properties["Match Type"] = properties["Match Type"].apply(
|
|
149
|
-
lambda x: "exact" if not isinstance(x, str) or len(x) == 0 else x
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
# Replace empty or non-string values in "Comment" column with a default value
|
|
153
|
-
properties["Comment"] = properties["Comment"].fillna("Imported from Ontology by NEAT")
|
|
154
|
-
properties["Comment"] = properties["Comment"].apply(
|
|
155
|
-
lambda x: "Imported from Ontology by NEAT" if not isinstance(x, str) or len(x) == 0 else x
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Replace empty or non-boolean values in "Deprecated" column with False
|
|
159
|
-
properties["Deprecated"] = properties["Deprecated"].fillna(False)
|
|
160
|
-
properties["Deprecated"] = properties["Deprecated"].apply(lambda x: False if not isinstance(x, bool) else x)
|
|
161
|
-
|
|
162
|
-
# Reduce length of elements in the "Description" column to 1024 characters
|
|
163
|
-
properties["Description"] = properties["Description"].apply(lambda x: x[:1024] if isinstance(x, str) else None)
|
|
164
|
-
|
|
165
|
-
# fixes and additions
|
|
166
|
-
properties = fix_dangling_properties(properties)
|
|
167
|
-
properties = fix_missing_property_value_type(properties)
|
|
168
|
-
|
|
169
|
-
return properties
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
def fix_dangling_properties(properties: pd.DataFrame) -> pd.DataFrame:
|
|
173
|
-
"""This method fixes properties which are missing a domain definition in the ontology.
|
|
174
|
-
|
|
175
|
-
Args:
|
|
176
|
-
properties: Dataframe containing properties
|
|
177
|
-
|
|
178
|
-
Returns:
|
|
179
|
-
Dataframe containing properties with fixed domain
|
|
180
|
-
"""
|
|
181
|
-
domain = {
|
|
182
|
-
"ObjectProperty": _object_property_class()["Class"],
|
|
183
|
-
"DatatypeProperty": _data_type_property_class()["Class"],
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
# apply missing range
|
|
187
|
-
properties["Class"] = properties.apply(
|
|
188
|
-
lambda row: domain[row._property_type]
|
|
189
|
-
if row._property_type == "ObjectProperty" and pd.isna(row["Class"])
|
|
190
|
-
else domain["DatatypeProperty"]
|
|
191
|
-
if pd.isna(row["Class"])
|
|
192
|
-
else row["Class"],
|
|
193
|
-
axis=1,
|
|
194
|
-
)
|
|
195
|
-
return properties
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def fix_missing_property_value_type(properties: pd.DataFrame) -> pd.DataFrame:
|
|
199
|
-
"""This method fixes properties which are missing a range definition in the ontology.
|
|
200
|
-
|
|
201
|
-
Args:
|
|
202
|
-
properties: Dataframe containing properties
|
|
203
|
-
|
|
204
|
-
Returns:
|
|
205
|
-
Dataframe containing properties with fixed range
|
|
206
|
-
"""
|
|
207
|
-
# apply missing range
|
|
208
|
-
properties["Type"] = properties.apply(
|
|
209
|
-
lambda row: _thing_class()["Class"]
|
|
210
|
-
if row._property_type == "ObjectProperty" and pd.isna(row["Type"])
|
|
211
|
-
else "string"
|
|
212
|
-
if pd.isna(row["Type"])
|
|
213
|
-
else row["Type"],
|
|
214
|
-
axis=1,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
return properties
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
"""This module performs importing of various formats to one of serializations for which
|
|
2
|
-
there are loaders to TransformationRules pydantic class."""
|
|
3
|
-
|
|
4
|
-
# TODO: if this module grows too big, split it into several files and place under ./converter directory
|
|
5
|
-
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
import pandas as pd
|
|
9
|
-
from pydantic_core import ErrorDetails
|
|
10
|
-
from rdflib import DC, DCTERMS, OWL, RDF, RDFS, SKOS, Graph
|
|
11
|
-
|
|
12
|
-
from cognite.neat.legacy.rules.importers._base import BaseImporter
|
|
13
|
-
from cognite.neat.legacy.rules.models.raw_rules import RawRules
|
|
14
|
-
from cognite.neat.legacy.rules.models.rules import Rules
|
|
15
|
-
from cognite.neat.legacy.rules.models.tables import Tables
|
|
16
|
-
from cognite.neat.legacy.rules.models.value_types import XSD_VALUE_TYPE_MAPPINGS
|
|
17
|
-
|
|
18
|
-
from ._owl2classes import parse_owl_classes
|
|
19
|
-
from ._owl2metadata import parse_owl_metadata
|
|
20
|
-
from ._owl2properties import parse_owl_properties
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class OWLImporter(BaseImporter):
|
|
24
|
-
"""Convert OWL ontology to tables/ transformation rules / Excel file.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
owl_filepath: Path to OWL ontology
|
|
28
|
-
|
|
29
|
-
!!! Note
|
|
30
|
-
OWL Ontologies typically lacks some information that is required for making a complete
|
|
31
|
-
data model. This means that the methods .to_rules() will typically fail. Instead, it is recommended
|
|
32
|
-
that you use the .to_spreadsheet() method to generate an Excel file, and then manually add the missing
|
|
33
|
-
information to the Excel file. The Excel file can then be converted to a `Rules` object.
|
|
34
|
-
|
|
35
|
-
Alternatively, one can set the `make_compliant` parameter to True to allow neat to attempt to make
|
|
36
|
-
the imported rules compliant by adding default values for missing information, attaching dangling
|
|
37
|
-
properties to default containers based on the property type, etc. One has to be aware
|
|
38
|
-
that NEAT will be opinionated about how to make the ontology compliant, and that the resulting
|
|
39
|
-
rules may not be what you expect.
|
|
40
|
-
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
def __init__(self, owl_filepath: Path):
|
|
44
|
-
self.owl_filepath = owl_filepath
|
|
45
|
-
|
|
46
|
-
def to_tables(self, make_compliant: bool = False) -> dict[str, pd.DataFrame]:
|
|
47
|
-
graph = Graph()
|
|
48
|
-
try:
|
|
49
|
-
graph.parse(self.owl_filepath)
|
|
50
|
-
except Exception as e:
|
|
51
|
-
raise Exception(f"Could not parse owl file: {e}") from e
|
|
52
|
-
|
|
53
|
-
# bind key namespaces
|
|
54
|
-
graph.bind("owl", OWL)
|
|
55
|
-
graph.bind("rdf", RDF)
|
|
56
|
-
graph.bind("rdfs", RDFS)
|
|
57
|
-
graph.bind("dcterms", DCTERMS)
|
|
58
|
-
graph.bind("dc", DC)
|
|
59
|
-
graph.bind("skos", SKOS)
|
|
60
|
-
|
|
61
|
-
tables: dict[str, pd.DataFrame] = {
|
|
62
|
-
Tables.metadata: parse_owl_metadata(graph, make_compliant=make_compliant),
|
|
63
|
-
Tables.classes: parse_owl_classes(graph, make_compliant=make_compliant),
|
|
64
|
-
Tables.properties: parse_owl_properties(graph, make_compliant=make_compliant),
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if make_compliant:
|
|
68
|
-
tables = make_tables_compliant(tables)
|
|
69
|
-
# add sorting of classes and properties prior exporting
|
|
70
|
-
|
|
71
|
-
tables[Tables.classes] = tables[Tables.classes].sort_values(by=["Class"])
|
|
72
|
-
tables[Tables.properties] = tables[Tables.properties].sort_values(by=["Class", "Property"])
|
|
73
|
-
|
|
74
|
-
return tables
|
|
75
|
-
|
|
76
|
-
def to_raw_rules(self, make_compliant: bool = False) -> RawRules:
|
|
77
|
-
"""Creates `RawRules` object from the data."""
|
|
78
|
-
|
|
79
|
-
tables = self.to_tables(make_compliant=make_compliant)
|
|
80
|
-
|
|
81
|
-
return RawRules.from_tables(tables=tables, importer_type=self.__class__.__name__)
|
|
82
|
-
|
|
83
|
-
def to_rules(
|
|
84
|
-
self,
|
|
85
|
-
return_report: bool = False,
|
|
86
|
-
skip_validation: bool = False,
|
|
87
|
-
validators_to_skip: set[str] | None = None,
|
|
88
|
-
make_compliant: bool = False,
|
|
89
|
-
) -> tuple[Rules | None, list[ErrorDetails] | None, list | None] | Rules:
|
|
90
|
-
"""
|
|
91
|
-
Creates `Rules` object from the data.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
return_report: To return validation report. Defaults to False.
|
|
95
|
-
skip_validation: Bypasses Rules validation. Defaults to False.
|
|
96
|
-
validators_to_skip: List of validators to skip. Defaults to None.
|
|
97
|
-
make_compliant: Flag for generating compliant rules, by default False
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
Instance of `Rules`, which can be validated, not validated based on
|
|
101
|
-
`skip_validation` flag, or partially validated if `validators_to_skip` is set,
|
|
102
|
-
and optional list of errors and warnings if
|
|
103
|
-
`return_report` is set to True.
|
|
104
|
-
|
|
105
|
-
!!! Note "Skip Validation
|
|
106
|
-
`skip_validation` flag should be only used for purpose when `Rules` object
|
|
107
|
-
is exported to an Excel file. Do not use this flag for any other purpose!
|
|
108
|
-
"""
|
|
109
|
-
|
|
110
|
-
raw_rules = self.to_raw_rules(make_compliant=make_compliant)
|
|
111
|
-
|
|
112
|
-
return raw_rules.to_rules(return_report, skip_validation, validators_to_skip)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def make_tables_compliant(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
|
|
116
|
-
tables = _add_missing_classes(tables)
|
|
117
|
-
tables = _add_missing_value_types(tables)
|
|
118
|
-
tables = _add_properties_to_dangling_classes(tables)
|
|
119
|
-
tables = _add_entity_type_property(tables)
|
|
120
|
-
|
|
121
|
-
return tables
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def _add_missing_classes(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
|
|
125
|
-
"""Add missing classes to containers.
|
|
126
|
-
|
|
127
|
-
Args:
|
|
128
|
-
tables: imported tables from owl ontology
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
Updated tables with missing classes added to containers
|
|
132
|
-
"""
|
|
133
|
-
|
|
134
|
-
missing_classes = set(tables[Tables.properties].Class.to_list()) - set(tables[Tables.classes].Class.to_list())
|
|
135
|
-
|
|
136
|
-
rows = []
|
|
137
|
-
for class_ in missing_classes:
|
|
138
|
-
rows += [
|
|
139
|
-
{
|
|
140
|
-
"Class": class_,
|
|
141
|
-
"Name": None,
|
|
142
|
-
"Description": None,
|
|
143
|
-
"Parent Class": None,
|
|
144
|
-
"Deprecated": False,
|
|
145
|
-
"Deprecation Date": None,
|
|
146
|
-
"Replaced By": None,
|
|
147
|
-
"Source": None,
|
|
148
|
-
"Source Entity Name": None,
|
|
149
|
-
"Match Type": None,
|
|
150
|
-
"Comment": (
|
|
151
|
-
"Added by NEAT. "
|
|
152
|
-
"This is a class that a domain of a property but was not defined in the ontology. "
|
|
153
|
-
"It is added by NEAT to make the ontology compliant with CDF."
|
|
154
|
-
),
|
|
155
|
-
}
|
|
156
|
-
]
|
|
157
|
-
|
|
158
|
-
if rows:
|
|
159
|
-
tables[Tables.classes] = pd.concat(
|
|
160
|
-
[tables[Tables.classes], pd.DataFrame(rows)],
|
|
161
|
-
ignore_index=True,
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
return tables
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def _add_missing_value_types(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
|
|
168
|
-
"""Add properties to classes that do not have any properties defined to them
|
|
169
|
-
|
|
170
|
-
Args:
|
|
171
|
-
tables: imported tables from owl ontology
|
|
172
|
-
|
|
173
|
-
Returns:
|
|
174
|
-
Updated tables with missing properties added to containers
|
|
175
|
-
"""
|
|
176
|
-
|
|
177
|
-
xsd_types = set(XSD_VALUE_TYPE_MAPPINGS.keys())
|
|
178
|
-
referred_types = set(tables[Tables.properties]["Type"].to_list())
|
|
179
|
-
defined_classes = set(tables[Tables.classes]["Class"].to_list())
|
|
180
|
-
|
|
181
|
-
rows = []
|
|
182
|
-
for class_ in referred_types.difference(defined_classes).difference(xsd_types):
|
|
183
|
-
rows += [
|
|
184
|
-
{
|
|
185
|
-
"Class": class_,
|
|
186
|
-
"Name": None,
|
|
187
|
-
"Description": None,
|
|
188
|
-
"Parent Class": None,
|
|
189
|
-
"Deprecated": False,
|
|
190
|
-
"Deprecation Date": None,
|
|
191
|
-
"Replaced By": None,
|
|
192
|
-
"Source": None,
|
|
193
|
-
"Source Entity Name": None,
|
|
194
|
-
"Match Type": None,
|
|
195
|
-
"Comment": (
|
|
196
|
-
"Added by NEAT. "
|
|
197
|
-
"This is a class that a domain of a property but was not defined in the ontology. "
|
|
198
|
-
"It is added by NEAT to make the ontology compliant with CDF."
|
|
199
|
-
),
|
|
200
|
-
}
|
|
201
|
-
]
|
|
202
|
-
|
|
203
|
-
if rows:
|
|
204
|
-
tables[Tables.classes] = pd.concat(
|
|
205
|
-
[tables[Tables.classes], pd.DataFrame(rows)],
|
|
206
|
-
ignore_index=True,
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
return tables
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
def _add_properties_to_dangling_classes(
|
|
213
|
-
tables: dict[str, pd.DataFrame], properties_to_add: list[str] | None = None
|
|
214
|
-
) -> dict[str, pd.DataFrame]:
|
|
215
|
-
"""Add properties to classes that do not have any properties defined to them
|
|
216
|
-
|
|
217
|
-
Args:
|
|
218
|
-
tables: imported tables from owl ontology
|
|
219
|
-
|
|
220
|
-
Returns:
|
|
221
|
-
Updated tables with missing properties added to containers
|
|
222
|
-
"""
|
|
223
|
-
|
|
224
|
-
if properties_to_add is None:
|
|
225
|
-
properties_to_add = ["label"]
|
|
226
|
-
undefined_classes = set(tables[Tables.classes].Class.to_list()) - set(tables[Tables.properties].Class.to_list())
|
|
227
|
-
|
|
228
|
-
rows = []
|
|
229
|
-
for class_ in undefined_classes:
|
|
230
|
-
for property_ in properties_to_add:
|
|
231
|
-
rows += [
|
|
232
|
-
{
|
|
233
|
-
"Class": class_,
|
|
234
|
-
"Property": property_,
|
|
235
|
-
"Name": property_,
|
|
236
|
-
"Description": None,
|
|
237
|
-
"Type": "string",
|
|
238
|
-
"Min Count": None,
|
|
239
|
-
"Max Count": 1,
|
|
240
|
-
"Deprecated": False,
|
|
241
|
-
"Deprecation Date": None,
|
|
242
|
-
"Replaced By": None,
|
|
243
|
-
"Source": None,
|
|
244
|
-
"Source Entity Name": None,
|
|
245
|
-
"Match Type": None,
|
|
246
|
-
"Comment": "Added by NEAT. Default property to make the ontology compliant with CDF.",
|
|
247
|
-
}
|
|
248
|
-
]
|
|
249
|
-
|
|
250
|
-
if rows:
|
|
251
|
-
tables[Tables.properties] = pd.concat(
|
|
252
|
-
[tables[Tables.properties], pd.DataFrame(rows)],
|
|
253
|
-
ignore_index=True,
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
return tables
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
def _add_entity_type_property(tables: dict[str, pd.DataFrame]) -> dict[str, pd.DataFrame]:
|
|
260
|
-
missing_entity_type = set(
|
|
261
|
-
tables[Tables.properties].groupby("Class").filter(lambda x: "entityType" not in x.Property.to_list()).Class
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
rows = []
|
|
265
|
-
for class_ in missing_entity_type:
|
|
266
|
-
rows += [
|
|
267
|
-
{
|
|
268
|
-
"Class": class_,
|
|
269
|
-
"Property": "entityType",
|
|
270
|
-
"Name": "entityType",
|
|
271
|
-
"Description": None,
|
|
272
|
-
"Type": "string",
|
|
273
|
-
"Min Count": None,
|
|
274
|
-
"Max Count": 1,
|
|
275
|
-
"Deprecated": False,
|
|
276
|
-
"Deprecation Date": None,
|
|
277
|
-
"Replaced By": None,
|
|
278
|
-
"Source": None,
|
|
279
|
-
"Source Entity Name": None,
|
|
280
|
-
"Match Type": None,
|
|
281
|
-
"Comment": "Added by NEAT. Default property added to make the ontology compliant with CDF.",
|
|
282
|
-
}
|
|
283
|
-
]
|
|
284
|
-
|
|
285
|
-
if rows:
|
|
286
|
-
tables[Tables.properties] = pd.concat(
|
|
287
|
-
[tables[Tables.properties], pd.DataFrame(rows)],
|
|
288
|
-
ignore_index=True,
|
|
289
|
-
)
|
|
290
|
-
return tables
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"""This module performs importing of graph to TransformationRules pydantic class.
|
|
2
|
-
In more details, it traverses the graph and abstracts class and properties, basically
|
|
3
|
-
generating a list of rules based on which nodes that form the graph are made.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
|
|
8
|
-
import pandas as pd
|
|
9
|
-
from openpyxl import Workbook, load_workbook
|
|
10
|
-
|
|
11
|
-
from cognite.neat.utils.auxiliary import local_import
|
|
12
|
-
|
|
13
|
-
from ._base import BaseImporter
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ExcelImporter(BaseImporter):
|
|
17
|
-
def __init__(self, filepath: Path):
|
|
18
|
-
self.filepath = filepath
|
|
19
|
-
|
|
20
|
-
def to_tables(self) -> dict[str, pd.DataFrame]:
|
|
21
|
-
workbook: Workbook = load_workbook(self.filepath)
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
sheet_name: pd.read_excel(
|
|
25
|
-
self.filepath,
|
|
26
|
-
sheet_name=sheet_name,
|
|
27
|
-
header=None if sheet_name == "Metadata" else 0,
|
|
28
|
-
skiprows=1 if sheet_name in ["Classes", "Properties", "Instances"] else None,
|
|
29
|
-
)
|
|
30
|
-
for sheet_name in workbook.sheetnames
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class GoogleSheetImporter(BaseImporter):
|
|
35
|
-
def __init__(self, sheet_id: str):
|
|
36
|
-
self.sheet_id = sheet_id
|
|
37
|
-
|
|
38
|
-
def to_tables(self) -> dict[str, pd.DataFrame]:
|
|
39
|
-
local_import("gspread", "google")
|
|
40
|
-
import gspread # type: ignore[import]
|
|
41
|
-
|
|
42
|
-
client_google = gspread.service_account()
|
|
43
|
-
google_sheet = client_google.open_by_key(self.sheet_id)
|
|
44
|
-
|
|
45
|
-
return {worksheet.title: pd.DataFrame(worksheet.get_all_records()) for worksheet in google_sheet.worksheets()}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
import pandas as pd
|
|
4
|
-
|
|
5
|
-
from ._base import BaseImporter
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class XSDImporter(BaseImporter):
|
|
9
|
-
"""
|
|
10
|
-
Importer for XSD (XML Schema) files.
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
xml_directory: Path to directory containing XSD files.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
def __init__(self, xsd_directory: Path):
|
|
17
|
-
self.xsd_directory = xsd_directory
|
|
18
|
-
|
|
19
|
-
def to_tables(self) -> dict[str, pd.DataFrame]:
|
|
20
|
-
raise NotImplementedError
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Literal
|
|
3
|
-
|
|
4
|
-
import yaml
|
|
5
|
-
|
|
6
|
-
from ._dict2rules import ArbitraryDictImporter
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ArbitraryYAMLImporter(ArbitraryDictImporter):
|
|
10
|
-
"""
|
|
11
|
-
Importer for data given in a YAML file or string.
|
|
12
|
-
|
|
13
|
-
This importer infers the data model from the YAML string based on the shape of the data.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
yaml_path_or_str: Path to file with YAML.
|
|
17
|
-
relationship_direction: Direction of relationships, either "parent-to-child" or "child-to-parent". JSON
|
|
18
|
-
files are nested with children nested inside parents. This option determines whether the resulting rules
|
|
19
|
-
will have an edge from parents to children or from children to parents.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
def __init__(
|
|
23
|
-
self,
|
|
24
|
-
yaml_path_or_str: Path,
|
|
25
|
-
relationship_direction: Literal["parent-to-child", "child-to-parent"] = "parent-to-child",
|
|
26
|
-
):
|
|
27
|
-
if isinstance(yaml_path_or_str, str):
|
|
28
|
-
data = yaml.safe_load(yaml_path_or_str)
|
|
29
|
-
super().__init__(data, relationship_direction)
|
|
30
|
-
elif isinstance(yaml_path_or_str, Path):
|
|
31
|
-
if not yaml_path_or_str.exists():
|
|
32
|
-
raise ValueError(f"File {yaml_path_or_str} does not exist")
|
|
33
|
-
if yaml_path_or_str.suffix != ".json":
|
|
34
|
-
raise ValueError(f"File {yaml_path_or_str} is not a JSON file")
|
|
35
|
-
self.json_path = yaml_path_or_str
|
|
36
|
-
data = yaml.safe_load(yaml_path_or_str.read_text())
|
|
37
|
-
super().__init__(data, relationship_direction)
|
|
38
|
-
else:
|
|
39
|
-
raise TypeError(f"Expected Path or str, got {type(yaml_path_or_str)}")
|