cognite-neat 0.88.0__py3-none-any.whl → 0.88.2__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 +8 -2
- 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/loaders/_base.py +4 -4
- cognite/neat/graph/loaders/_rdf2asset.py +12 -14
- cognite/neat/graph/loaders/_rdf2dms.py +14 -10
- 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/__init__.py +16 -0
- cognite/neat/{issues.py → issues/_base.py} +78 -2
- cognite/neat/issues/errors/external.py +21 -0
- cognite/neat/issues/errors/properties.py +75 -0
- cognite/neat/issues/errors/resources.py +123 -0
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/{rules/issues → issues}/formatters.py +9 -9
- cognite/neat/issues/neat_warnings/__init__.py +2 -0
- cognite/neat/issues/neat_warnings/identifier.py +27 -0
- cognite/neat/issues/neat_warnings/models.py +22 -0
- cognite/neat/issues/neat_warnings/properties.py +77 -0
- cognite/neat/issues/neat_warnings/resources.py +125 -0
- cognite/neat/rules/exporters/_rules2dms.py +3 -2
- cognite/neat/rules/exporters/_rules2ontology.py +28 -20
- cognite/neat/rules/exporters/_validation.py +15 -21
- cognite/neat/rules/importers/__init__.py +7 -3
- cognite/neat/rules/importers/_base.py +3 -3
- cognite/neat/rules/importers/_dms2rules.py +39 -18
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +44 -53
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +6 -5
- cognite/neat/rules/importers/_rdf/__init__.py +0 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/__init__.py +3 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +82 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +34 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +123 -0
- cognite/neat/rules/importers/{_owl2rules/_owl2rules.py → _rdf/_imf2rules/_imf2rules.py} +15 -11
- cognite/neat/rules/importers/{_inference2rules.py → _rdf/_inference2rules.py} +1 -1
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +57 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2metadata.py +68 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +59 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +76 -0
- cognite/neat/rules/importers/_rdf/_shared.py +586 -0
- cognite/neat/rules/importers/_spreadsheet2rules.py +31 -28
- cognite/neat/rules/importers/_yaml2rules.py +2 -1
- cognite/neat/rules/issues/__init__.py +1 -5
- cognite/neat/rules/issues/base.py +2 -21
- cognite/neat/rules/issues/dms.py +20 -134
- cognite/neat/rules/issues/ontology.py +298 -0
- cognite/neat/rules/issues/spreadsheet.py +51 -3
- cognite/neat/rules/issues/tables.py +72 -0
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/_types/_field.py +14 -21
- cognite/neat/rules/models/asset/_validation.py +1 -1
- cognite/neat/rules/models/dms/_schema.py +53 -30
- cognite/neat/rules/models/dms/_validation.py +2 -2
- cognite/neat/rules/models/entities.py +3 -0
- cognite/neat/rules/models/information/_rules.py +5 -4
- cognite/neat/rules/models/information/_validation.py +1 -1
- cognite/neat/utils/rdf_.py +17 -9
- cognite/neat/utils/regex_patterns.py +52 -0
- cognite/neat/workflows/steps/lib/current/rules_importer.py +73 -1
- cognite/neat/workflows/steps/lib/current/rules_validator.py +19 -7
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/METADATA +2 -6
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/RECORD +85 -72
- 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/issues/loader.py +0 -104
- cognite/neat/graph/stores/_oxrdflib.py +0 -247
- cognite/neat/rules/exceptions.py +0 -2972
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -215
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -213
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/issues/importing.py +0 -408
- 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/{graph/issues → issues/errors}/__init__.py +0 -0
- /cognite/neat/rules/importers/{_owl2rules → _rdf/_owl2rules}/__init__.py +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.0.dist-info → cognite_neat-0.88.2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cognite.neat.issues import NeatError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class FailedAuthorizationError(NeatError):
|
|
9
|
+
description = "Missing authorization for {action}: {reason}"
|
|
10
|
+
|
|
11
|
+
action: str
|
|
12
|
+
reason: str
|
|
13
|
+
|
|
14
|
+
def message(self) -> str:
|
|
15
|
+
return self.description.format(action=self.action, reason=self.reason)
|
|
16
|
+
|
|
17
|
+
def dump(self) -> dict[str, Any]:
|
|
18
|
+
output = super().dump()
|
|
19
|
+
output["action"] = self.action
|
|
20
|
+
output["reason"] = self.reason
|
|
21
|
+
return output
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Generic
|
|
3
|
+
|
|
4
|
+
from .resources import ResourceError, T_Identifier, T_ReferenceIdentifier
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class PropertyNotFoundError(ResourceError[T_Identifier]):
|
|
9
|
+
"""The {resource_type} with identifier {identifier} does not have a property {property_name}"""
|
|
10
|
+
|
|
11
|
+
property_name: str
|
|
12
|
+
|
|
13
|
+
def message(self) -> str:
|
|
14
|
+
return (self.__doc__ or "").format(
|
|
15
|
+
resource_type=self.resource_type, identifier=repr(self.identifier), property_name=self.property_name
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def dump(self) -> dict[str, Any]:
|
|
19
|
+
output = super().dump()
|
|
20
|
+
output["property_name"] = self.property_name
|
|
21
|
+
return output
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class ReferredPropertyNotFoundError(ResourceError, Generic[T_Identifier, T_ReferenceIdentifier]):
|
|
26
|
+
"""The {resource_type} with identifier {identifier} does not have a property {property_name} referred
|
|
27
|
+
to by {referred_type} {referred_by} does not exist
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
fix = "Ensure the {resource_type} {identifier} has a {property_name} property"
|
|
31
|
+
|
|
32
|
+
referred_by: T_ReferenceIdentifier
|
|
33
|
+
referred_type: str
|
|
34
|
+
property_name: str
|
|
35
|
+
|
|
36
|
+
def message(self) -> str:
|
|
37
|
+
return (self.__doc__ or "").format(
|
|
38
|
+
resource_type=self.resource_type,
|
|
39
|
+
identifier=repr(self.identifier),
|
|
40
|
+
referred_type=self.referred_type,
|
|
41
|
+
referred_by=repr(self.referred_by),
|
|
42
|
+
property_name=self.property_name,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def dump(self) -> dict[str, Any]:
|
|
46
|
+
output = super().dump()
|
|
47
|
+
output["resource_type"] = self.resource_type
|
|
48
|
+
output["identifier"] = self.identifier
|
|
49
|
+
output["referred_by"] = self.referred_by
|
|
50
|
+
output["referred_type"] = self.referred_type
|
|
51
|
+
output["property_name"] = self.property_name
|
|
52
|
+
return output
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True)
|
|
56
|
+
class PropertyTypeNotSupportedError(ResourceError[T_Identifier]):
|
|
57
|
+
"""The {resource_type} with identifier {identifier} has a property {property_name}
|
|
58
|
+
of unsupported type {property_type}"""
|
|
59
|
+
|
|
60
|
+
property_name: str
|
|
61
|
+
property_type: str
|
|
62
|
+
|
|
63
|
+
def message(self) -> str:
|
|
64
|
+
return (self.__doc__ or "").format(
|
|
65
|
+
resource_type=self.resource_type,
|
|
66
|
+
identifier=repr(self.identifier),
|
|
67
|
+
property_name=self.property_name,
|
|
68
|
+
property_type=self.property_type,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def dump(self) -> dict[str, Any]:
|
|
72
|
+
output = super().dump()
|
|
73
|
+
output["property_name"] = self.property_name
|
|
74
|
+
output["property_type"] = self.property_type
|
|
75
|
+
return output
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from collections.abc import Hashable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from cognite.neat.issues import NeatError
|
|
6
|
+
|
|
7
|
+
T_Identifier = TypeVar("T_Identifier", bound=Hashable)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ResourceError(NeatError, Generic[T_Identifier]):
|
|
12
|
+
"""Base class for resource errors"""
|
|
13
|
+
|
|
14
|
+
identifier: T_Identifier
|
|
15
|
+
resource_type: str
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ResourceNotFoundError(ResourceError[T_Identifier]):
|
|
20
|
+
"""The {resource_type} with identifier {identifier} is missing: {reason}"""
|
|
21
|
+
|
|
22
|
+
fix = "Check the {resource_type} {identifier} and try again."
|
|
23
|
+
reason: str
|
|
24
|
+
|
|
25
|
+
def message(self) -> str:
|
|
26
|
+
return (self.__doc__ or "").format(
|
|
27
|
+
resource_type=self.resource_type, identifier=repr(self.identifier), reason=self.reason
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def dump(self) -> dict[str, Any]:
|
|
31
|
+
output = super().dump()
|
|
32
|
+
output["resource_type"] = self.resource_type
|
|
33
|
+
output["identifier"] = self.identifier
|
|
34
|
+
output["reason"] = self.reason
|
|
35
|
+
return output
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
T_ReferenceIdentifier = TypeVar("T_ReferenceIdentifier", bound=Hashable)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ReferredResourceNotFoundError(ResourceError, Generic[T_Identifier, T_ReferenceIdentifier]):
|
|
43
|
+
"""The {resource_type} with identifier {identifier} referred by {referred_type} {referred_by} does not exist"""
|
|
44
|
+
|
|
45
|
+
fix = "Create the {resource_type}"
|
|
46
|
+
|
|
47
|
+
referred_by: T_ReferenceIdentifier
|
|
48
|
+
referred_type: str
|
|
49
|
+
|
|
50
|
+
def message(self) -> str:
|
|
51
|
+
return (self.__doc__ or "").format(
|
|
52
|
+
resource_type=self.resource_type,
|
|
53
|
+
identifier=repr(self.identifier),
|
|
54
|
+
referred_type=self.referred_type,
|
|
55
|
+
referred_by=repr(self.referred_by),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def dump(self) -> dict[str, Any]:
|
|
59
|
+
output = super().dump()
|
|
60
|
+
output["resource_type"] = self.resource_type
|
|
61
|
+
output["identifier"] = self.identifier
|
|
62
|
+
output["referred_by"] = self.referred_by
|
|
63
|
+
output["referred_type"] = self.referred_type
|
|
64
|
+
return output
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class FailedConvertError(NeatError):
|
|
69
|
+
description = "Failed to convert the {identifier} to {target_format}: {reason}"
|
|
70
|
+
fix = "Check the error message and correct the rules."
|
|
71
|
+
identifier: str
|
|
72
|
+
target_format: str
|
|
73
|
+
reason: str
|
|
74
|
+
|
|
75
|
+
def message(self) -> str:
|
|
76
|
+
return self.description.format(identifier=self.identifier, target_format=self.target_format, reason=self.reason)
|
|
77
|
+
|
|
78
|
+
def dump(self) -> dict[str, Any]:
|
|
79
|
+
output = super().dump()
|
|
80
|
+
output["identifier"] = self.identifier
|
|
81
|
+
output["targetFormat"] = self.target_format
|
|
82
|
+
output["reason"] = self.reason
|
|
83
|
+
return output
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class InvalidResourceError(NeatError):
|
|
88
|
+
"""The {resource_type} with identifier {identifier} is invalid and will be skipped. {reason}"""
|
|
89
|
+
|
|
90
|
+
fix = "Check the error message and correct the instance."
|
|
91
|
+
|
|
92
|
+
resource_type: str
|
|
93
|
+
identifier: str
|
|
94
|
+
reason: str
|
|
95
|
+
|
|
96
|
+
def message(self) -> str:
|
|
97
|
+
return (self.__doc__ or "").format(
|
|
98
|
+
resource_type=self.resource_type, identifier=self.identifier, reason=self.reason
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def dump(self) -> dict[str, Any]:
|
|
102
|
+
output = super().dump()
|
|
103
|
+
output["type"] = self.resource_type
|
|
104
|
+
output["identifier"] = self.identifier
|
|
105
|
+
output["reason"] = self.reason
|
|
106
|
+
return output
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass(frozen=True)
|
|
110
|
+
class MissingIdentifierError(NeatError):
|
|
111
|
+
"""The {resource_type} with name {name} is missing an identifier."""
|
|
112
|
+
|
|
113
|
+
resource_type: str
|
|
114
|
+
name: str | None = None
|
|
115
|
+
|
|
116
|
+
def message(self) -> str:
|
|
117
|
+
return (self.__doc__ or "").format(resource_type=self.resource_type, name=self.name or "unknown")
|
|
118
|
+
|
|
119
|
+
def dump(self) -> dict[str, Any]:
|
|
120
|
+
output = super().dump()
|
|
121
|
+
output["type"] = self.resource_type
|
|
122
|
+
output["name"] = self.name
|
|
123
|
+
return output
|
|
File without changes
|
|
@@ -3,7 +3,7 @@ import xml.etree.ElementTree as ET
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from .
|
|
6
|
+
from ._base import NeatError, NeatIssueList, NeatWarning
|
|
7
7
|
|
|
8
8
|
__all__ = ["Formatter", "BasicHTML", "FORMATTER_BY_NAME"]
|
|
9
9
|
|
|
@@ -13,14 +13,14 @@ class Formatter(ABC):
|
|
|
13
13
|
default_file_prefix: str = "validation_report"
|
|
14
14
|
|
|
15
15
|
@abstractmethod
|
|
16
|
-
def create_report(self, issues:
|
|
16
|
+
def create_report(self, issues: NeatIssueList) -> str:
|
|
17
17
|
raise NotImplementedError()
|
|
18
18
|
|
|
19
19
|
@property
|
|
20
20
|
def default_file_name(self) -> str:
|
|
21
21
|
return f"{self.default_file_prefix}_{type(self).__name__.lower()}{self.file_suffix}"
|
|
22
22
|
|
|
23
|
-
def write_to_file(self, issues:
|
|
23
|
+
def write_to_file(self, issues: NeatIssueList, file_or_dir_path: Path | None = None) -> None:
|
|
24
24
|
if file_or_dir_path is None:
|
|
25
25
|
file_or_dir_path = Path(self.default_file_name)
|
|
26
26
|
elif file_or_dir_path.is_dir():
|
|
@@ -41,9 +41,9 @@ class BasicHTML(Formatter):
|
|
|
41
41
|
self._doc = ET.Element("html")
|
|
42
42
|
self._body = ET.SubElement(self._doc, "body")
|
|
43
43
|
|
|
44
|
-
def create_report(self, issues:
|
|
45
|
-
errors = [issue for issue in issues if isinstance(issue,
|
|
46
|
-
warnings_ = [issue for issue in issues if isinstance(issue,
|
|
44
|
+
def create_report(self, issues: NeatIssueList) -> str:
|
|
45
|
+
errors = [issue for issue in issues if isinstance(issue, NeatError)]
|
|
46
|
+
warnings_ = [issue for issue in issues if isinstance(issue, NeatWarning)]
|
|
47
47
|
self._doc.clear()
|
|
48
48
|
self._body = ET.SubElement(self._doc, "body")
|
|
49
49
|
h1 = ET.SubElement(self._body, "h1")
|
|
@@ -61,11 +61,11 @@ class BasicHTML(Formatter):
|
|
|
61
61
|
|
|
62
62
|
return ET.tostring(self._doc, encoding="unicode")
|
|
63
63
|
|
|
64
|
-
def _write_errors_or_warnings(self, issues: list[
|
|
65
|
-
issue_name = "errors" if isinstance(issues[0],
|
|
64
|
+
def _write_errors_or_warnings(self, issues: list[NeatError] | list[NeatWarning]) -> None:
|
|
65
|
+
issue_name = "errors" if isinstance(issues[0], NeatError) else "warnings"
|
|
66
66
|
main_categories = {base_ for issue in issues for base_ in type(issue).__bases__}
|
|
67
67
|
for category in main_categories:
|
|
68
|
-
issues_in_category: list[
|
|
68
|
+
issues_in_category: list[NeatError] | list[NeatWarning] = [ # type: ignore[assignment]
|
|
69
69
|
issue for issue in issues if isinstance(issue, category)
|
|
70
70
|
]
|
|
71
71
|
h3 = ET.SubElement(self._body, "h3")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cognite.neat.issues import NeatWarning
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class RegexViolationWarning(NeatWarning):
|
|
9
|
+
"""The value '{value}' of {identifier} does not match the {pattern_name} pattern '{pattern}'"""
|
|
10
|
+
|
|
11
|
+
value: str
|
|
12
|
+
pattern: str
|
|
13
|
+
identifier: str
|
|
14
|
+
pattern_name: str
|
|
15
|
+
|
|
16
|
+
def message(self) -> str:
|
|
17
|
+
return (self.__doc__ or "").format(
|
|
18
|
+
value=self.value, pattern=self.pattern, identifier=self.identifier, pattern_name=self.pattern_name
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def dump(self) -> dict[str, Any]:
|
|
22
|
+
output = super().dump()
|
|
23
|
+
output["value"] = self.value
|
|
24
|
+
output["pattern"] = self.pattern
|
|
25
|
+
output["identifier"] = self.identifier
|
|
26
|
+
output["pattern_name"] = self.pattern_name
|
|
27
|
+
return output
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from cognite.neat.issues import NeatWarning
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class InvalidClassWarning(NeatWarning):
|
|
9
|
+
description = "The class {class_name} is invalid and will be skipped. {reason}"
|
|
10
|
+
fix = "Check the error message and correct the class."
|
|
11
|
+
|
|
12
|
+
class_name: str
|
|
13
|
+
reason: str
|
|
14
|
+
|
|
15
|
+
def message(self) -> str:
|
|
16
|
+
return self.description.format(class_name=self.class_name, reason=self.reason)
|
|
17
|
+
|
|
18
|
+
def dump(self) -> dict[str, Any]:
|
|
19
|
+
output = super().dump()
|
|
20
|
+
output["class_name"] = self.class_name
|
|
21
|
+
output["reason"] = self.reason
|
|
22
|
+
return output
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Generic
|
|
3
|
+
|
|
4
|
+
from .resources import ResourceWarning, T_Identifier, T_ReferenceIdentifier
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True)
|
|
8
|
+
class PropertyTypeNotSupportedWarning(ResourceWarning[T_Identifier]):
|
|
9
|
+
"""The {resource_type} with identifier {identifier} has a property {property_name}
|
|
10
|
+
of unsupported type {property_type}. This will be ignored."""
|
|
11
|
+
|
|
12
|
+
property_name: str
|
|
13
|
+
property_type: str
|
|
14
|
+
|
|
15
|
+
def message(self) -> str:
|
|
16
|
+
return (self.__doc__ or "").format(
|
|
17
|
+
resource_type=self.resource_type,
|
|
18
|
+
identifier=repr(self.identifier),
|
|
19
|
+
property_name=self.property_name,
|
|
20
|
+
property_type=self.property_type,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def dump(self) -> dict[str, Any]:
|
|
24
|
+
output = super().dump()
|
|
25
|
+
output["property_name"] = self.property_name
|
|
26
|
+
output["property_type"] = self.property_type
|
|
27
|
+
return output
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class ReferredPropertyNotFoundWarning(ResourceWarning, Generic[T_Identifier, T_ReferenceIdentifier]):
|
|
32
|
+
"""The {resource_type} with identifier {identifier} does not have a property {property_name} referred
|
|
33
|
+
to by {referred_type} {referred_by} does not exist. This will be ignored.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
fix = "Ensure the {resource_type} {identifier} has a {property_name} property"
|
|
37
|
+
|
|
38
|
+
referred_by: T_ReferenceIdentifier
|
|
39
|
+
referred_type: str
|
|
40
|
+
property_name: str
|
|
41
|
+
|
|
42
|
+
def message(self) -> str:
|
|
43
|
+
return (self.__doc__ or "").format(
|
|
44
|
+
resource_type=self.resource_type,
|
|
45
|
+
identifier=repr(self.identifier),
|
|
46
|
+
referred_type=self.referred_type,
|
|
47
|
+
referred_by=repr(self.referred_by),
|
|
48
|
+
property_name=self.property_name,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def dump(self) -> dict[str, Any]:
|
|
52
|
+
output = super().dump()
|
|
53
|
+
output["resource_type"] = self.resource_type
|
|
54
|
+
output["identifier"] = self.identifier
|
|
55
|
+
output["referred_by"] = self.referred_by
|
|
56
|
+
output["referred_type"] = self.referred_type
|
|
57
|
+
output["property_name"] = self.property_name
|
|
58
|
+
return output
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class PropertyRedefinedWarning(ResourceWarning[T_Identifier]):
|
|
63
|
+
"""The {resource_type} with identifier {identifier} has a property {property_name} redefined."""
|
|
64
|
+
|
|
65
|
+
property_id: str
|
|
66
|
+
|
|
67
|
+
def message(self) -> str:
|
|
68
|
+
return (self.__doc__ or "").format(
|
|
69
|
+
resource_type=self.resource_type, identifier=repr(self.identifier), property_name=self.property_id
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def dump(self) -> dict[str, Any]:
|
|
73
|
+
output = super().dump()
|
|
74
|
+
output["property_id"] = self.property_id
|
|
75
|
+
output["resource_type"] = self.resource_type
|
|
76
|
+
output["identifier"] = self.identifier
|
|
77
|
+
return output
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from collections.abc import Hashable
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from cognite.neat.issues import NeatWarning
|
|
6
|
+
|
|
7
|
+
T_Identifier = TypeVar("T_Identifier", bound=Hashable)
|
|
8
|
+
|
|
9
|
+
T_ReferenceIdentifier = TypeVar("T_ReferenceIdentifier", bound=Hashable)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class ResourceWarning(NeatWarning, Generic[T_Identifier]):
|
|
14
|
+
"""Base class for resource warnings"""
|
|
15
|
+
|
|
16
|
+
identifier: T_Identifier
|
|
17
|
+
resource_type: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class ResourceNotFoundWarning(ResourceWarning[T_Identifier]):
|
|
22
|
+
"""The {resource_type} with identifier {identifier} is missing: {reason}. This will be ignored."""
|
|
23
|
+
|
|
24
|
+
fix = "Check the {resource_type} {identifier} and try again."
|
|
25
|
+
reason: str
|
|
26
|
+
|
|
27
|
+
def message(self) -> str:
|
|
28
|
+
return (self.__doc__ or "").format(
|
|
29
|
+
resource_type=self.resource_type, identifier=repr(self.identifier), reason=self.reason
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def dump(self) -> dict[str, Any]:
|
|
33
|
+
output = super().dump()
|
|
34
|
+
output["resource_type"] = self.resource_type
|
|
35
|
+
output["identifier"] = self.identifier
|
|
36
|
+
output["reason"] = self.reason
|
|
37
|
+
return output
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class ReferredResourceNotFoundWarning(ResourceWarning, Generic[T_Identifier, T_ReferenceIdentifier]):
|
|
42
|
+
"""The {resource_type} with identifier {identifier} referred by {referred_type} {referred_by} does not exist.
|
|
43
|
+
This will be ignored."""
|
|
44
|
+
|
|
45
|
+
fix = "Create the {resource_type}"
|
|
46
|
+
|
|
47
|
+
referred_by: T_ReferenceIdentifier
|
|
48
|
+
referred_type: str
|
|
49
|
+
|
|
50
|
+
def message(self) -> str:
|
|
51
|
+
return (self.__doc__ or "").format(
|
|
52
|
+
resource_type=self.resource_type,
|
|
53
|
+
identifier=repr(self.identifier),
|
|
54
|
+
referred_type=self.referred_type,
|
|
55
|
+
referred_by=repr(self.referred_by),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def dump(self) -> dict[str, Any]:
|
|
59
|
+
output = super().dump()
|
|
60
|
+
output["resource_type"] = self.resource_type
|
|
61
|
+
output["identifier"] = self.identifier
|
|
62
|
+
output["referred_by"] = self.referred_by
|
|
63
|
+
output["referred_type"] = self.referred_type
|
|
64
|
+
return output
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class MultipleResourcesWarning(NeatWarning, Generic[T_Identifier]):
|
|
69
|
+
"""Multiple resources of type {resource_type} with identifiers {resources} were found. This will be ignored."""
|
|
70
|
+
|
|
71
|
+
fix = "Remove the duplicate resources"
|
|
72
|
+
|
|
73
|
+
resources: frozenset[T_Identifier]
|
|
74
|
+
resource_type: str
|
|
75
|
+
|
|
76
|
+
def message(self) -> str:
|
|
77
|
+
return (self.__doc__ or "").format(
|
|
78
|
+
resource_type=self.resource_type,
|
|
79
|
+
resources=self.resources,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def dump(self) -> dict[str, Any]:
|
|
83
|
+
output = super().dump()
|
|
84
|
+
output["resource_type"] = self.resource_type
|
|
85
|
+
output["resources"] = self.resources
|
|
86
|
+
return output
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@dataclass(frozen=True)
|
|
90
|
+
class FailedLoadingResourcesWarning(NeatWarning, Generic[T_Identifier]):
|
|
91
|
+
"""Failed to load resources of type {resource_type} with identifiers {resources}. Continuing without
|
|
92
|
+
these resources."""
|
|
93
|
+
|
|
94
|
+
extra = "The error was: {error}"
|
|
95
|
+
|
|
96
|
+
fix = "Check the error."
|
|
97
|
+
|
|
98
|
+
resources: frozenset[T_Identifier]
|
|
99
|
+
resource_type: str
|
|
100
|
+
error: str | None = None
|
|
101
|
+
|
|
102
|
+
def message(self) -> str:
|
|
103
|
+
return (self.__doc__ or "").format(
|
|
104
|
+
resource_type=self.resource_type,
|
|
105
|
+
resources=self.resources,
|
|
106
|
+
) + (self.extra.format(error=self.error) if self.error else "")
|
|
107
|
+
|
|
108
|
+
def dump(self) -> dict[str, Any]:
|
|
109
|
+
output = super().dump()
|
|
110
|
+
output["resource_type"] = self.resource_type
|
|
111
|
+
output["resources"] = self.resources
|
|
112
|
+
return output
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ResourceTypeNotSupportedWarning(ResourceWarning[T_Identifier]):
|
|
116
|
+
"""The {resource_type} with identifier {identifier} is not supported. This will be ignored."""
|
|
117
|
+
|
|
118
|
+
def message(self) -> str:
|
|
119
|
+
return (self.__doc__ or "").format(resource_type=self.resource_type, identifier=repr(self.identifier))
|
|
120
|
+
|
|
121
|
+
def dump(self) -> dict[str, Any]:
|
|
122
|
+
output = super().dump()
|
|
123
|
+
output["resource_type"] = self.resource_type
|
|
124
|
+
output["identifier"] = self.identifier
|
|
125
|
+
return output
|
|
@@ -15,9 +15,10 @@ from cognite.client.data_classes.data_modeling import (
|
|
|
15
15
|
)
|
|
16
16
|
from cognite.client.exceptions import CogniteAPIError
|
|
17
17
|
|
|
18
|
+
from cognite.neat.issues import IssueList
|
|
19
|
+
from cognite.neat.issues.neat_warnings.resources import FailedLoadingResourcesWarning
|
|
18
20
|
from cognite.neat.rules import issues
|
|
19
21
|
from cognite.neat.rules._shared import Rules
|
|
20
|
-
from cognite.neat.rules.issues import IssueList
|
|
21
22
|
from cognite.neat.rules.models import InformationRules
|
|
22
23
|
from cognite.neat.rules.models.dms import DMSRules, DMSSchema, PipelineSchema
|
|
23
24
|
from cognite.neat.utils.cdf.loaders import (
|
|
@@ -313,7 +314,7 @@ class DMSExporter(CDFExporter[DMSSchema]):
|
|
|
313
314
|
try:
|
|
314
315
|
data_models = loader.client.data_modeling.data_models.list(space=space, limit=25, all_versions=False)
|
|
315
316
|
except CogniteAPIError as e:
|
|
316
|
-
warnings.warn(
|
|
317
|
+
warnings.warn(FailedLoadingResourcesWarning[str](frozenset({space}), "Space", str(e)), stacklevel=2)
|
|
317
318
|
return []
|
|
318
319
|
else:
|
|
319
320
|
return [
|
|
@@ -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
|