cognite-neat 0.88.1__py3-none-any.whl → 0.88.3__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.
- cognite/neat/_version.py +1 -1
- cognite/neat/graph/__init__.py +0 -3
- cognite/neat/graph/loaders/_base.py +6 -6
- cognite/neat/graph/loaders/_rdf2asset.py +28 -31
- cognite/neat/graph/loaders/_rdf2dms.py +24 -15
- cognite/neat/issues/__init__.py +14 -0
- cognite/neat/issues/_base.py +415 -0
- cognite/neat/issues/errors/__init__.py +72 -0
- cognite/neat/issues/errors/_external.py +67 -0
- cognite/neat/issues/errors/_general.py +28 -0
- cognite/neat/issues/errors/_properties.py +62 -0
- cognite/neat/issues/errors/_resources.py +111 -0
- cognite/neat/issues/errors/_workflow.py +36 -0
- cognite/neat/{rules/issues → issues}/formatters.py +10 -10
- cognite/neat/issues/warnings/__init__.py +66 -0
- cognite/neat/issues/warnings/_external.py +40 -0
- cognite/neat/issues/warnings/_general.py +29 -0
- cognite/neat/issues/warnings/_models.py +92 -0
- cognite/neat/issues/warnings/_properties.py +44 -0
- cognite/neat/issues/warnings/_resources.py +55 -0
- cognite/neat/issues/warnings/user_modeling.py +113 -0
- cognite/neat/rules/_shared.py +10 -2
- cognite/neat/rules/exporters/_base.py +6 -6
- cognite/neat/rules/exporters/_rules2dms.py +19 -11
- cognite/neat/rules/exporters/_rules2excel.py +4 -4
- cognite/neat/rules/exporters/_rules2ontology.py +74 -51
- cognite/neat/rules/exporters/_rules2yaml.py +3 -3
- cognite/neat/rules/exporters/_validation.py +11 -96
- cognite/neat/rules/importers/__init__.py +7 -3
- cognite/neat/rules/importers/_base.py +9 -13
- cognite/neat/rules/importers/_dms2rules.py +42 -24
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +49 -53
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +31 -23
- cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -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} +24 -18
- cognite/neat/rules/importers/{_inference2rules.py → _rdf/_inference2rules.py} +9 -9
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +58 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2metadata.py +68 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +60 -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 +35 -22
- cognite/neat/rules/importers/_yaml2rules.py +23 -21
- cognite/neat/rules/models/_constants.py +2 -1
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/_types/_field.py +9 -11
- cognite/neat/rules/models/asset/_rules.py +1 -3
- cognite/neat/rules/models/asset/_validation.py +14 -10
- cognite/neat/rules/models/dms/_converter.py +2 -4
- cognite/neat/rules/models/dms/_exporter.py +30 -8
- cognite/neat/rules/models/dms/_rules.py +23 -7
- cognite/neat/rules/models/dms/_schema.py +94 -62
- cognite/neat/rules/models/dms/_validation.py +105 -66
- cognite/neat/rules/models/entities.py +3 -0
- cognite/neat/rules/models/information/_converter.py +2 -2
- cognite/neat/rules/models/information/_rules.py +7 -8
- cognite/neat/rules/models/information/_validation.py +48 -25
- cognite/neat/rules/transformers/__init__.py +0 -0
- cognite/neat/rules/transformers/_base.py +15 -0
- cognite/neat/utils/auxiliary.py +2 -35
- cognite/neat/utils/text.py +17 -0
- cognite/neat/workflows/base.py +4 -4
- cognite/neat/workflows/cdf_store.py +3 -3
- cognite/neat/workflows/steps/data_contracts.py +1 -1
- cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
- cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
- cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
- cognite/neat/workflows/steps/lib/current/rules_exporter.py +10 -10
- cognite/neat/workflows/steps/lib/current/rules_importer.py +78 -6
- cognite/neat/workflows/steps/lib/current/rules_validator.py +20 -9
- cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
- cognite/neat/workflows/steps_registry.py +4 -5
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.3.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.3.dist-info}/RECORD +86 -77
- cognite/neat/exceptions.py +0 -145
- cognite/neat/graph/exceptions.py +0 -90
- cognite/neat/graph/issues/loader.py +0 -104
- cognite/neat/issues.py +0 -158
- cognite/neat/rules/importers/_owl2rules/_owl2classes.py +0 -215
- cognite/neat/rules/importers/_owl2rules/_owl2metadata.py +0 -209
- cognite/neat/rules/importers/_owl2rules/_owl2properties.py +0 -203
- cognite/neat/rules/issues/__init__.py +0 -26
- cognite/neat/rules/issues/base.py +0 -82
- cognite/neat/rules/issues/dms.py +0 -683
- cognite/neat/rules/issues/fileread.py +0 -197
- cognite/neat/rules/issues/importing.py +0 -423
- cognite/neat/rules/issues/ontology.py +0 -298
- cognite/neat/rules/issues/spreadsheet.py +0 -563
- cognite/neat/rules/issues/spreadsheet_file.py +0 -151
- cognite/neat/rules/issues/tables.py +0 -72
- cognite/neat/workflows/_exceptions.py +0 -41
- /cognite/neat/{graph/issues → rules/importers/_rdf}/__init__.py +0 -0
- /cognite/neat/rules/importers/{_owl2rules → _rdf/_owl2rules}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/_base.py +0 -0
- /cognite/neat/{graph/stores → store}/_provenance.py +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.3.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.3.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.1.dist-info → cognite_neat-0.88.3.dist-info}/entry_points.txt +0 -0
cognite/neat/graph/exceptions.py
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"""This module contains the definition of validation errors and warnings raised during graph methods"""
|
|
2
|
-
|
|
3
|
-
from cognite.neat.constants import DEFAULT_DOCS_URL
|
|
4
|
-
from cognite.neat.exceptions import NeatException
|
|
5
|
-
|
|
6
|
-
DOCS_BASE_URL = f"{DEFAULT_DOCS_URL}api/exceptions.html#{__name__}"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class UnsupportedPropertyType(NeatException):
|
|
10
|
-
"""Unsupported property type when processing the graph capturing sheet
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
property_type: property type that is not supported
|
|
14
|
-
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
type_: str = "UnsupportedPropertyType"
|
|
18
|
-
code: int = 1000
|
|
19
|
-
description: str = "Unsupported property type when processing the graph capturing sheet."
|
|
20
|
-
example: str = ""
|
|
21
|
-
fix: str = ""
|
|
22
|
-
|
|
23
|
-
def __init__(self, property_type: str, verbose: bool = False):
|
|
24
|
-
self.property_type = property_type
|
|
25
|
-
|
|
26
|
-
self.message = (
|
|
27
|
-
f"Property type {self.property_type} is not supported. "
|
|
28
|
-
" Only the following property types are supported: DatatypeProperty and ObjectProperty"
|
|
29
|
-
f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}"
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
if verbose:
|
|
33
|
-
self.message += f"\nDescription: {self.description}"
|
|
34
|
-
self.message += f"\nExample: {self.example}"
|
|
35
|
-
self.message += f"\nFix: {self.fix}"
|
|
36
|
-
super().__init__(self.message)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class NamespaceRequired(NeatException):
|
|
40
|
-
"""The functionality requires namespace in the TransformationRules.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
functionality: functionality that requires namespace
|
|
44
|
-
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
type_ = "NamespaceRequired"
|
|
48
|
-
description: str = "The functionality requires namespace in the TransformationRules."
|
|
49
|
-
example: str = ""
|
|
50
|
-
fix: str = ""
|
|
51
|
-
|
|
52
|
-
def __init__(self, functionality: str, verbose: bool = False):
|
|
53
|
-
self.message = (
|
|
54
|
-
f"Namespace is required to be set in the Transformation rules"
|
|
55
|
-
f"to use {functionality}."
|
|
56
|
-
f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}"
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
if verbose:
|
|
60
|
-
self.message += f"\nDescription: {self.description}"
|
|
61
|
-
self.message += f"\nExample: {self.example}"
|
|
62
|
-
self.message += f"\nFix: {self.fix}"
|
|
63
|
-
super().__init__(self.message)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class DatasetIdRequired(NeatException):
|
|
67
|
-
"""The functionality requires data_set_id in the TransformationRules.
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
functionality: functionality that requires namespace
|
|
71
|
-
verbose: flag that indicates whether to provide enhanced exception message, by default False
|
|
72
|
-
"""
|
|
73
|
-
|
|
74
|
-
type_ = "DatasetIdRequired"
|
|
75
|
-
description: str = "The functionality requires data_set_id in the TransformationRules."
|
|
76
|
-
example: str = ""
|
|
77
|
-
fix: str = ""
|
|
78
|
-
|
|
79
|
-
def __init__(self, functionality: str, verbose: bool = False):
|
|
80
|
-
self.message = (
|
|
81
|
-
f"DataSetId is required to be set in the Transformation rules"
|
|
82
|
-
f"to use {functionality}."
|
|
83
|
-
f"\nFor more information visit: {DOCS_BASE_URL}.{self.__class__.__name__}"
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
if verbose:
|
|
87
|
-
self.message += f"\nDescription: {self.description}"
|
|
88
|
-
self.message += f"\nExample: {self.example}"
|
|
89
|
-
self.message += f"\nFix: {self.fix}"
|
|
90
|
-
super().__init__(self.message)
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
from cognite.neat.issues import NeatError, NeatWarning
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
"FailedAuthorizationError",
|
|
8
|
-
"MissingDataModelError",
|
|
9
|
-
"FailedConvertError",
|
|
10
|
-
"InvalidClassWarning",
|
|
11
|
-
"InvalidInstanceError",
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass(frozen=True)
|
|
16
|
-
class FailedAuthorizationError(NeatError):
|
|
17
|
-
description = "Missing authorization for {action}: {reason}"
|
|
18
|
-
|
|
19
|
-
action: str
|
|
20
|
-
reason: str
|
|
21
|
-
|
|
22
|
-
def message(self) -> str:
|
|
23
|
-
return self.description.format(action=self.action, reason=self.reason)
|
|
24
|
-
|
|
25
|
-
def dump(self) -> dict[str, Any]:
|
|
26
|
-
output = super().dump()
|
|
27
|
-
output["action"] = self.action
|
|
28
|
-
output["reason"] = self.reason
|
|
29
|
-
return output
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@dataclass(frozen=True)
|
|
33
|
-
class MissingDataModelError(NeatError):
|
|
34
|
-
description = "The data model with identifier {identifier} is missing: {reason}"
|
|
35
|
-
fix = "Check the data model identifier and try again."
|
|
36
|
-
|
|
37
|
-
identifier: str
|
|
38
|
-
reason: str
|
|
39
|
-
|
|
40
|
-
def message(self) -> str:
|
|
41
|
-
return self.description.format(identifier=self.identifier, reason=self.reason)
|
|
42
|
-
|
|
43
|
-
def dump(self) -> dict[str, Any]:
|
|
44
|
-
output = super().dump()
|
|
45
|
-
output["identifier"] = self.identifier
|
|
46
|
-
output["reason"] = self.reason
|
|
47
|
-
return output
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@dataclass(frozen=True)
|
|
51
|
-
class FailedConvertError(NeatError):
|
|
52
|
-
description = "Failed to convert the {identifier} to {target_format}: {reason}"
|
|
53
|
-
fix = "Check the error message and correct the rules."
|
|
54
|
-
identifier: str
|
|
55
|
-
target_format: str
|
|
56
|
-
reason: str
|
|
57
|
-
|
|
58
|
-
def message(self) -> str:
|
|
59
|
-
return self.description.format(identifier=self.identifier, target_format=self.target_format, reason=self.reason)
|
|
60
|
-
|
|
61
|
-
def dump(self) -> dict[str, Any]:
|
|
62
|
-
output = super().dump()
|
|
63
|
-
output["identifier"] = self.identifier
|
|
64
|
-
output["targetFormat"] = self.target_format
|
|
65
|
-
output["reason"] = self.reason
|
|
66
|
-
return output
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@dataclass(frozen=True)
|
|
70
|
-
class InvalidClassWarning(NeatWarning):
|
|
71
|
-
description = "The class {class_name} is invalid and will be skipped. {reason}"
|
|
72
|
-
fix = "Check the error message and correct the class."
|
|
73
|
-
|
|
74
|
-
class_name: str
|
|
75
|
-
reason: str
|
|
76
|
-
|
|
77
|
-
def message(self) -> str:
|
|
78
|
-
return self.description.format(class_name=self.class_name, reason=self.reason)
|
|
79
|
-
|
|
80
|
-
def dump(self) -> dict[str, Any]:
|
|
81
|
-
output = super().dump()
|
|
82
|
-
output["class_name"] = self.class_name
|
|
83
|
-
output["reason"] = self.reason
|
|
84
|
-
return output
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@dataclass(frozen=True)
|
|
88
|
-
class InvalidInstanceError(NeatError):
|
|
89
|
-
description = "The {type_} with identifier {identifier} is invalid and will be skipped. {reason}"
|
|
90
|
-
fix = "Check the error message and correct the instance."
|
|
91
|
-
|
|
92
|
-
type_: str
|
|
93
|
-
identifier: str
|
|
94
|
-
reason: str
|
|
95
|
-
|
|
96
|
-
def message(self) -> str:
|
|
97
|
-
return self.description.format(type_=self.type_, identifier=self.identifier, reason=self.reason)
|
|
98
|
-
|
|
99
|
-
def dump(self) -> dict[str, Any]:
|
|
100
|
-
output = super().dump()
|
|
101
|
-
output["type"] = self.type_
|
|
102
|
-
output["identifier"] = self.identifier
|
|
103
|
-
output["reason"] = self.reason
|
|
104
|
-
return output
|
cognite/neat/issues.py
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import warnings
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from collections import UserList
|
|
5
|
-
from collections.abc import Sequence
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from functools import total_ordering
|
|
8
|
-
from typing import Any, ClassVar, TypeVar
|
|
9
|
-
from warnings import WarningMessage
|
|
10
|
-
|
|
11
|
-
import pandas as pd
|
|
12
|
-
from pydantic_core import PydanticCustomError
|
|
13
|
-
|
|
14
|
-
if sys.version_info < (3, 11):
|
|
15
|
-
from exceptiongroup import ExceptionGroup
|
|
16
|
-
from typing_extensions import Self
|
|
17
|
-
else:
|
|
18
|
-
from typing import Self
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@total_ordering
|
|
22
|
-
@dataclass(frozen=True)
|
|
23
|
-
class NeatIssue(ABC):
|
|
24
|
-
description: ClassVar[str]
|
|
25
|
-
fix: ClassVar[str]
|
|
26
|
-
|
|
27
|
-
def message(self) -> str:
|
|
28
|
-
"""Return a human-readable message for the issue.
|
|
29
|
-
|
|
30
|
-
This is the default implementation, which returns the description.
|
|
31
|
-
It is recommended to override this method in subclasses with a more
|
|
32
|
-
specific message.
|
|
33
|
-
"""
|
|
34
|
-
return self.description
|
|
35
|
-
|
|
36
|
-
@abstractmethod
|
|
37
|
-
def dump(self) -> dict[str, Any]:
|
|
38
|
-
"""Return a dictionary representation of the issue."""
|
|
39
|
-
raise NotImplementedError()
|
|
40
|
-
|
|
41
|
-
def __lt__(self, other: "NeatIssue") -> bool:
|
|
42
|
-
if not isinstance(other, NeatIssue):
|
|
43
|
-
return NotImplemented
|
|
44
|
-
return (type(self).__name__, self.message()) < (type(other).__name__, other.message())
|
|
45
|
-
|
|
46
|
-
def __eq__(self, other: object) -> bool:
|
|
47
|
-
if not isinstance(other, NeatIssue):
|
|
48
|
-
return NotImplemented
|
|
49
|
-
return (type(self).__name__, self.message()) == (type(other).__name__, other.message())
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
@dataclass(frozen=True)
|
|
53
|
-
class NeatError(NeatIssue, ABC):
|
|
54
|
-
def dump(self) -> dict[str, Any]:
|
|
55
|
-
return {"errorType": type(self).__name__}
|
|
56
|
-
|
|
57
|
-
def as_exception(self) -> ValueError:
|
|
58
|
-
return ValueError(self.message())
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
@dataclass(frozen=True)
|
|
69
|
-
class NeatWarning(NeatIssue, ABC, UserWarning):
|
|
70
|
-
def dump(self) -> dict[str, Any]:
|
|
71
|
-
return {"warningType": type(self).__name__}
|
|
72
|
-
|
|
73
|
-
@classmethod
|
|
74
|
-
def from_warning(cls, warning: WarningMessage) -> "NeatWarning":
|
|
75
|
-
return DefaultWarning.from_warning_message(warning)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@dataclass(frozen=True)
|
|
79
|
-
class DefaultWarning(NeatWarning):
|
|
80
|
-
description = "A warning was raised during validation."
|
|
81
|
-
fix = "No fix is available."
|
|
82
|
-
|
|
83
|
-
warning: str | Warning
|
|
84
|
-
category: type[Warning]
|
|
85
|
-
source: str | None = None
|
|
86
|
-
|
|
87
|
-
def dump(self) -> dict[str, Any]:
|
|
88
|
-
output = super().dump()
|
|
89
|
-
output["msg"] = str(self.warning)
|
|
90
|
-
output["category"] = self.category.__name__
|
|
91
|
-
output["source"] = self.source
|
|
92
|
-
return output
|
|
93
|
-
|
|
94
|
-
@classmethod
|
|
95
|
-
def from_warning_message(cls, warning: WarningMessage) -> NeatWarning:
|
|
96
|
-
if isinstance(warning.message, NeatWarning):
|
|
97
|
-
return warning.message
|
|
98
|
-
|
|
99
|
-
return cls(
|
|
100
|
-
warning=warning.message,
|
|
101
|
-
category=warning.category,
|
|
102
|
-
source=warning.source,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
def message(self) -> str:
|
|
106
|
-
return str(self.warning)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
T_NeatIssue = TypeVar("T_NeatIssue", bound=NeatIssue)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class NeatIssueList(UserList[T_NeatIssue], ABC):
|
|
113
|
-
def __init__(self, issues: Sequence[T_NeatIssue] | None = None, title: str | None = None):
|
|
114
|
-
super().__init__(issues or [])
|
|
115
|
-
self.title = title
|
|
116
|
-
|
|
117
|
-
@property
|
|
118
|
-
def errors(self) -> Self:
|
|
119
|
-
return type(self)([issue for issue in self if isinstance(issue, NeatError)]) # type: ignore[misc]
|
|
120
|
-
|
|
121
|
-
@property
|
|
122
|
-
def has_errors(self) -> bool:
|
|
123
|
-
return any(isinstance(issue, NeatError) for issue in self)
|
|
124
|
-
|
|
125
|
-
@property
|
|
126
|
-
def warnings(self) -> Self:
|
|
127
|
-
return type(self)([issue for issue in self if isinstance(issue, NeatWarning)]) # type: ignore[misc]
|
|
128
|
-
|
|
129
|
-
def as_errors(self) -> ExceptionGroup:
|
|
130
|
-
return ExceptionGroup(
|
|
131
|
-
"Operation failed",
|
|
132
|
-
[ValueError(issue.message()) for issue in self if isinstance(issue, NeatError)],
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
def trigger_warnings(self) -> None:
|
|
136
|
-
for warning in [issue for issue in self if isinstance(issue, NeatWarning)]:
|
|
137
|
-
warnings.warn(warning, stacklevel=2)
|
|
138
|
-
|
|
139
|
-
def to_pandas(self) -> pd.DataFrame:
|
|
140
|
-
return pd.DataFrame([issue.dump() for issue in self])
|
|
141
|
-
|
|
142
|
-
def _repr_html_(self) -> str | None:
|
|
143
|
-
return self.to_pandas()._repr_html_() # type: ignore[operator]
|
|
144
|
-
|
|
145
|
-
def as_exception(self) -> "MultiValueError":
|
|
146
|
-
return MultiValueError(self.errors)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
class MultiValueError(ValueError):
|
|
150
|
-
"""This is a container for multiple errors.
|
|
151
|
-
|
|
152
|
-
It is used in the pydantic field_validator/model_validator to collect multiple errors, which
|
|
153
|
-
can then be caught in a try-except block and returned as an IssueList.
|
|
154
|
-
|
|
155
|
-
"""
|
|
156
|
-
|
|
157
|
-
def __init__(self, errors: Sequence[T_NeatIssue]):
|
|
158
|
-
self.errors = list(errors)
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
from typing import cast
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
from rdflib import OWL, Graph
|
|
6
|
-
|
|
7
|
-
from cognite.neat.rules.models._base import MatchType
|
|
8
|
-
from cognite.neat.utils.rdf_ import remove_namespace_from_uri
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def parse_owl_classes(graph: Graph, language: str = "en") -> list[dict]:
|
|
12
|
-
"""Parse owl classes from graph to pandas dataframe.
|
|
13
|
-
|
|
14
|
-
Args:
|
|
15
|
-
graph: Graph containing owl classes
|
|
16
|
-
language: Language to use for parsing, by default "en"
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
Dataframe containing owl classes
|
|
20
|
-
|
|
21
|
-
!!! note "Compliant OWL classes"
|
|
22
|
-
This makes the method very opinionated, but results in a compliant classes.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
query = """
|
|
26
|
-
SELECT ?class ?name ?description ?parentClass ?reference ?match ?comment
|
|
27
|
-
WHERE {
|
|
28
|
-
?class a owl:Class .
|
|
29
|
-
OPTIONAL {?class rdfs:subClassOf ?parentClass }.
|
|
30
|
-
OPTIONAL {?class rdfs:label ?name }.
|
|
31
|
-
OPTIONAL {?class rdfs:comment ?description} .
|
|
32
|
-
FILTER (!isBlank(?class))
|
|
33
|
-
FILTER (!bound(?parentClass) || !isBlank(?parentClass))
|
|
34
|
-
FILTER (!bound(?name) || LANG(?name) = "" || LANGMATCHES(LANG(?name), "en"))
|
|
35
|
-
FILTER (!bound(?description) || LANG(?description) = "" || LANGMATCHES(LANG(?description), "en"))
|
|
36
|
-
}
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
# create raw dataframe
|
|
40
|
-
|
|
41
|
-
raw_df = _parse_raw_dataframe(cast(list[tuple], list(graph.query(query.replace("en", language)))))
|
|
42
|
-
if raw_df.empty:
|
|
43
|
-
return []
|
|
44
|
-
|
|
45
|
-
# group values and clean up
|
|
46
|
-
processed_df = _clean_up_classes(raw_df)
|
|
47
|
-
|
|
48
|
-
# make compliant
|
|
49
|
-
processed_df = make_classes_compliant(processed_df)
|
|
50
|
-
|
|
51
|
-
# Make Parent Class list elements into string joined with comma
|
|
52
|
-
processed_df["Parent Class"] = processed_df["Parent Class"].apply(
|
|
53
|
-
lambda x: ", ".join(x) if isinstance(x, list) and x else None
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
return processed_df.dropna(axis=0, how="all").replace(float("nan"), None).to_dict(orient="records")
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _parse_raw_dataframe(query_results: list[tuple]) -> pd.DataFrame:
|
|
60
|
-
df = pd.DataFrame(
|
|
61
|
-
query_results,
|
|
62
|
-
columns=["Class", "Name", "Description", "Parent Class", "Reference", "Match", "Comment"],
|
|
63
|
-
)
|
|
64
|
-
if df.empty:
|
|
65
|
-
return df
|
|
66
|
-
|
|
67
|
-
# # remove NaNs
|
|
68
|
-
df.replace(np.nan, "", regex=True, inplace=True)
|
|
69
|
-
|
|
70
|
-
df.Reference = df.Class
|
|
71
|
-
df.Class = df.Class.apply(lambda x: remove_namespace_from_uri(x))
|
|
72
|
-
df["Match Type"] = len(df) * [MatchType.exact]
|
|
73
|
-
df["Comment"] = len(df) * [None]
|
|
74
|
-
df["Parent Class"] = df["Parent Class"].apply(lambda x: remove_namespace_from_uri(x))
|
|
75
|
-
|
|
76
|
-
return df
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def _clean_up_classes(df: pd.DataFrame) -> pd.DataFrame:
|
|
80
|
-
clean_list = [
|
|
81
|
-
{
|
|
82
|
-
"Class": class_,
|
|
83
|
-
"Name": group_df["Name"].unique()[0],
|
|
84
|
-
"Description": "\n".join(list(group_df.Description.unique())),
|
|
85
|
-
"Parent Class": ", ".join(list(group_df["Parent Class"].unique())),
|
|
86
|
-
"Reference": group_df["Reference"].unique()[0],
|
|
87
|
-
"Match Type": group_df["Match Type"].unique()[0],
|
|
88
|
-
"Comment": group_df["Comment"].unique()[0],
|
|
89
|
-
}
|
|
90
|
-
for class_, group_df in df.groupby("Class")
|
|
91
|
-
]
|
|
92
|
-
|
|
93
|
-
df = pd.DataFrame(clean_list)
|
|
94
|
-
|
|
95
|
-
# bring NaNs back
|
|
96
|
-
df.replace("", None, inplace=True)
|
|
97
|
-
|
|
98
|
-
# split Parent Class column back into list
|
|
99
|
-
df["Parent Class"] = df["Parent Class"].apply(lambda x: x.split(", ") if isinstance(x, str) else None)
|
|
100
|
-
|
|
101
|
-
return df
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def make_classes_compliant(classes: pd.DataFrame) -> pd.DataFrame:
|
|
105
|
-
"""Make classes compliant.
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
Dataframe containing compliant classes
|
|
109
|
-
|
|
110
|
-
!!! note "About the compliant classes"
|
|
111
|
-
The compliant classes are based on the OWL base ontology, but adapted to NEAT and use in CDF.
|
|
112
|
-
One thing to note is that this method would not be able to fix issues with class ids which
|
|
113
|
-
are not compliant with the CDF naming convention. For example, if a class id contains a space,
|
|
114
|
-
starts with a number, etc. This will cause issues when trying to create the class in CDF.
|
|
115
|
-
"""
|
|
116
|
-
|
|
117
|
-
# Replace empty or non-string values in "Match" column with "exact"
|
|
118
|
-
classes["Match Type"] = classes["Match Type"].fillna(MatchType.exact)
|
|
119
|
-
classes["Match Type"] = classes["Match Type"].apply(
|
|
120
|
-
lambda x: MatchType.exact if not isinstance(x, str) or len(x) == 0 else x
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Replace empty or non-string values in "Comment" column with a default value
|
|
124
|
-
classes["Comment"] = classes["Comment"].fillna("Imported from Ontology by NEAT")
|
|
125
|
-
classes["Comment"] = classes["Comment"].apply(
|
|
126
|
-
lambda x: "Imported from Ontology by NEAT" if not isinstance(x, str) or len(x) == 0 else x
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
# Add _object_property_class, _data_type_property_class, _thing_class to the dataframe
|
|
130
|
-
classes = pd.concat(
|
|
131
|
-
[classes, pd.DataFrame([_object_property_class(), _data_type_property_class(), _thing_class()])],
|
|
132
|
-
ignore_index=True,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
# Reduce length of elements in the "Description" column to 1024 characters
|
|
136
|
-
classes["Description"] = classes["Description"].apply(lambda x: x[:1024] if isinstance(x, str) else None)
|
|
137
|
-
|
|
138
|
-
# Add missing parent classes to the dataframe
|
|
139
|
-
classes = pd.concat(
|
|
140
|
-
[classes, pd.DataFrame(_add_parent_class(classes))],
|
|
141
|
-
ignore_index=True,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
return classes
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def _object_property_class() -> dict:
|
|
148
|
-
return {
|
|
149
|
-
"Class": "ObjectProperty",
|
|
150
|
-
"Name": None,
|
|
151
|
-
"Description": "The class of object properties.",
|
|
152
|
-
"Parent Class": None,
|
|
153
|
-
"Reference": OWL.ObjectProperty,
|
|
154
|
-
"Match Type": MatchType.exact,
|
|
155
|
-
"Comment": "Added by NEAT based on owl:ObjectProperty but adapted to NEAT and use in CDF.",
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _data_type_property_class() -> dict:
|
|
160
|
-
return {
|
|
161
|
-
"Class": "DatatypeProperty",
|
|
162
|
-
"Name": None,
|
|
163
|
-
"Description": "The class of data properties.",
|
|
164
|
-
"Parent Class": None,
|
|
165
|
-
"Reference": OWL.DatatypeProperty,
|
|
166
|
-
"Match Type": MatchType.exact,
|
|
167
|
-
"Comment": "Added by NEAT based on owl:DatatypeProperty but adapted to NEAT and use in CDF.",
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def _thing_class() -> dict:
|
|
172
|
-
return {
|
|
173
|
-
"Class": "Thing",
|
|
174
|
-
"Name": None,
|
|
175
|
-
"Description": "The class of holding class individuals.",
|
|
176
|
-
"Parent Class": None,
|
|
177
|
-
"Reference": OWL.Thing,
|
|
178
|
-
"Match Type": MatchType.exact,
|
|
179
|
-
"Comment": (
|
|
180
|
-
"Added by NEAT. "
|
|
181
|
-
"Imported from OWL base ontology, it is meant for use as a default"
|
|
182
|
-
" value type for object properties which miss a declared range."
|
|
183
|
-
),
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def _add_parent_class(df: pd.DataFrame) -> list[dict]:
|
|
188
|
-
parent_set = {
|
|
189
|
-
item
|
|
190
|
-
for sublist in df["Parent Class"].tolist()
|
|
191
|
-
if sublist
|
|
192
|
-
for item in sublist
|
|
193
|
-
if item != "" and item is not None
|
|
194
|
-
}
|
|
195
|
-
class_set = set(df["Class"].tolist())
|
|
196
|
-
|
|
197
|
-
rows = []
|
|
198
|
-
for missing_parent_class in parent_set.difference(class_set):
|
|
199
|
-
rows += [
|
|
200
|
-
{
|
|
201
|
-
"Class": missing_parent_class,
|
|
202
|
-
"Name": None,
|
|
203
|
-
"Description": None,
|
|
204
|
-
"Parent Class": None,
|
|
205
|
-
"Reference": None,
|
|
206
|
-
"Match Type": None,
|
|
207
|
-
"Comment": (
|
|
208
|
-
"Added by NEAT. "
|
|
209
|
-
"This is a parent class that is missing in the ontology. "
|
|
210
|
-
"It is added by NEAT to make the ontology compliant with CDF."
|
|
211
|
-
),
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
|
|
215
|
-
return rows
|