cognite-neat 0.88.2__py3-none-any.whl → 0.89.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/_version.py +1 -1
- cognite/neat/constants.py +3 -0
- cognite/neat/graph/__init__.py +0 -3
- cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
- cognite/neat/graph/loaders/_base.py +3 -3
- cognite/neat/graph/loaders/_rdf2asset.py +24 -25
- cognite/neat/graph/loaders/_rdf2dms.py +20 -15
- cognite/neat/issues/__init__.py +1 -3
- cognite/neat/issues/_base.py +261 -71
- cognite/neat/issues/errors/__init__.py +73 -0
- cognite/neat/issues/errors/_external.py +67 -0
- cognite/neat/issues/errors/_general.py +35 -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/issues/formatters.py +1 -1
- 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 +53 -2
- cognite/neat/rules/analysis/_base.py +1 -1
- cognite/neat/rules/exporters/_base.py +7 -18
- cognite/neat/rules/exporters/_rules2dms.py +17 -20
- cognite/neat/rules/exporters/_rules2excel.py +9 -16
- cognite/neat/rules/exporters/_rules2ontology.py +77 -64
- cognite/neat/rules/exporters/_rules2yaml.py +6 -9
- cognite/neat/rules/exporters/_validation.py +11 -96
- cognite/neat/rules/importers/_base.py +9 -58
- cognite/neat/rules/importers/_dms2rules.py +188 -135
- cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
- cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
- cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
- cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +12 -19
- cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
- cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
- cognite/neat/rules/importers/_rdf/_shared.py +4 -4
- cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
- cognite/neat/rules/importers/_yaml2rules.py +32 -58
- cognite/neat/rules/models/__init__.py +21 -5
- cognite/neat/rules/models/_base_input.py +162 -0
- cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
- cognite/neat/rules/models/_rdfpath.py +4 -4
- cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
- cognite/neat/rules/models/asset/__init__.py +5 -2
- cognite/neat/rules/models/asset/_rules.py +3 -23
- cognite/neat/rules/models/asset/_rules_input.py +40 -115
- cognite/neat/rules/models/asset/_validation.py +14 -10
- cognite/neat/rules/models/data_types.py +150 -44
- cognite/neat/rules/models/dms/__init__.py +19 -7
- cognite/neat/rules/models/dms/_exporter.py +102 -34
- cognite/neat/rules/models/dms/_rules.py +65 -162
- cognite/neat/rules/models/dms/_rules_input.py +186 -254
- cognite/neat/rules/models/dms/_schema.py +87 -78
- cognite/neat/rules/models/dms/_serializer.py +44 -3
- cognite/neat/rules/models/dms/_validation.py +106 -68
- cognite/neat/rules/models/domain.py +52 -1
- cognite/neat/rules/models/entities/__init__.py +63 -0
- cognite/neat/rules/models/entities/_constants.py +73 -0
- cognite/neat/rules/models/entities/_loaders.py +76 -0
- cognite/neat/rules/models/entities/_multi_value.py +67 -0
- cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
- cognite/neat/rules/models/entities/_types.py +86 -0
- cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
- cognite/neat/rules/models/information/__init__.py +10 -2
- cognite/neat/rules/models/information/_rules.py +10 -22
- cognite/neat/rules/models/information/_rules_input.py +57 -204
- cognite/neat/rules/models/information/_validation.py +48 -25
- cognite/neat/rules/transformers/__init__.py +21 -0
- cognite/neat/rules/transformers/_base.py +81 -0
- cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -21
- cognite/neat/rules/transformers/_map_onto.py +97 -0
- cognite/neat/rules/transformers/_pipelines.py +61 -0
- cognite/neat/rules/transformers/_verification.py +136 -0
- cognite/neat/{graph/stores → store}/_provenance.py +10 -1
- cognite/neat/utils/auxiliary.py +2 -35
- cognite/neat/utils/cdf/data_classes.py +20 -0
- cognite/neat/utils/regex_patterns.py +6 -0
- 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 +116 -47
- cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
- cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
- cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
- cognite/neat/workflows/steps_registry.py +4 -5
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
- cognite/neat/exceptions.py +0 -145
- cognite/neat/graph/exceptions.py +0 -90
- cognite/neat/issues/errors/external.py +0 -21
- cognite/neat/issues/errors/properties.py +0 -75
- cognite/neat/issues/errors/resources.py +0 -123
- cognite/neat/issues/errors/schema.py +0 -0
- cognite/neat/issues/neat_warnings/__init__.py +0 -2
- cognite/neat/issues/neat_warnings/identifier.py +0 -27
- cognite/neat/issues/neat_warnings/models.py +0 -22
- cognite/neat/issues/neat_warnings/properties.py +0 -77
- cognite/neat/issues/neat_warnings/resources.py +0 -125
- cognite/neat/rules/issues/__init__.py +0 -22
- cognite/neat/rules/issues/base.py +0 -63
- cognite/neat/rules/issues/dms.py +0 -549
- cognite/neat/rules/issues/fileread.py +0 -197
- 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/rules/models/_constants.py +0 -1
- cognite/neat/rules/models/_types/__init__.py +0 -19
- cognite/neat/rules/models/asset/_converter.py +0 -4
- cognite/neat/rules/models/dms/_converter.py +0 -145
- cognite/neat/workflows/_exceptions.py +0 -41
- /cognite/neat/{graph/stores → store}/__init__.py +0 -0
- /cognite/neat/{graph/stores → store}/_base.py +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
- {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/entry_points.txt +0 -0
|
@@ -3,13 +3,24 @@ from typing import Any, ClassVar
|
|
|
3
3
|
|
|
4
4
|
from cognite.client import data_modeling as dm
|
|
5
5
|
|
|
6
|
-
from cognite.neat.
|
|
7
|
-
from cognite.neat.
|
|
8
|
-
from cognite.neat.
|
|
9
|
-
|
|
6
|
+
from cognite.neat.constants import DMS_CONTAINER_PROPERTY_SIZE_LIMIT
|
|
7
|
+
from cognite.neat.issues import IssueList, NeatError, NeatIssue, NeatIssueList
|
|
8
|
+
from cognite.neat.issues.errors import (
|
|
9
|
+
PropertyDefinitionDuplicatedError,
|
|
10
|
+
ResourceChangedError,
|
|
11
|
+
ResourceNotDefinedError,
|
|
12
|
+
)
|
|
13
|
+
from cognite.neat.issues.warnings import (
|
|
14
|
+
NotSupportedHasDataFilterLimitWarning,
|
|
15
|
+
NotSupportedViewContainerLimitWarning,
|
|
16
|
+
)
|
|
17
|
+
from cognite.neat.issues.warnings.user_modeling import (
|
|
18
|
+
NotNeatSupportedFilterWarning,
|
|
19
|
+
ViewPropertyLimitWarning,
|
|
20
|
+
)
|
|
21
|
+
from cognite.neat.rules.models._base_rules import DataModelType, ExtensionCategory, SchemaCompleteness
|
|
10
22
|
from cognite.neat.rules.models.data_types import DataType
|
|
11
|
-
from cognite.neat.rules.models.entities import ContainerEntity
|
|
12
|
-
from cognite.neat.rules.models.wrapped_entities import RawFilter
|
|
23
|
+
from cognite.neat.rules.models.entities import ContainerEntity, RawFilter
|
|
13
24
|
|
|
14
25
|
from ._rules import DMSProperty, DMSRules
|
|
15
26
|
from ._schema import DMSSchema
|
|
@@ -51,7 +62,7 @@ class DMSPostValidation:
|
|
|
51
62
|
if prop.container and prop.container_property:
|
|
52
63
|
container_properties_by_id[(prop.container, prop.container_property)].append((prop_no, prop))
|
|
53
64
|
|
|
54
|
-
errors: list[
|
|
65
|
+
errors: list[NeatError] = []
|
|
55
66
|
for (container, prop_name), properties in container_properties_by_id.items():
|
|
56
67
|
if len(properties) == 1:
|
|
57
68
|
continue
|
|
@@ -63,42 +74,78 @@ class DMSPostValidation:
|
|
|
63
74
|
is_all_direct = all(prop.connection == "direct" for _, prop in properties)
|
|
64
75
|
if len(value_types) > 1 and not is_all_direct:
|
|
65
76
|
errors.append(
|
|
66
|
-
|
|
77
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
67
78
|
container_id,
|
|
79
|
+
"container",
|
|
68
80
|
prop_name,
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
frozenset({v.dms._type if isinstance(v, DataType) else str(v) for v in value_types}),
|
|
82
|
+
tuple(row_numbers),
|
|
83
|
+
"rows",
|
|
71
84
|
)
|
|
72
85
|
)
|
|
73
86
|
list_definitions = {prop.is_list for _, prop in properties if prop.is_list is not None}
|
|
74
87
|
if len(list_definitions) > 1:
|
|
75
88
|
errors.append(
|
|
76
|
-
|
|
89
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
90
|
+
container_id,
|
|
91
|
+
"container",
|
|
92
|
+
prop_name,
|
|
93
|
+
frozenset(list_definitions),
|
|
94
|
+
tuple(row_numbers),
|
|
95
|
+
"rows",
|
|
96
|
+
)
|
|
77
97
|
)
|
|
78
98
|
nullable_definitions = {prop.nullable for _, prop in properties if prop.nullable is not None}
|
|
79
99
|
if len(nullable_definitions) > 1:
|
|
80
100
|
errors.append(
|
|
81
|
-
|
|
101
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
102
|
+
container_id,
|
|
103
|
+
"container",
|
|
104
|
+
prop_name,
|
|
105
|
+
frozenset(nullable_definitions),
|
|
106
|
+
tuple(row_numbers),
|
|
107
|
+
"rows",
|
|
108
|
+
)
|
|
82
109
|
)
|
|
83
110
|
default_definitions = {prop.default for _, prop in properties if prop.default is not None}
|
|
84
111
|
if len(default_definitions) > 1:
|
|
85
112
|
errors.append(
|
|
86
|
-
|
|
87
|
-
container_id,
|
|
113
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
114
|
+
container_id,
|
|
115
|
+
"container",
|
|
116
|
+
prop_name,
|
|
117
|
+
frozenset(
|
|
118
|
+
tuple(f"{k}:{v}" for k, v in def_.items()) if isinstance(def_, dict) else def_
|
|
119
|
+
for def_ in default_definitions
|
|
120
|
+
),
|
|
121
|
+
tuple(row_numbers),
|
|
122
|
+
"rows",
|
|
88
123
|
)
|
|
89
124
|
)
|
|
90
125
|
index_definitions = {",".join(prop.index) for _, prop in properties if prop.index is not None}
|
|
91
126
|
if len(index_definitions) > 1:
|
|
92
127
|
errors.append(
|
|
93
|
-
|
|
128
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
129
|
+
container_id,
|
|
130
|
+
"container",
|
|
131
|
+
prop_name,
|
|
132
|
+
frozenset(index_definitions),
|
|
133
|
+
tuple(row_numbers),
|
|
134
|
+
"rows",
|
|
135
|
+
)
|
|
94
136
|
)
|
|
95
137
|
constraint_definitions = {
|
|
96
138
|
",".join(prop.constraint) for _, prop in properties if prop.constraint is not None
|
|
97
139
|
}
|
|
98
140
|
if len(constraint_definitions) > 1:
|
|
99
141
|
errors.append(
|
|
100
|
-
|
|
101
|
-
container_id,
|
|
142
|
+
PropertyDefinitionDuplicatedError[dm.ContainerId](
|
|
143
|
+
container_id,
|
|
144
|
+
"container",
|
|
145
|
+
prop_name,
|
|
146
|
+
frozenset(constraint_definitions),
|
|
147
|
+
tuple(row_numbers),
|
|
148
|
+
"rows",
|
|
102
149
|
)
|
|
103
150
|
)
|
|
104
151
|
|
|
@@ -125,32 +172,25 @@ class DMSPostValidation:
|
|
|
125
172
|
defined_views |= {view.view.as_id() for view in self.rules.last.views}
|
|
126
173
|
|
|
127
174
|
property_count_by_view: dict[dm.ViewId, int] = defaultdict(int)
|
|
128
|
-
errors: list[
|
|
175
|
+
errors: list[NeatIssue] = []
|
|
129
176
|
for prop_no, prop in enumerate(self.properties):
|
|
130
177
|
view_id = prop.view.as_id()
|
|
131
178
|
if view_id not in defined_views:
|
|
132
179
|
errors.append(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
)
|
|
180
|
+
ResourceNotDefinedError[dm.ViewId](
|
|
181
|
+
identifier=view_id,
|
|
182
|
+
resource_type="view",
|
|
183
|
+
location="Views Sheet",
|
|
184
|
+
column_name="View",
|
|
185
|
+
row_number=prop_no,
|
|
186
|
+
sheet_name="Properties",
|
|
187
|
+
),
|
|
142
188
|
)
|
|
143
189
|
else:
|
|
144
190
|
property_count_by_view[view_id] += 1
|
|
145
191
|
for view_id, count in property_count_by_view.items():
|
|
146
|
-
if count >
|
|
147
|
-
errors.append(
|
|
148
|
-
issues.dms.ViewSizeWarning(
|
|
149
|
-
view_id=view_id,
|
|
150
|
-
limit=DMS_CONTAINER_SIZE_LIMIT,
|
|
151
|
-
count=count,
|
|
152
|
-
)
|
|
153
|
-
)
|
|
192
|
+
if count > DMS_CONTAINER_PROPERTY_SIZE_LIMIT:
|
|
193
|
+
errors.append(ViewPropertyLimitWarning(view_id, count))
|
|
154
194
|
if self.metadata.schema_ is SchemaCompleteness.complete:
|
|
155
195
|
defined_containers = {container.container.as_id() for container in self.containers or []}
|
|
156
196
|
if self.metadata.data_model_type == DataModelType.solution and self.rules.reference:
|
|
@@ -161,28 +201,26 @@ class DMSPostValidation:
|
|
|
161
201
|
for prop_no, prop in enumerate(self.properties):
|
|
162
202
|
if prop.container and (container_id := prop.container.as_id()) not in defined_containers:
|
|
163
203
|
errors.append(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
url=None,
|
|
204
|
+
ResourceNotDefinedError[dm.ContainerId](
|
|
205
|
+
identifier=container_id,
|
|
206
|
+
resource_type="container",
|
|
207
|
+
location="Containers Sheet",
|
|
208
|
+
column_name="Container",
|
|
209
|
+
row_number=prop_no,
|
|
210
|
+
sheet_name="Properties",
|
|
172
211
|
)
|
|
173
212
|
)
|
|
174
213
|
for _container_no, container in enumerate(self.containers or []):
|
|
175
214
|
for constraint_no, constraint in enumerate(container.constraint or []):
|
|
176
215
|
if constraint.as_id() not in defined_containers:
|
|
177
216
|
errors.append(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
url=None,
|
|
217
|
+
ResourceNotDefinedError[dm.ContainerId](
|
|
218
|
+
identifier=constraint.as_id(),
|
|
219
|
+
resource_type="container",
|
|
220
|
+
location="Containers Sheet",
|
|
221
|
+
column_name="Constraint",
|
|
222
|
+
row_number=constraint_no,
|
|
223
|
+
sheet_name="Properties",
|
|
186
224
|
)
|
|
187
225
|
)
|
|
188
226
|
self.issue_list.extend(errors)
|
|
@@ -212,10 +250,11 @@ class DMSPostValidation:
|
|
|
212
250
|
new_dumped, existing_dumped
|
|
213
251
|
)
|
|
214
252
|
self.issue_list.append(
|
|
215
|
-
|
|
216
|
-
container_id
|
|
217
|
-
|
|
218
|
-
|
|
253
|
+
ResourceChangedError(
|
|
254
|
+
container_id,
|
|
255
|
+
"container",
|
|
256
|
+
changed_properties=frozenset(changed_properties),
|
|
257
|
+
changed_attributes=frozenset(changed_attributes),
|
|
219
258
|
)
|
|
220
259
|
)
|
|
221
260
|
|
|
@@ -241,10 +280,11 @@ class DMSPostValidation:
|
|
|
241
280
|
# Only added new properties, no problem
|
|
242
281
|
continue
|
|
243
282
|
self.issue_list.append(
|
|
244
|
-
|
|
245
|
-
view_id
|
|
246
|
-
|
|
247
|
-
|
|
283
|
+
ResourceChangedError(
|
|
284
|
+
view_id,
|
|
285
|
+
"view",
|
|
286
|
+
changed_properties=frozenset(changed_properties),
|
|
287
|
+
changed_attributes=frozenset(changed_attributes),
|
|
248
288
|
)
|
|
249
289
|
)
|
|
250
290
|
|
|
@@ -254,9 +294,9 @@ class DMSPostValidation:
|
|
|
254
294
|
|
|
255
295
|
if mapped_containers and len(mapped_containers) > 10:
|
|
256
296
|
self.issue_list.append(
|
|
257
|
-
|
|
258
|
-
view_id
|
|
259
|
-
|
|
297
|
+
NotSupportedViewContainerLimitWarning(
|
|
298
|
+
view_id,
|
|
299
|
+
len(mapped_containers),
|
|
260
300
|
)
|
|
261
301
|
)
|
|
262
302
|
if (
|
|
@@ -265,9 +305,9 @@ class DMSPostValidation:
|
|
|
265
305
|
and len(view.filter.dump()["hasData"]) > 10
|
|
266
306
|
):
|
|
267
307
|
self.issue_list.append(
|
|
268
|
-
|
|
269
|
-
view_id
|
|
270
|
-
|
|
308
|
+
NotSupportedHasDataFilterLimitWarning(
|
|
309
|
+
view_id,
|
|
310
|
+
len(view.filter.dump()["hasData"]),
|
|
271
311
|
)
|
|
272
312
|
)
|
|
273
313
|
|
|
@@ -275,9 +315,7 @@ class DMSPostValidation:
|
|
|
275
315
|
for view in self.views:
|
|
276
316
|
if view.filter_ and isinstance(view.filter_, RawFilter):
|
|
277
317
|
self.issue_list.append(
|
|
278
|
-
|
|
279
|
-
view_id=view.view.as_id(),
|
|
280
|
-
)
|
|
318
|
+
NotNeatSupportedFilterWarning(view.view.as_id()),
|
|
281
319
|
)
|
|
282
320
|
|
|
283
321
|
@staticmethod
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from dataclasses import dataclass, field
|
|
2
3
|
from typing import Any, ClassVar
|
|
3
4
|
|
|
4
5
|
from pydantic import Field, field_serializer, field_validator, model_serializer
|
|
@@ -7,7 +8,8 @@ from pydantic_core.core_schema import SerializationInfo
|
|
|
7
8
|
from cognite.neat.rules.models.data_types import DataType
|
|
8
9
|
from cognite.neat.rules.models.entities import ClassEntity, ClassEntityList
|
|
9
10
|
|
|
10
|
-
from .
|
|
11
|
+
from ._base_input import InputComponent, InputRules
|
|
12
|
+
from ._base_rules import (
|
|
11
13
|
BaseMetadata,
|
|
12
14
|
BaseRules,
|
|
13
15
|
RoleTypes,
|
|
@@ -76,3 +78,52 @@ class DomainRules(BaseRules):
|
|
|
76
78
|
cls.model_dump(**kwargs) for cls in self.classes or []
|
|
77
79
|
] or None
|
|
78
80
|
return output
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class DomainInputMetadata(InputComponent[DomainMetadata]):
|
|
85
|
+
creator: str
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def _get_verified_cls(cls) -> type[DomainMetadata]:
|
|
89
|
+
return DomainMetadata
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class DomainInputProperty(InputComponent[DomainProperty]):
|
|
94
|
+
class_: str
|
|
95
|
+
property_: str
|
|
96
|
+
value_type: str
|
|
97
|
+
name: str | None = None
|
|
98
|
+
description: str | None = None
|
|
99
|
+
min_count: int | None = None
|
|
100
|
+
max_count: int | float | None = None
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def _get_verified_cls(cls) -> type[DomainProperty]:
|
|
104
|
+
return DomainProperty
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class DomainInputClass(InputComponent[DomainClass]):
|
|
109
|
+
class_: str
|
|
110
|
+
name: str | None = None
|
|
111
|
+
description: str | None = None
|
|
112
|
+
parent: list[str] | None = None
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def _get_verified_cls(cls) -> type[DomainClass]:
|
|
116
|
+
return DomainClass
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class DomainInputRules(InputRules[DomainRules]):
|
|
121
|
+
metadata: DomainInputMetadata
|
|
122
|
+
properties: list[DomainInputProperty] = field(default_factory=list)
|
|
123
|
+
classes: list[DomainInputClass] = field(default_factory=list)
|
|
124
|
+
last: "DomainInputRules | None" = None
|
|
125
|
+
reference: "DomainInputRules | None" = None
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def _get_verified_cls(cls) -> type[DomainRules]:
|
|
129
|
+
return DomainRules
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from ._constants import EntityTypes, Undefined, Unknown
|
|
2
|
+
from ._loaders import load_connection, load_dms_value_type, load_value_type
|
|
3
|
+
from ._multi_value import MultiValueTypeInfo
|
|
4
|
+
from ._single_value import (
|
|
5
|
+
AssetEntity,
|
|
6
|
+
AssetFields,
|
|
7
|
+
ClassEntity,
|
|
8
|
+
ContainerEntity,
|
|
9
|
+
DataModelEntity,
|
|
10
|
+
DMSEntity,
|
|
11
|
+
DMSNodeEntity,
|
|
12
|
+
DMSUnknownEntity,
|
|
13
|
+
DMSVersionedEntity,
|
|
14
|
+
EdgeEntity,
|
|
15
|
+
Entity,
|
|
16
|
+
ReferenceEntity,
|
|
17
|
+
RelationshipEntity,
|
|
18
|
+
ReverseConnectionEntity,
|
|
19
|
+
T_Entity,
|
|
20
|
+
UnitEntity,
|
|
21
|
+
UnknownEntity,
|
|
22
|
+
ViewEntity,
|
|
23
|
+
)
|
|
24
|
+
from ._types import CdfResourceEntityList, ClassEntityList, ContainerEntityList, URLEntity, ViewEntityList
|
|
25
|
+
from ._wrapped import DMSFilter, HasDataFilter, NodeTypeFilter, RawFilter, WrappedEntity
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"Entity",
|
|
29
|
+
"UnitEntity",
|
|
30
|
+
"DMSVersionedEntity",
|
|
31
|
+
"RelationshipEntity",
|
|
32
|
+
"EdgeEntity",
|
|
33
|
+
"ViewEntity",
|
|
34
|
+
"ClassEntity",
|
|
35
|
+
"ViewEntityList",
|
|
36
|
+
"UnknownEntity",
|
|
37
|
+
"EntityTypes",
|
|
38
|
+
"Unknown",
|
|
39
|
+
"Undefined",
|
|
40
|
+
"T_Entity",
|
|
41
|
+
"AssetEntity",
|
|
42
|
+
"AssetFields",
|
|
43
|
+
"ReverseConnectionEntity",
|
|
44
|
+
"MultiValueTypeInfo",
|
|
45
|
+
"DMSNodeEntity",
|
|
46
|
+
"DMSEntity",
|
|
47
|
+
"ContainerEntity",
|
|
48
|
+
"DMSUnknownEntity",
|
|
49
|
+
"DataModelEntity",
|
|
50
|
+
"ReferenceEntity",
|
|
51
|
+
"ClassEntityList",
|
|
52
|
+
"CdfResourceEntityList",
|
|
53
|
+
"ContainerEntityList",
|
|
54
|
+
"URLEntity",
|
|
55
|
+
"load_value_type",
|
|
56
|
+
"load_dms_value_type",
|
|
57
|
+
"load_connection",
|
|
58
|
+
"WrappedEntity",
|
|
59
|
+
"NodeTypeFilter",
|
|
60
|
+
"DMSFilter",
|
|
61
|
+
"HasDataFilter",
|
|
62
|
+
"RawFilter",
|
|
63
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
if sys.version_info >= (3, 11):
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
else:
|
|
9
|
+
from backports.strenum import StrEnum
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EntityTypes(StrEnum):
|
|
13
|
+
view_non_versioned = "view_non_versioned"
|
|
14
|
+
subject = "subject"
|
|
15
|
+
predicate = "predicate"
|
|
16
|
+
object = "object"
|
|
17
|
+
class_ = "class"
|
|
18
|
+
parent_class = "parent_class"
|
|
19
|
+
property_ = "property"
|
|
20
|
+
object_property = "ObjectProperty"
|
|
21
|
+
data_property = "DatatypeProperty"
|
|
22
|
+
annotation_property = "AnnotationProperty"
|
|
23
|
+
object_value_type = "object_value_type"
|
|
24
|
+
data_value_type = "data_value_type" # these are strings, floats, ...
|
|
25
|
+
xsd_value_type = "xsd_value_type"
|
|
26
|
+
dms_value_type = "dms_value_type"
|
|
27
|
+
dms_node = "dms_node"
|
|
28
|
+
view = "view"
|
|
29
|
+
reference_entity = "reference_entity"
|
|
30
|
+
container = "container"
|
|
31
|
+
datamodel = "datamodel"
|
|
32
|
+
undefined = "undefined"
|
|
33
|
+
multi_value_type = "multi_value_type"
|
|
34
|
+
asset = "asset"
|
|
35
|
+
relationship = "relationship"
|
|
36
|
+
edge = "edge"
|
|
37
|
+
reverse = "reverse"
|
|
38
|
+
unit = "unit"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ALLOWED
|
|
42
|
+
_ALLOWED_PATTERN = r"[^a-zA-Z0-9-_.]"
|
|
43
|
+
|
|
44
|
+
# FOR PARSING STRINGS:
|
|
45
|
+
_PREFIX_REGEX = r"[a-zA-Z]+[a-zA-Z0-9-_.]*[a-zA-Z0-9]+"
|
|
46
|
+
_SUFFIX_REGEX = r"[a-zA-Z0-9-_.]+[a-zA-Z0-9]|[-_.]*[a-zA-Z0-9]+"
|
|
47
|
+
_VERSION_REGEX = r"[a-zA-Z0-9]([.a-zA-Z0-9_-]{0,41}[a-zA-Z0-9])?"
|
|
48
|
+
_PROPERTY_REGEX = r"[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]?"
|
|
49
|
+
_ENTITY_ID_REGEX = rf"{_PREFIX_REGEX}:({_SUFFIX_REGEX})"
|
|
50
|
+
_ENTITY_ID_REGEX_COMPILED = re.compile(rf"^(?P<prefix>{_PREFIX_REGEX}):(?P<suffix>{_SUFFIX_REGEX})$")
|
|
51
|
+
_VERSIONED_ENTITY_REGEX_COMPILED = re.compile(
|
|
52
|
+
rf"^(?P<prefix>{_PREFIX_REGEX}):(?P<suffix>{_SUFFIX_REGEX})\(version=(?P<version>{_VERSION_REGEX})\)$"
|
|
53
|
+
)
|
|
54
|
+
_CLASS_ID_REGEX = rf"(?P<{EntityTypes.class_}>{_ENTITY_ID_REGEX})"
|
|
55
|
+
_CLASS_ID_REGEX_COMPILED = re.compile(rf"^{_CLASS_ID_REGEX}$")
|
|
56
|
+
_PROPERTY_ID_REGEX = rf"\((?P<{EntityTypes.property_}>{_ENTITY_ID_REGEX})\)"
|
|
57
|
+
|
|
58
|
+
ENTITY_PATTERN = re.compile(r"^(?P<prefix>.*?):?(?P<suffix>[^(:]*)(\((?P<content>.+)\))?$")
|
|
59
|
+
MULTI_VALUE_TYPE_PATTERN = re.compile(r"^(?P<types>.*?)(\((?P<content>[^)]+)\))?$")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class _UndefinedType(BaseModel): ...
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _UnknownType(BaseModel):
|
|
66
|
+
def __str__(self) -> str:
|
|
67
|
+
return "#N/A"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# This is a trick to make Undefined and Unknown singletons
|
|
71
|
+
Undefined = _UndefinedType()
|
|
72
|
+
Unknown = _UnknownType()
|
|
73
|
+
_PARSE = object()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from cognite.neat.issues.errors import NeatTypeError
|
|
4
|
+
from cognite.neat.rules.models.data_types import DataType
|
|
5
|
+
|
|
6
|
+
from ._multi_value import MultiValueTypeInfo
|
|
7
|
+
from ._single_value import (
|
|
8
|
+
ClassEntity,
|
|
9
|
+
DMSUnknownEntity,
|
|
10
|
+
EdgeEntity,
|
|
11
|
+
ReverseConnectionEntity,
|
|
12
|
+
Unknown,
|
|
13
|
+
UnknownEntity,
|
|
14
|
+
ViewEntity,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def load_value_type(
|
|
19
|
+
raw: str | MultiValueTypeInfo | DataType | ClassEntity | UnknownEntity, default_prefix: str
|
|
20
|
+
) -> MultiValueTypeInfo | DataType | ClassEntity | UnknownEntity:
|
|
21
|
+
if isinstance(raw, MultiValueTypeInfo | DataType | ClassEntity | UnknownEntity):
|
|
22
|
+
return raw
|
|
23
|
+
elif isinstance(raw, str):
|
|
24
|
+
# property holding xsd data type
|
|
25
|
+
# check if it is multi value type
|
|
26
|
+
if "|" in raw:
|
|
27
|
+
value_type = MultiValueTypeInfo.load(raw)
|
|
28
|
+
value_type.set_default_prefix(default_prefix)
|
|
29
|
+
return value_type
|
|
30
|
+
elif DataType.is_data_type(raw):
|
|
31
|
+
return DataType.load(raw)
|
|
32
|
+
|
|
33
|
+
# unknown value type
|
|
34
|
+
elif raw == str(Unknown):
|
|
35
|
+
return UnknownEntity()
|
|
36
|
+
|
|
37
|
+
# property holding link to class
|
|
38
|
+
else:
|
|
39
|
+
return ClassEntity.load(raw, prefix=default_prefix)
|
|
40
|
+
else:
|
|
41
|
+
raise NeatTypeError(f"Invalid value type: {type(raw)}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_dms_value_type(
|
|
45
|
+
raw: str | DataType | ViewEntity | DMSUnknownEntity,
|
|
46
|
+
default_space: str,
|
|
47
|
+
default_version: str,
|
|
48
|
+
) -> DataType | ViewEntity | DMSUnknownEntity:
|
|
49
|
+
if isinstance(raw, DataType | ViewEntity | DMSUnknownEntity):
|
|
50
|
+
return raw
|
|
51
|
+
elif isinstance(raw, str):
|
|
52
|
+
if DataType.is_data_type(raw):
|
|
53
|
+
return DataType.load(raw)
|
|
54
|
+
elif raw == str(Unknown):
|
|
55
|
+
return DMSUnknownEntity()
|
|
56
|
+
else:
|
|
57
|
+
return ViewEntity.load(raw, space=default_space, version=default_version)
|
|
58
|
+
raise NeatTypeError(f"Invalid value type: {type(raw)}")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def load_connection(
|
|
62
|
+
raw: Literal["direct"] | ReverseConnectionEntity | EdgeEntity | str | None,
|
|
63
|
+
default_space: str,
|
|
64
|
+
default_version: str,
|
|
65
|
+
) -> Literal["direct"] | ReverseConnectionEntity | EdgeEntity | None:
|
|
66
|
+
if (
|
|
67
|
+
isinstance(raw, EdgeEntity | ReverseConnectionEntity)
|
|
68
|
+
or raw is None
|
|
69
|
+
or (isinstance(raw, str) and raw == "direct")
|
|
70
|
+
):
|
|
71
|
+
return raw # type: ignore[return-value]
|
|
72
|
+
elif isinstance(raw, str) and raw.startswith("edge"):
|
|
73
|
+
return EdgeEntity.load(raw, space=default_space, version=default_version) # type: ignore[return-value]
|
|
74
|
+
elif isinstance(raw, str) and raw.startswith("reverse"):
|
|
75
|
+
return ReverseConnectionEntity.load(raw) # type: ignore[return-value]
|
|
76
|
+
raise NeatTypeError(f"Invalid connection: {type(raw)}")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from typing import Any, ClassVar
|
|
2
|
+
|
|
3
|
+
from pydantic import (
|
|
4
|
+
BaseModel,
|
|
5
|
+
model_serializer,
|
|
6
|
+
model_validator,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from cognite.neat.rules.models.data_types import DataType
|
|
10
|
+
|
|
11
|
+
from ._constants import _PARSE, EntityTypes, Undefined
|
|
12
|
+
from ._single_value import ClassEntity, UnknownEntity
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MultiValueTypeInfo(BaseModel):
|
|
16
|
+
type_: ClassVar[EntityTypes] = EntityTypes.multi_value_type
|
|
17
|
+
types: list[DataType | ClassEntity]
|
|
18
|
+
|
|
19
|
+
def __str__(self) -> str:
|
|
20
|
+
return " | ".join([str(t) for t in self.types])
|
|
21
|
+
|
|
22
|
+
@model_serializer(when_used="unless-none", return_type=str)
|
|
23
|
+
def as_str(self) -> str:
|
|
24
|
+
return str(self)
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def load(cls, data: Any) -> "MultiValueTypeInfo":
|
|
28
|
+
# already instance of MultiValueTypeInfo
|
|
29
|
+
if isinstance(data, cls):
|
|
30
|
+
return data
|
|
31
|
+
|
|
32
|
+
# it is a raw string that needs to be parsed
|
|
33
|
+
elif isinstance(data, str):
|
|
34
|
+
return cls.model_validate({_PARSE: data})
|
|
35
|
+
|
|
36
|
+
# it is dict that needs to be parsed
|
|
37
|
+
else:
|
|
38
|
+
return cls.model_validate(data)
|
|
39
|
+
|
|
40
|
+
@model_validator(mode="before")
|
|
41
|
+
def _load(cls, data: Any) -> "dict | MultiValueTypeInfo":
|
|
42
|
+
if isinstance(data, dict) and _PARSE in data:
|
|
43
|
+
data = data[_PARSE]
|
|
44
|
+
elif isinstance(data, dict):
|
|
45
|
+
return data
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError(f"Cannot load {cls.__name__} from {data}")
|
|
48
|
+
|
|
49
|
+
result = cls._parse(data)
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def _parse(cls, raw: str) -> dict:
|
|
54
|
+
if not (types := [type_.strip() for type_ in raw.split("|")]):
|
|
55
|
+
return {"types": [UnknownEntity()]}
|
|
56
|
+
else:
|
|
57
|
+
return {
|
|
58
|
+
"types": [
|
|
59
|
+
(DataType.load(type_) if DataType.is_data_type(type_) else ClassEntity.load(type_))
|
|
60
|
+
for type_ in types
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def set_default_prefix(self, prefix: str):
|
|
65
|
+
for type_ in self.types:
|
|
66
|
+
if isinstance(type_, ClassEntity) and type_.prefix is Undefined:
|
|
67
|
+
type_.prefix = prefix
|