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.

Files changed (129) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/constants.py +3 -0
  3. cognite/neat/graph/__init__.py +0 -3
  4. cognite/neat/graph/extractors/_mock_graph_generator.py +2 -1
  5. cognite/neat/graph/loaders/_base.py +3 -3
  6. cognite/neat/graph/loaders/_rdf2asset.py +24 -25
  7. cognite/neat/graph/loaders/_rdf2dms.py +20 -15
  8. cognite/neat/issues/__init__.py +1 -3
  9. cognite/neat/issues/_base.py +261 -71
  10. cognite/neat/issues/errors/__init__.py +73 -0
  11. cognite/neat/issues/errors/_external.py +67 -0
  12. cognite/neat/issues/errors/_general.py +35 -0
  13. cognite/neat/issues/errors/_properties.py +62 -0
  14. cognite/neat/issues/errors/_resources.py +111 -0
  15. cognite/neat/issues/errors/_workflow.py +36 -0
  16. cognite/neat/issues/formatters.py +1 -1
  17. cognite/neat/issues/warnings/__init__.py +66 -0
  18. cognite/neat/issues/warnings/_external.py +40 -0
  19. cognite/neat/issues/warnings/_general.py +29 -0
  20. cognite/neat/issues/warnings/_models.py +92 -0
  21. cognite/neat/issues/warnings/_properties.py +44 -0
  22. cognite/neat/issues/warnings/_resources.py +55 -0
  23. cognite/neat/issues/warnings/user_modeling.py +113 -0
  24. cognite/neat/rules/_shared.py +53 -2
  25. cognite/neat/rules/analysis/_base.py +1 -1
  26. cognite/neat/rules/exporters/_base.py +7 -18
  27. cognite/neat/rules/exporters/_rules2dms.py +17 -20
  28. cognite/neat/rules/exporters/_rules2excel.py +9 -16
  29. cognite/neat/rules/exporters/_rules2ontology.py +77 -64
  30. cognite/neat/rules/exporters/_rules2yaml.py +6 -9
  31. cognite/neat/rules/exporters/_validation.py +11 -96
  32. cognite/neat/rules/importers/_base.py +9 -58
  33. cognite/neat/rules/importers/_dms2rules.py +188 -135
  34. cognite/neat/rules/importers/_dtdl2rules/dtdl_converter.py +48 -35
  35. cognite/neat/rules/importers/_dtdl2rules/dtdl_importer.py +36 -45
  36. cognite/neat/rules/importers/_dtdl2rules/spec.py +7 -0
  37. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2classes.py +8 -4
  38. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2metadata.py +3 -3
  39. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2properties.py +18 -11
  40. cognite/neat/rules/importers/_rdf/_imf2rules/_imf2rules.py +12 -19
  41. cognite/neat/rules/importers/_rdf/_inference2rules.py +14 -37
  42. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2classes.py +1 -0
  43. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -0
  44. cognite/neat/rules/importers/_rdf/_owl2rules/_owl2rules.py +9 -20
  45. cognite/neat/rules/importers/_rdf/_shared.py +4 -4
  46. cognite/neat/rules/importers/_spreadsheet2rules.py +46 -97
  47. cognite/neat/rules/importers/_yaml2rules.py +32 -58
  48. cognite/neat/rules/models/__init__.py +21 -5
  49. cognite/neat/rules/models/_base_input.py +162 -0
  50. cognite/neat/rules/models/{_base.py → _base_rules.py} +1 -12
  51. cognite/neat/rules/models/_rdfpath.py +4 -4
  52. cognite/neat/rules/models/{_types/_field.py → _types.py} +5 -10
  53. cognite/neat/rules/models/asset/__init__.py +5 -2
  54. cognite/neat/rules/models/asset/_rules.py +3 -23
  55. cognite/neat/rules/models/asset/_rules_input.py +40 -115
  56. cognite/neat/rules/models/asset/_validation.py +14 -10
  57. cognite/neat/rules/models/data_types.py +150 -44
  58. cognite/neat/rules/models/dms/__init__.py +19 -7
  59. cognite/neat/rules/models/dms/_exporter.py +102 -34
  60. cognite/neat/rules/models/dms/_rules.py +65 -162
  61. cognite/neat/rules/models/dms/_rules_input.py +186 -254
  62. cognite/neat/rules/models/dms/_schema.py +87 -78
  63. cognite/neat/rules/models/dms/_serializer.py +44 -3
  64. cognite/neat/rules/models/dms/_validation.py +106 -68
  65. cognite/neat/rules/models/domain.py +52 -1
  66. cognite/neat/rules/models/entities/__init__.py +63 -0
  67. cognite/neat/rules/models/entities/_constants.py +73 -0
  68. cognite/neat/rules/models/entities/_loaders.py +76 -0
  69. cognite/neat/rules/models/entities/_multi_value.py +67 -0
  70. cognite/neat/rules/models/{entities.py → entities/_single_value.py} +74 -232
  71. cognite/neat/rules/models/entities/_types.py +86 -0
  72. cognite/neat/rules/models/{wrapped_entities.py → entities/_wrapped.py} +1 -1
  73. cognite/neat/rules/models/information/__init__.py +10 -2
  74. cognite/neat/rules/models/information/_rules.py +10 -22
  75. cognite/neat/rules/models/information/_rules_input.py +57 -204
  76. cognite/neat/rules/models/information/_validation.py +48 -25
  77. cognite/neat/rules/transformers/__init__.py +21 -0
  78. cognite/neat/rules/transformers/_base.py +81 -0
  79. cognite/neat/rules/{models/information/_converter.py → transformers/_converters.py} +217 -21
  80. cognite/neat/rules/transformers/_map_onto.py +97 -0
  81. cognite/neat/rules/transformers/_pipelines.py +61 -0
  82. cognite/neat/rules/transformers/_verification.py +136 -0
  83. cognite/neat/{graph/stores → store}/_provenance.py +10 -1
  84. cognite/neat/utils/auxiliary.py +2 -35
  85. cognite/neat/utils/cdf/data_classes.py +20 -0
  86. cognite/neat/utils/regex_patterns.py +6 -0
  87. cognite/neat/utils/text.py +17 -0
  88. cognite/neat/workflows/base.py +4 -4
  89. cognite/neat/workflows/cdf_store.py +3 -3
  90. cognite/neat/workflows/steps/data_contracts.py +1 -1
  91. cognite/neat/workflows/steps/lib/current/graph_extractor.py +3 -3
  92. cognite/neat/workflows/steps/lib/current/graph_loader.py +2 -2
  93. cognite/neat/workflows/steps/lib/current/graph_store.py +1 -1
  94. cognite/neat/workflows/steps/lib/current/rules_exporter.py +116 -47
  95. cognite/neat/workflows/steps/lib/current/rules_importer.py +30 -28
  96. cognite/neat/workflows/steps/lib/current/rules_validator.py +5 -6
  97. cognite/neat/workflows/steps/lib/io/io_steps.py +5 -5
  98. cognite/neat/workflows/steps_registry.py +4 -5
  99. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/METADATA +1 -1
  100. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/RECORD +105 -106
  101. cognite/neat/exceptions.py +0 -145
  102. cognite/neat/graph/exceptions.py +0 -90
  103. cognite/neat/issues/errors/external.py +0 -21
  104. cognite/neat/issues/errors/properties.py +0 -75
  105. cognite/neat/issues/errors/resources.py +0 -123
  106. cognite/neat/issues/errors/schema.py +0 -0
  107. cognite/neat/issues/neat_warnings/__init__.py +0 -2
  108. cognite/neat/issues/neat_warnings/identifier.py +0 -27
  109. cognite/neat/issues/neat_warnings/models.py +0 -22
  110. cognite/neat/issues/neat_warnings/properties.py +0 -77
  111. cognite/neat/issues/neat_warnings/resources.py +0 -125
  112. cognite/neat/rules/issues/__init__.py +0 -22
  113. cognite/neat/rules/issues/base.py +0 -63
  114. cognite/neat/rules/issues/dms.py +0 -549
  115. cognite/neat/rules/issues/fileread.py +0 -197
  116. cognite/neat/rules/issues/ontology.py +0 -298
  117. cognite/neat/rules/issues/spreadsheet.py +0 -563
  118. cognite/neat/rules/issues/spreadsheet_file.py +0 -151
  119. cognite/neat/rules/issues/tables.py +0 -72
  120. cognite/neat/rules/models/_constants.py +0 -1
  121. cognite/neat/rules/models/_types/__init__.py +0 -19
  122. cognite/neat/rules/models/asset/_converter.py +0 -4
  123. cognite/neat/rules/models/dms/_converter.py +0 -145
  124. cognite/neat/workflows/_exceptions.py +0 -41
  125. /cognite/neat/{graph/stores → store}/__init__.py +0 -0
  126. /cognite/neat/{graph/stores → store}/_base.py +0 -0
  127. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/LICENSE +0 -0
  128. {cognite_neat-0.88.2.dist-info → cognite_neat-0.89.0.dist-info}/WHEEL +0 -0
  129. {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.issues import IssueList, NeatIssueList
7
- from cognite.neat.rules import issues
8
- from cognite.neat.rules.models._base import DataModelType, ExtensionCategory, SchemaCompleteness
9
- from cognite.neat.rules.models._constants import DMS_CONTAINER_SIZE_LIMIT
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[issues.spreadsheet.InconsistentContainerDefinitionError] = []
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
- issues.spreadsheet.MultiValueTypeError(
77
+ PropertyDefinitionDuplicatedError[dm.ContainerId](
67
78
  container_id,
79
+ "container",
68
80
  prop_name,
69
- row_numbers,
70
- {v.dms._type if isinstance(v, DataType) else str(v) for v in value_types},
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
- issues.spreadsheet.MultiValueIsListError(container_id, prop_name, row_numbers, list_definitions)
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
- issues.spreadsheet.MultiNullableError(container_id, prop_name, row_numbers, nullable_definitions)
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
- issues.spreadsheet.MultiDefaultError(
87
- container_id, prop_name, row_numbers, list(default_definitions)
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
- issues.spreadsheet.MultiIndexError(container_id, prop_name, row_numbers, index_definitions)
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
- issues.spreadsheet.MultiUniqueConstraintError(
101
- container_id, prop_name, row_numbers, constraint_definitions
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[issues.ValidationIssue] = []
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
- issues.spreadsheet.NonExistingViewError(
134
- column="View",
135
- row=prop_no,
136
- type="value_error.missing",
137
- view_id=view_id,
138
- msg="",
139
- input=None,
140
- url=None,
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 > DMS_CONTAINER_SIZE_LIMIT:
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
- issues.spreadsheet.NonExistingContainerError(
165
- column="Container",
166
- row=prop_no,
167
- type="value_error.missing",
168
- container_id=container_id,
169
- msg="",
170
- input=None,
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
- issues.spreadsheet.NonExistingContainerError(
179
- column="Constraint",
180
- row=constraint_no,
181
- type="value_error.missing",
182
- container_id=constraint.as_id(),
183
- msg="",
184
- input=None,
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
- issues.dms.ChangingContainerError(
216
- container_id=container_id,
217
- changed_properties=changed_properties or None,
218
- changed_attributes=changed_attributes or None,
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
- issues.dms.ChangingViewError(
245
- view_id=view_id,
246
- changed_properties=changed_properties or None,
247
- changed_attributes=changed_attributes or None,
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
- issues.dms.ViewMapsToTooManyContainersWarning(
258
- view_id=view_id,
259
- container_ids=mapped_containers,
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
- issues.dms.HasDataFilterAppliedToTooManyContainersWarning(
269
- view_id=view_id,
270
- container_ids=mapped_containers,
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
- issues.dms.RawFilterAppliedToViewWarning(
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 ._base import (
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