cognite-neat 0.99.1__py3-none-any.whl → 0.100.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/_api/data_modeling_loaders.py +383 -182
- cognite/neat/_client/data_classes/data_modeling.py +4 -0
- cognite/neat/_graph/extractors/_base.py +7 -0
- cognite/neat/_graph/extractors/_classic_cdf/_classic.py +23 -13
- cognite/neat/_graph/loaders/_rdf2dms.py +50 -11
- cognite/neat/_graph/transformers/__init__.py +3 -3
- cognite/neat/_graph/transformers/_classic_cdf.py +120 -52
- cognite/neat/_issues/warnings/__init__.py +2 -0
- cognite/neat/_issues/warnings/_resources.py +15 -0
- cognite/neat/_rules/analysis/_base.py +15 -5
- cognite/neat/_rules/analysis/_dms.py +20 -0
- cognite/neat/_rules/analysis/_information.py +22 -0
- cognite/neat/_rules/exporters/_base.py +3 -5
- cognite/neat/_rules/exporters/_rules2dms.py +190 -198
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +22 -5
- cognite/neat/_rules/models/_base_rules.py +19 -0
- cognite/neat/_rules/models/_types.py +5 -0
- cognite/neat/_rules/models/dms/_exporter.py +215 -93
- cognite/neat/_rules/models/dms/_rules.py +4 -4
- cognite/neat/_rules/models/dms/_rules_input.py +8 -3
- cognite/neat/_rules/models/dms/_validation.py +42 -11
- cognite/neat/_rules/models/entities/_multi_value.py +3 -0
- cognite/neat/_rules/models/information/_rules.py +17 -2
- cognite/neat/_rules/models/information/_rules_input.py +11 -2
- cognite/neat/_rules/models/information/_validation.py +99 -3
- cognite/neat/_rules/models/mapping/_classic2core.yaml +1 -1
- cognite/neat/_rules/transformers/__init__.py +2 -1
- cognite/neat/_rules/transformers/_converters.py +163 -61
- cognite/neat/_rules/transformers/_mapping.py +132 -2
- cognite/neat/_session/_base.py +42 -31
- cognite/neat/_session/_mapping.py +105 -5
- cognite/neat/_session/_prepare.py +43 -9
- cognite/neat/_session/_read.py +50 -4
- cognite/neat/_session/_set.py +1 -0
- cognite/neat/_session/_to.py +34 -11
- cognite/neat/_session/_wizard.py +5 -0
- cognite/neat/_session/engine/_interface.py +3 -2
- cognite/neat/_store/_base.py +79 -19
- cognite/neat/_utils/collection_.py +22 -0
- cognite/neat/_utils/rdf_.py +24 -0
- cognite/neat/_version.py +2 -2
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +3 -3
- {cognite_neat-0.99.1.dist-info → cognite_neat-0.100.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.99.1.dist-info → cognite_neat-0.100.0.dist-info}/RECORD +47 -47
- {cognite_neat-0.99.1.dist-info → cognite_neat-0.100.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.99.1.dist-info → cognite_neat-0.100.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.99.1.dist-info → cognite_neat-0.100.0.dist-info}/entry_points.txt +0 -0
|
@@ -33,8 +33,8 @@ class InformationInputMetadata(InputComponent[InformationMetadata]):
|
|
|
33
33
|
description: str | None = None
|
|
34
34
|
created: datetime | str | None = None
|
|
35
35
|
updated: datetime | str | None = None
|
|
36
|
-
physical: str | None = None
|
|
37
|
-
conceptual: str | None = None
|
|
36
|
+
physical: str | URIRef | None = None
|
|
37
|
+
conceptual: str | URIRef | None = None
|
|
38
38
|
|
|
39
39
|
@classmethod
|
|
40
40
|
def _get_verified_cls(cls) -> type[InformationMetadata]:
|
|
@@ -81,6 +81,11 @@ class InformationInputProperty(InputComponent[InformationProperty]):
|
|
|
81
81
|
transformation: str | None = None
|
|
82
82
|
# Only used internally
|
|
83
83
|
inherited: bool = False
|
|
84
|
+
neatId: str | URIRef | None = None
|
|
85
|
+
|
|
86
|
+
# linking
|
|
87
|
+
physical: str | URIRef | None = None
|
|
88
|
+
conceptual: str | URIRef | None = None
|
|
84
89
|
|
|
85
90
|
@classmethod
|
|
86
91
|
def _get_verified_cls(cls) -> type[InformationProperty]:
|
|
@@ -99,6 +104,10 @@ class InformationInputClass(InputComponent[InformationClass]):
|
|
|
99
104
|
name: str | None = None
|
|
100
105
|
description: str | None = None
|
|
101
106
|
implements: str | list[ClassEntity] | None = None
|
|
107
|
+
neatId: str | URIRef | None = None
|
|
108
|
+
# linking
|
|
109
|
+
physical: str | URIRef | None = None
|
|
110
|
+
conceptual: str | URIRef | None = None
|
|
102
111
|
|
|
103
112
|
@classmethod
|
|
104
113
|
def _get_verified_cls(cls) -> type[InformationClass]:
|
|
@@ -3,10 +3,15 @@ from collections import Counter
|
|
|
3
3
|
|
|
4
4
|
from cognite.neat._issues import IssueList
|
|
5
5
|
from cognite.neat._issues.errors import NeatValueError
|
|
6
|
+
from cognite.neat._issues.errors._resources import ResourceNotDefinedError
|
|
6
7
|
from cognite.neat._issues.warnings._models import UndefinedClassWarning
|
|
7
|
-
from cognite.neat._issues.warnings._resources import
|
|
8
|
-
|
|
8
|
+
from cognite.neat._issues.warnings._resources import (
|
|
9
|
+
ResourceNotDefinedWarning,
|
|
10
|
+
ResourceRegexViolationWarning,
|
|
11
|
+
)
|
|
12
|
+
from cognite.neat._rules._constants import PATTERNS, EntityTypes
|
|
9
13
|
from cognite.neat._rules.models.entities import ClassEntity, UnknownEntity
|
|
14
|
+
from cognite.neat._rules.models.entities._multi_value import MultiValueTypeInfo
|
|
10
15
|
|
|
11
16
|
from ._rules import InformationRules
|
|
12
17
|
|
|
@@ -25,14 +30,15 @@ class InformationValidation:
|
|
|
25
30
|
def validate(self) -> IssueList:
|
|
26
31
|
self._namespaces_reassigned()
|
|
27
32
|
self._classes_without_properties()
|
|
33
|
+
self._undefined_classes()
|
|
28
34
|
self._parent_class_defined()
|
|
29
35
|
self._referenced_classes_exist()
|
|
30
36
|
self._referenced_value_types_exist()
|
|
37
|
+
self._regex_compliance_with_dms()
|
|
31
38
|
|
|
32
39
|
return self.issue_list
|
|
33
40
|
|
|
34
41
|
def _classes_without_properties(self) -> None:
|
|
35
|
-
# needs to be complete for this validation to pass
|
|
36
42
|
defined_classes = {class_.class_ for class_ in self.classes}
|
|
37
43
|
referred_classes = {property_.class_ for property_ in self.properties}
|
|
38
44
|
class_parent_pairs = self._class_parent_pairs()
|
|
@@ -51,6 +57,20 @@ class InformationValidation:
|
|
|
51
57
|
)
|
|
52
58
|
)
|
|
53
59
|
|
|
60
|
+
def _undefined_classes(self) -> None:
|
|
61
|
+
defined_classes = {class_.class_ for class_ in self.classes}
|
|
62
|
+
referred_classes = {property_.class_ for property_ in self.properties}
|
|
63
|
+
|
|
64
|
+
if undefined_classes := referred_classes.difference(defined_classes):
|
|
65
|
+
for class_ in undefined_classes:
|
|
66
|
+
self.issue_list.append(
|
|
67
|
+
ResourceNotDefinedError(
|
|
68
|
+
identifier=class_,
|
|
69
|
+
resource_type="class",
|
|
70
|
+
location="Classes sheet",
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
54
74
|
def _parent_class_defined(self) -> None:
|
|
55
75
|
"""This is a validation to check if the parent class of a class is defined in the classes sheet."""
|
|
56
76
|
class_parent_pairs = self._class_parent_pairs()
|
|
@@ -106,6 +126,82 @@ class InformationValidation:
|
|
|
106
126
|
)
|
|
107
127
|
)
|
|
108
128
|
|
|
129
|
+
def _regex_compliance_with_dms(self) -> None:
|
|
130
|
+
"""Check regex compliance with DMS of properties, classes and value types."""
|
|
131
|
+
|
|
132
|
+
for prop_ in self.properties:
|
|
133
|
+
if not PATTERNS.dms_property_id_compliance.match(prop_.property_):
|
|
134
|
+
self.issue_list.append(
|
|
135
|
+
ResourceRegexViolationWarning(
|
|
136
|
+
prop_.property_,
|
|
137
|
+
"Property",
|
|
138
|
+
"Properties sheet, Property column",
|
|
139
|
+
PATTERNS.dms_property_id_compliance.pattern,
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
if not PATTERNS.view_id_compliance.match(prop_.class_.suffix):
|
|
143
|
+
self.issue_list.append(
|
|
144
|
+
ResourceRegexViolationWarning(
|
|
145
|
+
prop_.class_,
|
|
146
|
+
"Class",
|
|
147
|
+
"Properties sheet, Class column",
|
|
148
|
+
PATTERNS.view_id_compliance.pattern,
|
|
149
|
+
)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Handling Value Type
|
|
153
|
+
if (
|
|
154
|
+
isinstance(prop_.value_type, ClassEntity)
|
|
155
|
+
and prop_.value_type != UnknownEntity()
|
|
156
|
+
and not PATTERNS.view_id_compliance.match(prop_.value_type.suffix)
|
|
157
|
+
):
|
|
158
|
+
self.issue_list.append(
|
|
159
|
+
ResourceRegexViolationWarning(
|
|
160
|
+
prop_.value_type,
|
|
161
|
+
"Value Type",
|
|
162
|
+
"Properties sheet, Value Type column",
|
|
163
|
+
PATTERNS.view_id_compliance.pattern,
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
if isinstance(prop_.value_type, MultiValueTypeInfo):
|
|
167
|
+
for value_type in prop_.value_type.types:
|
|
168
|
+
if (
|
|
169
|
+
isinstance(prop_.value_type, ClassEntity)
|
|
170
|
+
and prop_.value_type != UnknownEntity()
|
|
171
|
+
and not PATTERNS.view_id_compliance.match(value_type.suffix)
|
|
172
|
+
):
|
|
173
|
+
self.issue_list.append(
|
|
174
|
+
ResourceRegexViolationWarning(
|
|
175
|
+
value_type,
|
|
176
|
+
"Value Type",
|
|
177
|
+
"Properties sheet, Value Type column",
|
|
178
|
+
PATTERNS.view_id_compliance.pattern,
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
for class_ in self.classes:
|
|
183
|
+
if not PATTERNS.view_id_compliance.match(class_.class_.suffix):
|
|
184
|
+
self.issue_list.append(
|
|
185
|
+
ResourceRegexViolationWarning(
|
|
186
|
+
class_.class_,
|
|
187
|
+
"Class",
|
|
188
|
+
"Classes sheet, Class column",
|
|
189
|
+
PATTERNS.view_id_compliance.pattern,
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
if class_.implements:
|
|
194
|
+
for parent in class_.implements:
|
|
195
|
+
if not PATTERNS.view_id_compliance.match(parent.suffix):
|
|
196
|
+
self.issue_list.append(
|
|
197
|
+
ResourceRegexViolationWarning(
|
|
198
|
+
parent,
|
|
199
|
+
"Class",
|
|
200
|
+
"Classes sheet, Implements column",
|
|
201
|
+
PATTERNS.view_id_compliance.pattern,
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
|
|
109
205
|
def _class_parent_pairs(self) -> dict[ClassEntity, list[ClassEntity]]:
|
|
110
206
|
class_parent_pairs: dict[ClassEntity, list[ClassEntity]] = {}
|
|
111
207
|
classes = self.rules.model_copy(deep=True).classes
|
|
@@ -316,7 +316,7 @@ views:
|
|
|
316
316
|
in_model: true
|
|
317
317
|
view: cdf_cdm:CogniteSourceSystem(version=v1)
|
|
318
318
|
- description: Represents a single unit of measurement
|
|
319
|
-
implements: CogniteDescribable
|
|
319
|
+
implements: cdf_cdm:CogniteDescribable(version=v1)
|
|
320
320
|
in_model: true
|
|
321
321
|
view: cdf_cdm:CogniteUnit(version=v1)
|
|
322
322
|
- description: Assets represent systems that support industrial functions or processes.
|
|
@@ -9,7 +9,7 @@ from ._converters import (
|
|
|
9
9
|
ToCompliantEntities,
|
|
10
10
|
ToExtension,
|
|
11
11
|
)
|
|
12
|
-
from ._mapping import MapOneToOne, RuleMapper
|
|
12
|
+
from ._mapping import AsParentPropertyId, MapOneToOne, RuleMapper
|
|
13
13
|
from ._pipelines import ImporterPipeline
|
|
14
14
|
from ._verification import VerifyAnyRules, VerifyDMSRules, VerifyInformationRules
|
|
15
15
|
|
|
@@ -30,4 +30,5 @@ __all__ = [
|
|
|
30
30
|
"ToExtension",
|
|
31
31
|
"ReduceCogniteModel",
|
|
32
32
|
"SetIDDMSModel",
|
|
33
|
+
"AsParentPropertyId",
|
|
33
34
|
]
|
|
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from collections import Counter, defaultdict
|
|
5
5
|
from collections.abc import Collection, Mapping
|
|
6
6
|
from datetime import date, datetime
|
|
7
|
-
from typing import Literal, TypeVar, cast, overload
|
|
7
|
+
from typing import ClassVar, Literal, TypeVar, cast, overload
|
|
8
8
|
|
|
9
9
|
from cognite.client.data_classes import data_modeling as dms
|
|
10
10
|
from cognite.client.data_classes.data_modeling import DataModelId, DataModelIdentifier, ViewId
|
|
@@ -15,6 +15,7 @@ from cognite.neat._constants import (
|
|
|
15
15
|
)
|
|
16
16
|
from cognite.neat._issues._base import IssueList
|
|
17
17
|
from cognite.neat._issues.errors import NeatValueError
|
|
18
|
+
from cognite.neat._issues.warnings import NeatValueWarning
|
|
18
19
|
from cognite.neat._issues.warnings._models import (
|
|
19
20
|
EnterpriseModelNotBuildOnTopOfCDMWarning,
|
|
20
21
|
SolutionModelBuildOnTopOfCDMWarning,
|
|
@@ -34,7 +35,7 @@ from cognite.neat._rules.models import (
|
|
|
34
35
|
SheetList,
|
|
35
36
|
data_types,
|
|
36
37
|
)
|
|
37
|
-
from cognite.neat._rules.models.data_types import DataType, String
|
|
38
|
+
from cognite.neat._rules.models.data_types import AnyURI, DataType, String
|
|
38
39
|
from cognite.neat._rules.models.dms import DMSMetadata, DMSProperty, DMSView
|
|
39
40
|
from cognite.neat._rules.models.dms._rules import DMSContainer
|
|
40
41
|
from cognite.neat._rules.models.entities import (
|
|
@@ -249,11 +250,12 @@ class PrefixEntities(RulesTransformer[InputRules, InputRules]): # type: ignore[
|
|
|
249
250
|
class InformationToDMS(ConversionTransformer[InformationRules, DMSRules]):
|
|
250
251
|
"""Converts InformationRules to DMSRules."""
|
|
251
252
|
|
|
252
|
-
def __init__(self, ignore_undefined_value_types: bool = False):
|
|
253
|
+
def __init__(self, ignore_undefined_value_types: bool = False, mode: Literal["edge_properties"] | None = None):
|
|
253
254
|
self.ignore_undefined_value_types = ignore_undefined_value_types
|
|
255
|
+
self.mode = mode
|
|
254
256
|
|
|
255
257
|
def _transform(self, rules: InformationRules) -> DMSRules:
|
|
256
|
-
return _InformationRulesConverter(rules).as_dms_rules(self.ignore_undefined_value_types)
|
|
258
|
+
return _InformationRulesConverter(rules).as_dms_rules(self.ignore_undefined_value_types, self.mode)
|
|
257
259
|
|
|
258
260
|
|
|
259
261
|
class DMSToInformation(ConversionTransformer[DMSRules, InformationRules]):
|
|
@@ -637,11 +639,15 @@ class ReduceCogniteModel(RulesTransformer[DMSRules, DMSRules]):
|
|
|
637
639
|
|
|
638
640
|
|
|
639
641
|
class _InformationRulesConverter:
|
|
642
|
+
_edge_properties: ClassVar[frozenset[str]] = frozenset({"endNode", "end_node", "startNode", "start_node"})
|
|
643
|
+
|
|
640
644
|
def __init__(self, information: InformationRules):
|
|
641
645
|
self.rules = information
|
|
642
646
|
self.property_count_by_container: dict[ContainerEntity, int] = defaultdict(int)
|
|
643
647
|
|
|
644
|
-
def as_dms_rules(
|
|
648
|
+
def as_dms_rules(
|
|
649
|
+
self, ignore_undefined_value_types: bool = False, mode: Literal["edge_properties"] | None = None
|
|
650
|
+
) -> "DMSRules":
|
|
645
651
|
from cognite.neat._rules.models.dms._rules import (
|
|
646
652
|
DMSContainer,
|
|
647
653
|
DMSProperty,
|
|
@@ -652,27 +658,55 @@ class _InformationRulesConverter:
|
|
|
652
658
|
info_metadata = self.rules.metadata
|
|
653
659
|
default_version = info_metadata.version
|
|
654
660
|
default_space = self._to_space(info_metadata.prefix)
|
|
655
|
-
|
|
661
|
+
dms_metadata = self._convert_metadata_to_dms(info_metadata)
|
|
662
|
+
edge_classes: set[ClassEntity] = set()
|
|
663
|
+
property_to_edge: dict[tuple[ClassEntity, str], ClassEntity] = {}
|
|
664
|
+
end_node_by_edge: dict[ClassEntity, ClassEntity] = {}
|
|
665
|
+
if mode == "edge_properties":
|
|
666
|
+
edge_classes = {
|
|
667
|
+
cls_.class_ for cls_ in self.rules.classes if cls_.implements and cls_.implements[0].suffix == "Edge"
|
|
668
|
+
}
|
|
669
|
+
property_to_edge = {
|
|
670
|
+
(prop.class_, prop.property_): prop.value_type
|
|
671
|
+
for prop in self.rules.properties
|
|
672
|
+
if prop.value_type in edge_classes and isinstance(prop.value_type, ClassEntity)
|
|
673
|
+
}
|
|
674
|
+
end_node_by_edge = {
|
|
675
|
+
prop.class_: prop.value_type
|
|
676
|
+
for prop in self.rules.properties
|
|
677
|
+
if prop.class_ in edge_classes
|
|
678
|
+
and (prop.property_ == "endNode" or prop.property_ == "end_node")
|
|
679
|
+
and isinstance(prop.value_type, ClassEntity)
|
|
680
|
+
}
|
|
656
681
|
|
|
657
682
|
properties_by_class: dict[ClassEntity, list[DMSProperty]] = defaultdict(list)
|
|
658
683
|
referenced_containers: dict[ContainerEntity, Counter[ClassEntity]] = defaultdict(Counter)
|
|
659
684
|
for prop in self.rules.properties:
|
|
660
685
|
if ignore_undefined_value_types and isinstance(prop.value_type, UnknownEntity):
|
|
661
686
|
continue
|
|
662
|
-
|
|
687
|
+
if prop.class_ in edge_classes and prop.property_ in self._edge_properties:
|
|
688
|
+
continue
|
|
689
|
+
dms_property = self._as_dms_property(
|
|
690
|
+
prop, default_space, default_version, edge_classes, property_to_edge, end_node_by_edge
|
|
691
|
+
)
|
|
663
692
|
properties_by_class[prop.class_].append(dms_property)
|
|
664
693
|
if dms_property.container:
|
|
665
694
|
referenced_containers[dms_property.container][prop.class_] += 1
|
|
666
695
|
|
|
667
|
-
views: list[DMSView] = [
|
|
668
|
-
|
|
696
|
+
views: list[DMSView] = []
|
|
697
|
+
|
|
698
|
+
for cls_ in self.rules.classes:
|
|
699
|
+
dms_view = DMSView(
|
|
669
700
|
name=cls_.name,
|
|
670
701
|
view=cls_.class_.as_view_entity(default_space, default_version),
|
|
671
702
|
description=cls_.description,
|
|
672
|
-
implements=self._get_view_implements(cls_, info_metadata),
|
|
703
|
+
implements=self._get_view_implements(cls_, info_metadata, mode),
|
|
673
704
|
)
|
|
674
|
-
|
|
675
|
-
|
|
705
|
+
|
|
706
|
+
dms_view.logical = cls_.neatId
|
|
707
|
+
cls_.physical = dms_view.neatId
|
|
708
|
+
|
|
709
|
+
views.append(dms_view)
|
|
676
710
|
|
|
677
711
|
class_by_entity = {cls_.class_: cls_ for cls_ in self.rules.classes}
|
|
678
712
|
|
|
@@ -696,7 +730,7 @@ class _InformationRulesConverter:
|
|
|
696
730
|
containers.append(container)
|
|
697
731
|
|
|
698
732
|
return DMSRules(
|
|
699
|
-
metadata=
|
|
733
|
+
metadata=dms_metadata,
|
|
700
734
|
properties=SheetList[DMSProperty]([prop for prop_set in properties_by_class.values() for prop in prop_set]),
|
|
701
735
|
views=SheetList[DMSView](views),
|
|
702
736
|
containers=SheetList[DMSContainer](containers),
|
|
@@ -724,7 +758,7 @@ class _InformationRulesConverter:
|
|
|
724
758
|
DMSMetadata,
|
|
725
759
|
)
|
|
726
760
|
|
|
727
|
-
|
|
761
|
+
dms_metadata = DMSMetadata(
|
|
728
762
|
space=metadata.space,
|
|
729
763
|
version=metadata.version,
|
|
730
764
|
external_id=metadata.external_id,
|
|
@@ -734,74 +768,138 @@ class _InformationRulesConverter:
|
|
|
734
768
|
updated=metadata.updated,
|
|
735
769
|
)
|
|
736
770
|
|
|
737
|
-
|
|
738
|
-
|
|
771
|
+
dms_metadata.logical = metadata.identifier
|
|
772
|
+
metadata.physical = dms_metadata.identifier
|
|
773
|
+
|
|
774
|
+
return dms_metadata
|
|
739
775
|
|
|
776
|
+
def _as_dms_property(
|
|
777
|
+
self,
|
|
778
|
+
info_property: InformationProperty,
|
|
779
|
+
default_space: str,
|
|
780
|
+
default_version: str,
|
|
781
|
+
edge_classes: set[ClassEntity],
|
|
782
|
+
property_to_edge: dict[tuple[ClassEntity, str], ClassEntity],
|
|
783
|
+
end_node_by_edge: dict[ClassEntity, ClassEntity],
|
|
784
|
+
) -> "DMSProperty":
|
|
740
785
|
from cognite.neat._rules.models.dms._rules import DMSProperty
|
|
741
786
|
|
|
742
787
|
# returns property type, which can be ObjectProperty or DatatypeProperty
|
|
743
|
-
value_type
|
|
788
|
+
value_type = self._get_value_type(
|
|
789
|
+
info_property,
|
|
790
|
+
default_space,
|
|
791
|
+
default_version,
|
|
792
|
+
edge_classes,
|
|
793
|
+
end_node_by_edge,
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
connection = self._get_connection(info_property, value_type, property_to_edge, default_space, default_version)
|
|
797
|
+
|
|
798
|
+
container: ContainerEntity | None = None
|
|
799
|
+
container_property: str | None = None
|
|
800
|
+
is_list: bool | None = info_property.is_list
|
|
801
|
+
nullable: bool | None = not info_property.is_mandatory
|
|
802
|
+
if isinstance(connection, EdgeEntity):
|
|
803
|
+
nullable = None
|
|
804
|
+
elif connection == "direct":
|
|
805
|
+
nullable = True
|
|
806
|
+
container, container_property = self._get_container(info_property, default_space)
|
|
807
|
+
else:
|
|
808
|
+
container, container_property = self._get_container(info_property, default_space)
|
|
809
|
+
|
|
810
|
+
dms_property = DMSProperty(
|
|
811
|
+
name=info_property.name,
|
|
812
|
+
value_type=value_type,
|
|
813
|
+
nullable=nullable,
|
|
814
|
+
is_list=is_list,
|
|
815
|
+
connection=connection,
|
|
816
|
+
default=info_property.default,
|
|
817
|
+
container=container,
|
|
818
|
+
container_property=container_property,
|
|
819
|
+
view=info_property.class_.as_view_entity(default_space, default_version),
|
|
820
|
+
view_property=info_property.property_,
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
# linking
|
|
824
|
+
dms_property.logical = info_property.neatId
|
|
825
|
+
info_property.physical = dms_property.neatId
|
|
826
|
+
|
|
827
|
+
return dms_property
|
|
828
|
+
|
|
829
|
+
@staticmethod
|
|
830
|
+
def _get_connection(
|
|
831
|
+
prop: InformationProperty,
|
|
832
|
+
value_type: DataType | ViewEntity | DMSUnknownEntity,
|
|
833
|
+
property_to_edge: dict[tuple[ClassEntity, str], ClassEntity],
|
|
834
|
+
default_space: str,
|
|
835
|
+
default_version: str,
|
|
836
|
+
) -> Literal["direct"] | ReverseConnectionEntity | EdgeEntity | None:
|
|
837
|
+
if isinstance(value_type, ViewEntity) and (prop.class_, prop.property_) in property_to_edge:
|
|
838
|
+
edge_properties = property_to_edge[(prop.class_, prop.property_)]
|
|
839
|
+
return EdgeEntity(properties=edge_properties.as_view_entity(default_space, default_version))
|
|
840
|
+
if isinstance(value_type, ViewEntity) and prop.is_list:
|
|
841
|
+
return EdgeEntity()
|
|
842
|
+
elif isinstance(value_type, ViewEntity):
|
|
843
|
+
return "direct"
|
|
844
|
+
# defaulting to direct connection
|
|
845
|
+
elif isinstance(value_type, DMSUnknownEntity):
|
|
846
|
+
return "direct"
|
|
847
|
+
return None
|
|
848
|
+
|
|
849
|
+
def _get_value_type(
|
|
850
|
+
self,
|
|
851
|
+
prop: InformationProperty,
|
|
852
|
+
default_space: str,
|
|
853
|
+
default_version: str,
|
|
854
|
+
edge_classes: set[ClassEntity],
|
|
855
|
+
end_node_by_edge: dict[ClassEntity, ClassEntity],
|
|
856
|
+
) -> DataType | ViewEntity | DMSUnknownEntity:
|
|
744
857
|
if isinstance(prop.value_type, DataType):
|
|
745
|
-
|
|
858
|
+
return prop.value_type
|
|
746
859
|
|
|
747
860
|
# UnknownEntity should resolve to DMSUnknownEntity
|
|
748
861
|
# meaning end node type is unknown
|
|
749
862
|
elif isinstance(prop.value_type, UnknownEntity):
|
|
750
|
-
|
|
751
|
-
|
|
863
|
+
return DMSUnknownEntity()
|
|
864
|
+
|
|
865
|
+
elif isinstance(prop.value_type, ClassEntity) and (prop.value_type in edge_classes):
|
|
866
|
+
if prop.value_type in end_node_by_edge:
|
|
867
|
+
return end_node_by_edge[prop.value_type].as_view_entity(default_space, default_version)
|
|
868
|
+
warnings.warn(
|
|
869
|
+
NeatValueWarning(
|
|
870
|
+
f"Edge class {prop.value_type} does not have 'endNode' property, defaulting to DMSUnknownEntity"
|
|
871
|
+
),
|
|
872
|
+
stacklevel=2,
|
|
873
|
+
)
|
|
874
|
+
return DMSUnknownEntity()
|
|
752
875
|
elif isinstance(prop.value_type, ClassEntity):
|
|
753
|
-
|
|
876
|
+
return prop.value_type.as_view_entity(default_space, default_version)
|
|
754
877
|
|
|
755
878
|
elif isinstance(prop.value_type, MultiValueTypeInfo):
|
|
756
879
|
# Multi Object type should resolve to DMSUnknownEntity
|
|
757
880
|
# meaning end node type is unknown
|
|
758
881
|
if prop.value_type.is_multi_object_type():
|
|
759
|
-
|
|
882
|
+
non_unknown = [type_ for type_ in prop.value_type.types if isinstance(type_, UnknownEntity)]
|
|
883
|
+
if list(non_unknown) == 1:
|
|
884
|
+
#
|
|
885
|
+
return non_unknown[0].as_view_entity(default_space, default_version)
|
|
886
|
+
return DMSUnknownEntity()
|
|
760
887
|
|
|
761
888
|
# Multi Data type should resolve to a single data type, or it should
|
|
762
889
|
elif prop.value_type.is_multi_data_type():
|
|
763
|
-
|
|
890
|
+
return self.convert_multi_data_type(prop.value_type)
|
|
764
891
|
|
|
765
892
|
# Mixed types default to string
|
|
766
893
|
else:
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
894
|
+
non_any_uri = [type_ for type_ in prop.value_type.types if type_ != AnyURI()]
|
|
895
|
+
if list(non_any_uri) == 1:
|
|
896
|
+
if isinstance(non_any_uri[0], ClassEntity):
|
|
897
|
+
return non_any_uri[0].as_view_entity(default_space, default_version)
|
|
898
|
+
else:
|
|
899
|
+
return non_any_uri[0]
|
|
900
|
+
return String()
|
|
771
901
|
|
|
772
|
-
|
|
773
|
-
if isinstance(value_type, ViewEntity):
|
|
774
|
-
# Default connection type.
|
|
775
|
-
connection = EdgeEntity() if prop.is_list else "direct"
|
|
776
|
-
|
|
777
|
-
# defaulting to direct connection
|
|
778
|
-
elif isinstance(value_type, DMSUnknownEntity):
|
|
779
|
-
connection = "direct"
|
|
780
|
-
|
|
781
|
-
container: ContainerEntity | None = None
|
|
782
|
-
container_property: str | None = None
|
|
783
|
-
is_list: bool | None = prop.is_list
|
|
784
|
-
nullable: bool | None = not prop.is_mandatory
|
|
785
|
-
if isinstance(connection, EdgeEntity):
|
|
786
|
-
nullable = None
|
|
787
|
-
elif connection == "direct":
|
|
788
|
-
nullable = True
|
|
789
|
-
container, container_property = self._get_container(prop, default_space)
|
|
790
|
-
else:
|
|
791
|
-
container, container_property = self._get_container(prop, default_space)
|
|
792
|
-
|
|
793
|
-
return DMSProperty(
|
|
794
|
-
name=prop.name,
|
|
795
|
-
value_type=value_type,
|
|
796
|
-
nullable=nullable,
|
|
797
|
-
is_list=is_list,
|
|
798
|
-
connection=connection,
|
|
799
|
-
default=prop.default,
|
|
800
|
-
container=container,
|
|
801
|
-
container_property=container_property,
|
|
802
|
-
view=prop.class_.as_view_entity(default_space, default_version),
|
|
803
|
-
view_property=prop.property_,
|
|
804
|
-
)
|
|
902
|
+
raise ValueError(f"Unsupported value type: {prop.value_type.type_}")
|
|
805
903
|
|
|
806
904
|
@classmethod
|
|
807
905
|
def _to_space(cls, prefix: str) -> str:
|
|
@@ -823,9 +921,13 @@ class _InformationRulesConverter:
|
|
|
823
921
|
self.property_count_by_container[container_entity] += 1
|
|
824
922
|
return container_entity, prop.property_
|
|
825
923
|
|
|
826
|
-
def _get_view_implements(
|
|
924
|
+
def _get_view_implements(
|
|
925
|
+
self, cls_: InformationClass, metadata: InformationMetadata, mode: Literal["edge_properties"] | None
|
|
926
|
+
) -> list[ViewEntity]:
|
|
827
927
|
implements = []
|
|
828
928
|
for parent in cls_.implements or []:
|
|
929
|
+
if mode == "edge_properties" and parent.suffix == "Edge":
|
|
930
|
+
continue
|
|
829
931
|
view_entity = parent.as_view_entity(metadata.prefix, metadata.version)
|
|
830
932
|
implements.append(view_entity)
|
|
831
933
|
return implements
|