cognite-neat 0.88.0__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/routers/configuration.py +1 -1
- 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 +19 -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/_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-0.88.0.dist-info → cognite_neat-0.88.1.dist-info}/METADATA +2 -6
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.1.dist-info}/RECORD +43 -45
- 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/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/Ontology_to_Data_Model/workflow.yaml +0 -116
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.1.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.1.dist-info}/entry_points.txt +0 -0
|
@@ -160,7 +160,7 @@ def triples2dictionary(triples: Iterable[tuple[URIRef, URIRef, str | URIRef]]) -
|
|
|
160
160
|
value: str
|
|
161
161
|
uri: URIRef
|
|
162
162
|
|
|
163
|
-
id_, property_, value = remove_namespace_from_uri(
|
|
163
|
+
id_, property_, value = remove_namespace_from_uri(triple) # type: ignore[misc]
|
|
164
164
|
uri = triple[0]
|
|
165
165
|
|
|
166
166
|
if uri not in dictionary:
|
|
@@ -108,7 +108,7 @@ class NeatGraphStore:
|
|
|
108
108
|
|
|
109
109
|
@classmethod
|
|
110
110
|
def from_memory_store(cls, rules: InformationRules | None = None) -> "Self":
|
|
111
|
-
return cls(Graph(), rules)
|
|
111
|
+
return cls(Graph(identifier=DEFAULT_NAMESPACE), rules)
|
|
112
112
|
|
|
113
113
|
@classmethod
|
|
114
114
|
def from_sparql_store(
|
|
@@ -126,17 +126,17 @@ class NeatGraphStore:
|
|
|
126
126
|
postAsEncoded=False,
|
|
127
127
|
autocommit=False,
|
|
128
128
|
)
|
|
129
|
-
graph = Graph(store=store)
|
|
129
|
+
graph = Graph(store=store, identifier=DEFAULT_NAMESPACE)
|
|
130
130
|
return cls(graph, rules)
|
|
131
131
|
|
|
132
132
|
@classmethod
|
|
133
133
|
def from_oxi_store(cls, storage_dir: Path | None = None, rules: InformationRules | None = None) -> "Self":
|
|
134
134
|
"""Creates a NeatGraphStore from an Oxigraph store."""
|
|
135
135
|
local_import("pyoxigraph", "oxi")
|
|
136
|
+
local_import("oxrdflib", "oxi")
|
|
137
|
+
import oxrdflib
|
|
136
138
|
import pyoxigraph
|
|
137
139
|
|
|
138
|
-
from cognite.neat.graph.stores._oxrdflib import OxigraphStore
|
|
139
|
-
|
|
140
140
|
# Adding support for both oxigraph in-memory and file-based storage
|
|
141
141
|
for i in range(4):
|
|
142
142
|
try:
|
|
@@ -149,8 +149,10 @@ class NeatGraphStore:
|
|
|
149
149
|
else:
|
|
150
150
|
raise Exception("Error initializing Oxigraph store")
|
|
151
151
|
|
|
152
|
-
graph = Graph(
|
|
153
|
-
|
|
152
|
+
graph = Graph(
|
|
153
|
+
store=oxrdflib.OxigraphStore(store=oxi_store),
|
|
154
|
+
identifier=DEFAULT_NAMESPACE,
|
|
155
|
+
)
|
|
154
156
|
|
|
155
157
|
return cls(graph, rules)
|
|
156
158
|
|
|
@@ -208,7 +210,8 @@ class NeatGraphStore:
|
|
|
208
210
|
property_renaming_config = InformationAnalysis(self.rules).define_property_renaming_config(class_entity)
|
|
209
211
|
|
|
210
212
|
for instance_id in instance_ids:
|
|
211
|
-
|
|
213
|
+
if res := self.queries.describe(instance_id, property_renaming_config):
|
|
214
|
+
yield res
|
|
212
215
|
|
|
213
216
|
def _parse_file(
|
|
214
217
|
self,
|
|
@@ -221,7 +224,7 @@ class NeatGraphStore:
|
|
|
221
224
|
Args:
|
|
222
225
|
filepath : File path to file containing graph data, by default None
|
|
223
226
|
mime_type : MIME type of graph data, by default "application/rdf+xml"
|
|
224
|
-
|
|
227
|
+
base_uri : Add base IRI to graph, by default True
|
|
225
228
|
"""
|
|
226
229
|
|
|
227
230
|
# Oxigraph store, do not want to type hint this as it is an optional dependency
|
|
@@ -229,10 +232,15 @@ class NeatGraphStore:
|
|
|
229
232
|
|
|
230
233
|
def parse_to_oxi_store():
|
|
231
234
|
local_import("pyoxigraph", "oxi")
|
|
232
|
-
|
|
235
|
+
import pyoxigraph
|
|
233
236
|
|
|
234
|
-
cast(
|
|
235
|
-
|
|
237
|
+
cast(pyoxigraph.Store, self.graph.store._store).bulk_load(
|
|
238
|
+
str(filepath),
|
|
239
|
+
mime_type,
|
|
240
|
+
base_iri=base_uri,
|
|
241
|
+
to_graph=pyoxigraph.NamedNode(self.graph.identifier),
|
|
242
|
+
)
|
|
243
|
+
cast(pyoxigraph.Store, self.graph.store._store).optimize()
|
|
236
244
|
|
|
237
245
|
parse_to_oxi_store()
|
|
238
246
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from rdflib import
|
|
1
|
+
from rdflib import Graph
|
|
2
2
|
|
|
3
3
|
from cognite.neat.rules.analysis import InformationAnalysis
|
|
4
4
|
from cognite.neat.rules.models._rdfpath import RDFPath, SingleProperty
|
|
@@ -17,6 +17,7 @@ class AddSelfReferenceProperty(BaseTransformer):
|
|
|
17
17
|
description: str = "Adds property that contains id of reference to all references of given class in Rules"
|
|
18
18
|
_use_only_once: bool = True
|
|
19
19
|
_need_changes = frozenset({})
|
|
20
|
+
_ref_template: str = """SELECT ?s WHERE {{?s a <{type_}>}}"""
|
|
20
21
|
|
|
21
22
|
def __init__(
|
|
22
23
|
self,
|
|
@@ -32,7 +33,7 @@ class AddSelfReferenceProperty(BaseTransformer):
|
|
|
32
33
|
|
|
33
34
|
namespace = self.rules.prefixes[prefix] if prefix in self.rules.prefixes else self.rules.metadata.namespace
|
|
34
35
|
|
|
35
|
-
for reference in graph.
|
|
36
|
+
for (reference,) in graph.query(self._ref_template.format(type_=namespace[suffix])): # type: ignore [misc]
|
|
36
37
|
graph.add(
|
|
37
38
|
(
|
|
38
39
|
reference,
|
cognite/neat/issues.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import Any, ClassVar, TypeVar
|
|
|
9
9
|
from warnings import WarningMessage
|
|
10
10
|
|
|
11
11
|
import pandas as pd
|
|
12
|
+
from pydantic_core import PydanticCustomError
|
|
12
13
|
|
|
13
14
|
if sys.version_info < (3, 11):
|
|
14
15
|
from exceptiongroup import ExceptionGroup
|
|
@@ -56,6 +57,13 @@ class NeatError(NeatIssue, ABC):
|
|
|
56
57
|
def as_exception(self) -> ValueError:
|
|
57
58
|
return ValueError(self.message())
|
|
58
59
|
|
|
60
|
+
def as_pydantic_exception(self) -> PydanticCustomError:
|
|
61
|
+
return PydanticCustomError(
|
|
62
|
+
self.__class__.__name__,
|
|
63
|
+
self.message(),
|
|
64
|
+
dict(description=self.description, fix=self.fix),
|
|
65
|
+
)
|
|
66
|
+
|
|
59
67
|
|
|
60
68
|
@dataclass(frozen=True)
|
|
61
69
|
class NeatWarning(NeatIssue, ABC, UserWarning):
|
|
@@ -9,8 +9,19 @@ from rdflib import DCTERMS, OWL, RDF, RDFS, XSD, BNode, Graph, Literal, Namespac
|
|
|
9
9
|
from rdflib.collection import Collection as GraphCollection
|
|
10
10
|
|
|
11
11
|
from cognite.neat.constants import DEFAULT_NAMESPACE as NEAT_NAMESPACE
|
|
12
|
-
from cognite.neat.rules import exceptions
|
|
13
12
|
from cognite.neat.rules.analysis import InformationAnalysis
|
|
13
|
+
from cognite.neat.rules.issues.ontology import (
|
|
14
|
+
MetadataSheetNamespaceNotDefinedError,
|
|
15
|
+
MissingDataModelPrefixOrNamespaceWarning,
|
|
16
|
+
OntologyMultiDefinitionPropertyWarning,
|
|
17
|
+
OntologyMultiDomainPropertyWarning,
|
|
18
|
+
OntologyMultiLabeledPropertyWarning,
|
|
19
|
+
OntologyMultiRangePropertyWarning,
|
|
20
|
+
OntologyMultiTypePropertyWarning,
|
|
21
|
+
PrefixMissingError,
|
|
22
|
+
PropertiesDefinedMultipleTimesError,
|
|
23
|
+
PropertyDefinitionsNotForSamePropertyError,
|
|
24
|
+
)
|
|
14
25
|
from cognite.neat.rules.models import DMSRules
|
|
15
26
|
from cognite.neat.rules.models.data_types import DataType
|
|
16
27
|
from cognite.neat.rules.models.entities import ClassEntity, EntityTypes
|
|
@@ -102,13 +113,15 @@ class Ontology(OntologyModel):
|
|
|
102
113
|
|
|
103
114
|
properties_redefined, redefinition_warnings = are_properties_redefined(rules, return_report=True)
|
|
104
115
|
if properties_redefined:
|
|
105
|
-
raise
|
|
116
|
+
raise PropertiesDefinedMultipleTimesError(
|
|
117
|
+
report=generate_exception_report(redefinition_warnings)
|
|
118
|
+
).as_exception()
|
|
106
119
|
|
|
107
120
|
if rules.prefixes is None:
|
|
108
|
-
raise
|
|
121
|
+
raise PrefixMissingError().as_exception()
|
|
109
122
|
|
|
110
123
|
if rules.metadata.namespace is None:
|
|
111
|
-
raise
|
|
124
|
+
raise MissingDataModelPrefixOrNamespaceWarning()
|
|
112
125
|
|
|
113
126
|
class_dict = InformationAnalysis(rules).as_class_dict()
|
|
114
127
|
return cls(
|
|
@@ -172,7 +185,7 @@ class Ontology(OntologyModel):
|
|
|
172
185
|
owl.bind(prefix, namespace)
|
|
173
186
|
|
|
174
187
|
if self.metadata.namespace is None:
|
|
175
|
-
raise
|
|
188
|
+
raise MetadataSheetNamespaceNotDefinedError().as_exception()
|
|
176
189
|
|
|
177
190
|
owl.add((URIRef(self.metadata.namespace), RDF.type, OWL.Ontology))
|
|
178
191
|
for property_ in self.properties:
|
|
@@ -221,7 +234,7 @@ class OWLMetadata(InformationMetadata):
|
|
|
221
234
|
def triples(self) -> list[tuple]:
|
|
222
235
|
# Mandatory triples originating from Metadata mandatory fields
|
|
223
236
|
if self.namespace is None:
|
|
224
|
-
raise
|
|
237
|
+
raise MetadataSheetNamespaceNotDefinedError().as_exception()
|
|
225
238
|
triples: list[tuple] = [
|
|
226
239
|
(URIRef(self.namespace), DCTERMS.hasVersion, Literal(self.version)),
|
|
227
240
|
(URIRef(self.namespace), OWL.versionInfo, Literal(self.version)),
|
|
@@ -319,7 +332,7 @@ class OWLProperty(OntologyModel):
|
|
|
319
332
|
"""Here list of properties is a list of properties with the same id, but different definitions."""
|
|
320
333
|
|
|
321
334
|
if not cls.same_property_id(definitions):
|
|
322
|
-
raise
|
|
335
|
+
raise PropertyDefinitionsNotForSamePropertyError().as_exception()
|
|
323
336
|
|
|
324
337
|
owl_property = cls.model_construct(
|
|
325
338
|
id_=namespace[definitions[0].property_],
|
|
@@ -350,10 +363,9 @@ class OWLProperty(OntologyModel):
|
|
|
350
363
|
def is_multi_type(cls, v, info: ValidationInfo):
|
|
351
364
|
if len(v) > 1:
|
|
352
365
|
warnings.warn(
|
|
353
|
-
|
|
366
|
+
OntologyMultiTypePropertyWarning(
|
|
354
367
|
remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v]
|
|
355
|
-
)
|
|
356
|
-
category=exceptions.OntologyMultiTypeProperty,
|
|
368
|
+
),
|
|
357
369
|
stacklevel=2,
|
|
358
370
|
)
|
|
359
371
|
return v
|
|
@@ -362,10 +374,9 @@ class OWLProperty(OntologyModel):
|
|
|
362
374
|
def is_multi_range(cls, v, info: ValidationInfo):
|
|
363
375
|
if len(v) > 1:
|
|
364
376
|
warnings.warn(
|
|
365
|
-
|
|
377
|
+
OntologyMultiRangePropertyWarning(
|
|
366
378
|
remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v]
|
|
367
|
-
)
|
|
368
|
-
category=exceptions.OntologyMultiRangeProperty,
|
|
379
|
+
),
|
|
369
380
|
stacklevel=2,
|
|
370
381
|
)
|
|
371
382
|
return v
|
|
@@ -374,10 +385,9 @@ class OWLProperty(OntologyModel):
|
|
|
374
385
|
def is_multi_domain(cls, v, info: ValidationInfo):
|
|
375
386
|
if len(v) > 1:
|
|
376
387
|
warnings.warn(
|
|
377
|
-
|
|
388
|
+
OntologyMultiDomainPropertyWarning(
|
|
378
389
|
remove_namespace_from_uri(info.data["id_"]), [remove_namespace_from_uri(t) for t in v]
|
|
379
|
-
)
|
|
380
|
-
category=exceptions.OntologyMultiDomainProperty,
|
|
390
|
+
),
|
|
381
391
|
stacklevel=2,
|
|
382
392
|
)
|
|
383
393
|
return v
|
|
@@ -386,8 +396,7 @@ class OWLProperty(OntologyModel):
|
|
|
386
396
|
def has_multi_name(cls, v, info: ValidationInfo):
|
|
387
397
|
if len(v) > 1:
|
|
388
398
|
warnings.warn(
|
|
389
|
-
|
|
390
|
-
category=exceptions.OntologyMultiLabeledProperty,
|
|
399
|
+
OntologyMultiLabeledPropertyWarning(remove_namespace_from_uri(info.data["id_"]), v),
|
|
391
400
|
stacklevel=2,
|
|
392
401
|
)
|
|
393
402
|
return v
|
|
@@ -396,8 +405,7 @@ class OWLProperty(OntologyModel):
|
|
|
396
405
|
def has_multi_comment(cls, v, info: ValidationInfo):
|
|
397
406
|
if len(v) > 1:
|
|
398
407
|
warnings.warn(
|
|
399
|
-
|
|
400
|
-
category=exceptions.OntologyMultiDefinitionProperty,
|
|
408
|
+
OntologyMultiDefinitionPropertyWarning(remove_namespace_from_uri(info.data["id_"])),
|
|
401
409
|
stacklevel=2,
|
|
402
410
|
)
|
|
403
411
|
return v
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import warnings
|
|
3
2
|
from typing import Literal, overload
|
|
4
3
|
|
|
5
4
|
from cognite.neat.exceptions import wrangle_warnings
|
|
6
|
-
from cognite.neat.rules import
|
|
5
|
+
from cognite.neat.rules.issues.dms import EntityIDNotDMSCompliantWarning
|
|
6
|
+
from cognite.neat.rules.issues.importing import PropertyRedefinedWarning
|
|
7
7
|
from cognite.neat.rules.models import InformationRules
|
|
8
|
-
from cognite.neat.
|
|
8
|
+
from cognite.neat.utils.regex_patterns import DMS_PROPERTY_ID_COMPLIANCE_REGEX, PATTERNS, VIEW_ID_COMPLIANCE_REGEX
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@overload
|
|
@@ -26,35 +26,30 @@ def are_entity_names_dms_compliant(
|
|
|
26
26
|
flag: bool = True
|
|
27
27
|
with warnings.catch_warnings(record=True) as validation_warnings:
|
|
28
28
|
for class_ in rules.classes:
|
|
29
|
-
if not
|
|
29
|
+
if not PATTERNS.view_id_compliance.match(class_.class_.suffix):
|
|
30
30
|
warnings.warn(
|
|
31
|
-
|
|
32
|
-
"Class", class_.class_.versioned_id, f"[Classes/Class/{class_.class_.versioned_id}]"
|
|
33
|
-
).message,
|
|
34
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
31
|
+
EntityIDNotDMSCompliantWarning(class_.class_.versioned_id, "Class", VIEW_ID_COMPLIANCE_REGEX),
|
|
35
32
|
stacklevel=2,
|
|
36
33
|
)
|
|
37
34
|
flag = False
|
|
38
35
|
|
|
39
|
-
for
|
|
36
|
+
for _, property_ in enumerate(rules.properties):
|
|
40
37
|
# check class id which would resolve as view/container id
|
|
41
|
-
if not
|
|
38
|
+
if not PATTERNS.view_id_compliance.match(property_.class_.suffix):
|
|
42
39
|
warnings.warn(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
EntityIDNotDMSCompliantWarning(
|
|
41
|
+
property_.class_.versioned_id,
|
|
42
|
+
"Class",
|
|
43
|
+
VIEW_ID_COMPLIANCE_REGEX,
|
|
44
|
+
),
|
|
47
45
|
stacklevel=2,
|
|
48
46
|
)
|
|
49
47
|
flag = False
|
|
50
48
|
|
|
51
49
|
# check property id which would resolve as view/container id
|
|
52
|
-
if not
|
|
50
|
+
if not PATTERNS.dms_property_id_compliance.match(property_.property_):
|
|
53
51
|
warnings.warn(
|
|
54
|
-
|
|
55
|
-
"Property", property_.property_, f"[Properties/Property/{row}]"
|
|
56
|
-
).message,
|
|
57
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
52
|
+
EntityIDNotDMSCompliantWarning(property_.property_, "Property", DMS_PROPERTY_ID_COMPLIANCE_REGEX),
|
|
58
53
|
stacklevel=2,
|
|
59
54
|
)
|
|
60
55
|
flag = False
|
|
@@ -83,8 +78,7 @@ def are_properties_redefined(rules: InformationRules, return_report: bool = Fals
|
|
|
83
78
|
elif property_.class_ in analyzed_properties[property_.property_]:
|
|
84
79
|
flag = True
|
|
85
80
|
warnings.warn(
|
|
86
|
-
|
|
87
|
-
category=exceptions.EntityIDNotDMSCompliant,
|
|
81
|
+
PropertyRedefinedWarning(property_.property_, property_.class_.versioned_id),
|
|
88
82
|
stacklevel=2,
|
|
89
83
|
)
|
|
90
84
|
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import datetime
|
|
2
|
-
import re
|
|
3
2
|
|
|
4
3
|
from rdflib import Graph, Namespace
|
|
5
4
|
|
|
6
5
|
from cognite.neat.constants import DEFAULT_NAMESPACE
|
|
7
6
|
from cognite.neat.rules.models import RoleTypes, SchemaCompleteness
|
|
8
|
-
from cognite.neat.rules.models._types._base import (
|
|
9
|
-
PREFIX_COMPLIANCE_REGEX,
|
|
10
|
-
VERSION_COMPLIANCE_REGEX,
|
|
11
|
-
)
|
|
12
7
|
from cognite.neat.utils.collection_ import remove_none_elements_from_set
|
|
13
8
|
from cognite.neat.utils.rdf_ import convert_rdflib_content
|
|
9
|
+
from cognite.neat.utils.regex_patterns import PATTERNS
|
|
14
10
|
|
|
15
11
|
|
|
16
12
|
def parse_owl_metadata(graph: Graph) -> dict:
|
|
@@ -144,7 +140,7 @@ def fix_description(metadata: dict, default: str = "This model has been inferred
|
|
|
144
140
|
|
|
145
141
|
def fix_prefix(metadata: dict, default: str = "neat") -> dict:
|
|
146
142
|
if prefix := metadata.get("prefix", None):
|
|
147
|
-
if not isinstance(prefix, str) or not
|
|
143
|
+
if not isinstance(prefix, str) or not PATTERNS.prefix_compliance.match(prefix):
|
|
148
144
|
metadata["prefix"] = default
|
|
149
145
|
else:
|
|
150
146
|
metadata["prefix"] = default
|
|
@@ -189,7 +185,7 @@ def fix_date(
|
|
|
189
185
|
|
|
190
186
|
def fix_version(metadata: dict, default: str = "1.0.0") -> dict:
|
|
191
187
|
if version := metadata.get("version", None):
|
|
192
|
-
if not
|
|
188
|
+
if not PATTERNS.version_compliance.match(version):
|
|
193
189
|
metadata["version"] = default
|
|
194
190
|
else:
|
|
195
191
|
metadata["version"] = default
|
|
@@ -131,25 +131,24 @@ class SpreadsheetReader:
|
|
|
131
131
|
names = MANDATORY_SHEETS_BY_ROLE[role]
|
|
132
132
|
return {f"{self._sheet_prefix}{sheet_name}" for sheet_name in names if sheet_name != "Metadata"}
|
|
133
133
|
|
|
134
|
-
def read(self, filepath: Path) -> None | ReadResult:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# The reading of metadata failed, so we can't continue
|
|
145
|
-
return None
|
|
146
|
-
|
|
147
|
-
sheets, read_info_by_sheet = self._read_sheets(excel_file, metadata.role)
|
|
148
|
-
if sheets is None or self.issue_list.has_errors:
|
|
134
|
+
def read(self, excel_file: pd.ExcelFile, filepath: Path) -> None | ReadResult:
|
|
135
|
+
self._seen_files.add(filepath)
|
|
136
|
+
self._seen_sheets.update(map(str, excel_file.sheet_names))
|
|
137
|
+
metadata: MetadataRaw | None
|
|
138
|
+
if self.metadata is not None:
|
|
139
|
+
metadata = self.metadata
|
|
140
|
+
else:
|
|
141
|
+
metadata = self._read_metadata(excel_file, filepath)
|
|
142
|
+
if metadata is None:
|
|
143
|
+
# The reading of metadata failed, so we can't continue
|
|
149
144
|
return None
|
|
150
|
-
sheets["Metadata"] = dict(metadata)
|
|
151
145
|
|
|
152
|
-
|
|
146
|
+
sheets, read_info_by_sheet = self._read_sheets(excel_file, metadata.role)
|
|
147
|
+
if sheets is None or self.issue_list.has_errors:
|
|
148
|
+
return None
|
|
149
|
+
sheets["Metadata"] = dict(metadata)
|
|
150
|
+
|
|
151
|
+
return ReadResult(sheets, read_info_by_sheet, metadata)
|
|
153
152
|
|
|
154
153
|
def _read_metadata(self, excel_file: ExcelFile, filepath: Path) -> MetadataRaw | None:
|
|
155
154
|
if self.metadata_sheet_name not in excel_file.sheet_names:
|
|
@@ -232,17 +231,21 @@ class ExcelImporter(BaseImporter):
|
|
|
232
231
|
issue_list.append(issues.spreadsheet_file.SpreadsheetNotFoundError(self.filepath))
|
|
233
232
|
return self._return_or_raise(issue_list, errors)
|
|
234
233
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if user_read is None or issue_list.has_errors:
|
|
238
|
-
return self._return_or_raise(issue_list, errors)
|
|
234
|
+
with pd.ExcelFile(self.filepath) as excel_file:
|
|
235
|
+
user_reader = SpreadsheetReader(issue_list)
|
|
239
236
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
user_read = user_reader.read(excel_file, self.filepath)
|
|
238
|
+
if user_read is None or issue_list.has_errors:
|
|
239
|
+
return self._return_or_raise(issue_list, errors)
|
|
240
|
+
|
|
241
|
+
last_read: ReadResult | None = None
|
|
242
|
+
if any(sheet_name.startswith("Last") for sheet_name in user_reader.seen_sheets):
|
|
243
|
+
last_read = SpreadsheetReader(issue_list, required=False, sheet_prefix="Last").read(
|
|
244
|
+
excel_file, self.filepath
|
|
245
|
+
)
|
|
246
|
+
reference_read: ReadResult | None = None
|
|
247
|
+
if any(sheet_name.startswith("Ref") for sheet_name in user_reader.seen_sheets):
|
|
248
|
+
reference_read = SpreadsheetReader(issue_list, sheet_prefix="Ref").read(excel_file, self.filepath)
|
|
246
249
|
|
|
247
250
|
if issue_list.has_errors:
|
|
248
251
|
return self._return_or_raise(issue_list, errors)
|
cognite/neat/rules/issues/dms.py
CHANGED
|
@@ -432,6 +432,26 @@ class ChangingViewError(DMSSchemaError):
|
|
|
432
432
|
return output
|
|
433
433
|
|
|
434
434
|
|
|
435
|
+
@dataclass(frozen=True)
|
|
436
|
+
class EntityIDNotDMSCompliantWarning(DMSSchemaWarning):
|
|
437
|
+
description = "The entity ID, {entity_id} of type {entity_type}, is not DMS compliant. Violating regex {regex}"
|
|
438
|
+
fix = "Change the entity ID to be DMS compliant"
|
|
439
|
+
error_name: ClassVar[str] = "EntityIDNotDMSCompliantWarning"
|
|
440
|
+
entity_id: str
|
|
441
|
+
entity_type: str
|
|
442
|
+
regex: str
|
|
443
|
+
|
|
444
|
+
def message(self) -> str:
|
|
445
|
+
return self.description.format(entity_id=self.entity_id, entity_type=self.entity_type, regex=self.regex)
|
|
446
|
+
|
|
447
|
+
def dump(self) -> dict[str, Any]:
|
|
448
|
+
output = super().dump()
|
|
449
|
+
output["entity_id"] = self.entity_id
|
|
450
|
+
output["dms_type"] = self.entity_type
|
|
451
|
+
output["regex"] = self.regex
|
|
452
|
+
return output
|
|
453
|
+
|
|
454
|
+
|
|
435
455
|
@dataclass(frozen=True)
|
|
436
456
|
class EmptyContainerWarning(DMSSchemaWarning):
|
|
437
457
|
description = "The container is empty"
|
|
@@ -126,6 +126,21 @@ class UnknownPropertyWarning(ValidationWarning):
|
|
|
126
126
|
return f"{prefix} This will be ignored in the imports."
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
@dataclass(frozen=True)
|
|
130
|
+
class PropertyRedefinedWarning(ValidationWarning):
|
|
131
|
+
description = "Property, {property}, redefined in {class_}. This will be ignored in the imports."
|
|
132
|
+
fix = "Check if the property is defined only once."
|
|
133
|
+
|
|
134
|
+
property_id: str
|
|
135
|
+
class_id: str
|
|
136
|
+
|
|
137
|
+
def dump(self) -> dict[str, str]:
|
|
138
|
+
return {"property_id": self.property_id, "class_id": self.class_id}
|
|
139
|
+
|
|
140
|
+
def message(self) -> str:
|
|
141
|
+
return self.description.format(property=self.property_id, class_=self.class_id)
|
|
142
|
+
|
|
143
|
+
|
|
129
144
|
@dataclass(frozen=True)
|
|
130
145
|
class UnknownValueTypeWarning(ModelImportWarning):
|
|
131
146
|
description = "Unknown value type. This limits validation done by NEAT. "
|