cognite-neat 0.81.12__py3-none-any.whl → 0.82.1__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 (30) hide show
  1. cognite/neat/_version.py +1 -1
  2. cognite/neat/graph/extractors/_mock_graph_generator.py +2 -8
  3. cognite/neat/graph/loaders/_rdf2dms.py +1 -1
  4. cognite/neat/graph/queries/__init__.py +3 -0
  5. cognite/neat/graph/queries/_base.py +99 -0
  6. cognite/neat/graph/queries/_construct.py +185 -0
  7. cognite/neat/graph/queries/_shared.py +159 -0
  8. cognite/neat/graph/stores/_base.py +24 -87
  9. cognite/neat/rules/_shared.py +2 -2
  10. cognite/neat/rules/analysis/_information_rules.py +34 -58
  11. cognite/neat/rules/importers/_inference2rules.py +5 -1
  12. cognite/neat/rules/importers/_spreadsheet2rules.py +6 -1
  13. cognite/neat/rules/models/__init__.py +4 -1
  14. cognite/neat/rules/models/_base.py +1 -0
  15. cognite/neat/rules/models/asset/__init__.py +10 -0
  16. cognite/neat/rules/models/asset/_converter.py +4 -0
  17. cognite/neat/rules/models/asset/_rules.py +156 -0
  18. cognite/neat/rules/models/asset/_rules_input.py +171 -0
  19. cognite/neat/rules/models/asset/_serializer.py +73 -0
  20. cognite/neat/rules/models/asset/_validation.py +4 -0
  21. cognite/neat/rules/models/entities.py +56 -1
  22. cognite/neat/rules/models/information/_rules.py +5 -0
  23. cognite/neat/rules/models/information/_rules_input.py +3 -6
  24. cognite/neat/utils/utils.py +6 -1
  25. cognite/neat/workflows/steps/data_contracts.py +5 -2
  26. {cognite_neat-0.81.12.dist-info → cognite_neat-0.82.1.dist-info}/METADATA +1 -1
  27. {cognite_neat-0.81.12.dist-info → cognite_neat-0.82.1.dist-info}/RECORD +30 -20
  28. {cognite_neat-0.81.12.dist-info → cognite_neat-0.82.1.dist-info}/LICENSE +0 -0
  29. {cognite_neat-0.81.12.dist-info → cognite_neat-0.82.1.dist-info}/WHEEL +0 -0
  30. {cognite_neat-0.81.12.dist-info → cognite_neat-0.82.1.dist-info}/entry_points.txt +0 -0
@@ -2,7 +2,7 @@ import itertools
2
2
  import logging
3
3
  import warnings
4
4
  from collections import defaultdict
5
- from typing import Any, cast
5
+ from typing import Any
6
6
 
7
7
  import pandas as pd
8
8
  from pydantic import ValidationError
@@ -17,24 +17,11 @@ from ._base import BaseAnalysis
17
17
 
18
18
 
19
19
  class InformationArchitectRulesAnalysis(BaseAnalysis):
20
+ """Assumes analysis over only the complete schema"""
21
+
20
22
  def __init__(self, rules: InformationRules):
21
23
  self.rules = rules
22
24
 
23
- @property
24
- def referred_classes(self) -> set[ClassEntity]:
25
- return self.directly_referred_classes.union(self.inherited_referred_classes)
26
-
27
- @property
28
- def referred_classes_properties(self) -> list[InformationProperty]:
29
- referred_classes_properties = []
30
- class_properties_dict = self.classes_with_properties(use_reference=True)
31
-
32
- for class_ in self.referred_classes:
33
- if class_ in class_properties_dict:
34
- referred_classes_properties.extend(class_properties_dict[class_])
35
-
36
- return referred_classes_properties
37
-
38
25
  @property
39
26
  def directly_referred_classes(self) -> set[ClassEntity]:
40
27
  return {
@@ -51,19 +38,17 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
51
38
  dir_referred_classes = self.directly_referred_classes
52
39
  inherited_referred_classes = []
53
40
  for class_ in dir_referred_classes:
54
- inherited_referred_classes.extend(self.class_inheritance_path(class_, use_reference=True))
41
+ inherited_referred_classes.extend(self.class_inheritance_path(class_))
55
42
  return set(inherited_referred_classes)
56
43
 
57
- def class_parent_pairs(self, use_reference: bool = False) -> dict[ClassEntity, list[ParentClassEntity]]:
44
+ def class_parent_pairs(self) -> dict[ClassEntity, list[ParentClassEntity]]:
58
45
  """This only returns class - parent pairs only if parent is in the same data model"""
59
46
  class_subclass_pairs: dict[ClassEntity, list[ParentClassEntity]] = {}
60
47
 
61
- rules = cast(InformationRules, self.rules.reference if use_reference else self.rules)
62
-
63
- if not rules:
48
+ if not self.rules:
64
49
  return class_subclass_pairs
65
50
 
66
- for definition in rules.classes:
51
+ for definition in self.rules.classes:
67
52
  class_subclass_pairs[definition.class_] = []
68
53
 
69
54
  if definition.parent is None:
@@ -81,13 +66,12 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
81
66
  return class_subclass_pairs
82
67
 
83
68
  def classes_with_properties(
84
- self, consider_inheritance: bool = False, use_reference: bool = False
69
+ self, consider_inheritance: bool = False
85
70
  ) -> dict[ClassEntity, list[InformationProperty]]:
86
71
  """Returns classes that have been defined in the data model.
87
72
 
88
73
  Args:
89
74
  consider_inheritance: Whether to consider inheritance or not. Defaults False
90
- use_reference: Whether to use reference rules or not. Defaults False
91
75
 
92
76
  Returns:
93
77
  Dictionary of classes with a list of properties defined for them
@@ -103,21 +87,19 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
103
87
 
104
88
  class_property_pairs: dict[ClassEntity, list[InformationProperty]] = defaultdict(list)
105
89
 
106
- rules = cast(InformationRules, self.rules.reference if use_reference else self.rules)
107
-
108
- for property_ in rules.properties:
90
+ for property_ in self.rules.properties:
109
91
  class_property_pairs[property_.class_].append(property_)
110
92
 
111
93
  if consider_inheritance:
112
- class_parent_pairs = self.class_parent_pairs(use_reference=use_reference)
94
+ class_parent_pairs = self.class_parent_pairs()
113
95
  for class_ in class_parent_pairs:
114
96
  self._add_inherited_properties(class_, class_property_pairs, class_parent_pairs)
115
97
 
116
98
  return class_property_pairs
117
99
 
118
- def class_inheritance_path(self, class_: ClassEntity | str, use_reference: bool = False) -> list[ClassEntity]:
100
+ def class_inheritance_path(self, class_: ClassEntity | str) -> list[ClassEntity]:
119
101
  class_ = class_ if isinstance(class_, ClassEntity) else ClassEntity.load(class_)
120
- class_parent_pairs = self.class_parent_pairs(use_reference)
102
+ class_parent_pairs = self.class_parent_pairs()
121
103
  return get_inheritance_path(class_, class_parent_pairs)
122
104
 
123
105
  @classmethod
@@ -133,8 +115,13 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
133
115
  if parent.as_class_entity() in class_property_pairs:
134
116
  for property_ in class_property_pairs[parent.as_class_entity()]:
135
117
  property_ = property_.model_copy()
136
- # this corresponds to importing properties from parent class
118
+
119
+ # This corresponds to importing properties from parent class
120
+ # making sure that the property is attached to desired child class
137
121
  property_.class_ = class_
122
+ property_.inherited = True
123
+
124
+ # need same if we have RDF path to make sure that the starting class is the
138
125
 
139
126
  if class_ in class_property_pairs:
140
127
  class_property_pairs[class_].append(property_)
@@ -142,14 +129,13 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
142
129
  class_property_pairs[class_] = [property_]
143
130
 
144
131
  def class_property_pairs(
145
- self, only_rdfpath: bool = False, consider_inheritance: bool = False, use_reference: bool = False
132
+ self, only_rdfpath: bool = False, consider_inheritance: bool = False
146
133
  ) -> dict[ClassEntity, dict[str, InformationProperty]]:
147
134
  """Returns a dictionary of classes with a dictionary of properties associated with them.
148
135
 
149
136
  Args:
150
137
  only_rdfpath : To consider only properties which have rule `rdfpath` set. Defaults False
151
138
  consider_inheritance: Whether to consider inheritance or not. Defaults False
152
- use_reference : Whether to use reference rules or not. Defaults False
153
139
 
154
140
  Returns:
155
141
  Dictionary of classes with a dictionary of properties associated with them.
@@ -178,7 +164,7 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
178
164
 
179
165
  class_property_pairs = {}
180
166
 
181
- for class_, properties in self.classes_with_properties(consider_inheritance, use_reference).items():
167
+ for class_, properties in self.classes_with_properties(consider_inheritance).items():
182
168
  processed_properties = {}
183
169
  for property_ in properties:
184
170
  if property_.property_ in processed_properties:
@@ -197,12 +183,11 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
197
183
 
198
184
  return class_property_pairs
199
185
 
200
- def class_linkage(self, consider_inheritance: bool = False, use_reference: bool = False) -> pd.DataFrame:
186
+ def class_linkage(self, consider_inheritance: bool = False) -> pd.DataFrame:
201
187
  """Returns a dataframe with the class linkage of the data model.
202
188
 
203
189
  Args:
204
190
  consider_inheritance: Whether to consider inheritance or not. Defaults False
205
- use_reference: Whether to use reference rules or not. Defaults False
206
191
 
207
192
  Returns:
208
193
  Dataframe with the class linkage of the data model
@@ -210,7 +195,7 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
210
195
 
211
196
  class_linkage = pd.DataFrame(columns=["source_class", "target_class", "connecting_property", "max_occurrence"])
212
197
 
213
- class_property_pairs = self.classes_with_properties(consider_inheritance, use_reference)
198
+ class_property_pairs = self.classes_with_properties(consider_inheritance)
214
199
  properties = list(itertools.chain.from_iterable(class_property_pairs.values()))
215
200
 
216
201
  for property_ in properties:
@@ -230,56 +215,50 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
230
215
 
231
216
  return class_linkage
232
217
 
233
- def connected_classes(self, consider_inheritance: bool = False, use_reference: bool = False) -> set[ClassEntity]:
218
+ def connected_classes(self, consider_inheritance: bool = False) -> set[ClassEntity]:
234
219
  """Return a set of classes that are connected to other classes.
235
220
 
236
221
  Args:
237
222
  consider_inheritance: Whether to consider inheritance or not. Defaults False
238
- use_reference: Whether to use reference rules or not. Defaults False
239
223
 
240
224
  Returns:
241
225
  Set of classes that are connected to other classes
242
226
  """
243
- class_linkage = self.class_linkage(consider_inheritance, use_reference)
227
+ class_linkage = self.class_linkage(consider_inheritance)
244
228
  return set(class_linkage.source_class.values).union(set(class_linkage.target_class.values))
245
229
 
246
- def defined_classes(self, consider_inheritance: bool = False, use_reference: bool = False) -> set[ClassEntity]:
230
+ def defined_classes(self, consider_inheritance: bool = False) -> set[ClassEntity]:
247
231
  """Returns classes that have properties defined for them in the data model.
248
232
 
249
233
  Args:
250
234
  consider_inheritance: Whether to consider inheritance or not. Defaults False
251
- use_reference: Whether to use reference rules or not. Defaults False
252
235
 
253
236
  Returns:
254
237
  Set of classes that have been defined in the data model
255
238
  """
256
- class_property_pairs = self.classes_with_properties(consider_inheritance, use_reference)
239
+ class_property_pairs = self.classes_with_properties(consider_inheritance)
257
240
  properties = list(itertools.chain.from_iterable(class_property_pairs.values()))
258
241
 
259
242
  return {property.class_ for property in properties}
260
243
 
261
- def disconnected_classes(self, consider_inheritance: bool = False, use_reference: bool = False) -> set[ClassEntity]:
244
+ def disconnected_classes(self, consider_inheritance: bool = False) -> set[ClassEntity]:
262
245
  """Return a set of classes that are disconnected (i.e. isolated) from other classes.
263
246
 
264
247
  Args:
265
248
  consider_inheritance: Whether to consider inheritance or not. Defaults False
266
- use_reference: Whether to use reference rules or not. Defaults False
267
249
 
268
250
  Returns:
269
251
  Set of classes that are disconnected from other classes
270
252
  """
271
- return self.defined_classes(consider_inheritance, use_reference) - self.connected_classes(
272
- consider_inheritance, use_reference
273
- )
253
+ return self.defined_classes(consider_inheritance) - self.connected_classes(consider_inheritance)
274
254
 
275
255
  def symmetrically_connected_classes(
276
- self, consider_inheritance: bool = False, use_reference: bool = False
256
+ self, consider_inheritance: bool = False
277
257
  ) -> set[tuple[ClassEntity, ClassEntity]]:
278
258
  """Returns a set of pairs of symmetrically linked classes.
279
259
 
280
260
  Args:
281
261
  consider_inheritance: Whether to consider inheritance or not. Defaults False
282
- use_reference: Whether to use reference rules or not. Defaults False
283
262
 
284
263
  Returns:
285
264
  Set of pairs of symmetrically linked classes
@@ -293,7 +272,7 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
293
272
  # TODO: Find better name for this method
294
273
  sym_pairs: set[tuple[ClassEntity, ClassEntity]] = set()
295
274
 
296
- class_linkage = self.class_linkage(consider_inheritance, use_reference)
275
+ class_linkage = self.class_linkage(consider_inheritance)
297
276
  if class_linkage.empty:
298
277
  return sym_pairs
299
278
 
@@ -321,13 +300,12 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
321
300
  class_dict[str(definition.class_.suffix)] = definition
322
301
  return class_dict
323
302
 
324
- def subset_rules(self, desired_classes: set[ClassEntity], use_reference: bool = False) -> InformationRules:
303
+ def subset_rules(self, desired_classes: set[ClassEntity]) -> InformationRules:
325
304
  """
326
305
  Subset rules to only include desired classes and their properties.
327
306
 
328
307
  Args:
329
308
  desired_classes: Desired classes to include in the reduced data model
330
- use_reference: Whether to use reference rules or not. Defaults False
331
309
 
332
310
  Returns:
333
311
  Instance of InformationRules
@@ -350,9 +328,7 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
350
328
  only with base Pydantic validators.
351
329
  """
352
330
 
353
- rules = cast(InformationRules, self.rules.reference if use_reference else self.rules)
354
-
355
- if rules.metadata.schema_ is not SchemaCompleteness.complete:
331
+ if self.rules.metadata.schema_ is not SchemaCompleteness.complete:
356
332
  raise ValueError("Rules are not complete cannot perform reduction!")
357
333
  class_as_dict = self.as_class_dict()
358
334
  class_parents_pairs = self.class_parent_pairs()
@@ -380,8 +356,8 @@ class InformationArchitectRulesAnalysis(BaseAnalysis):
380
356
  )
381
357
 
382
358
  reduced_data_model: dict[str, Any] = {
383
- "metadata": rules.metadata.model_copy(),
384
- "prefixes": (rules.prefixes or {}).copy(),
359
+ "metadata": self.rules.metadata.model_copy(),
360
+ "prefixes": (self.rules.prefixes or {}).copy(),
385
361
  "classes": [],
386
362
  "properties": [],
387
363
  }
@@ -17,7 +17,7 @@ from cognite.neat.rules.models.information import (
17
17
  InformationMetadata,
18
18
  InformationRulesInput,
19
19
  )
20
- from cognite.neat.utils.utils import get_namespace, remove_namespace
20
+ from cognite.neat.utils.utils import get_namespace, remove_namespace, uri_to_short_form
21
21
 
22
22
  ORDERED_CLASSES_QUERY = """SELECT ?class (count(?s) as ?instances )
23
23
  WHERE { ?s a ?class . }
@@ -176,6 +176,10 @@ class InferenceImporter(BaseImporter):
176
176
  "max_count": cast(RdfLiteral, occurrence).value,
177
177
  "value_type": value_type_id,
178
178
  "reference": property_uri,
179
+ "transformation": (
180
+ f"{uri_to_short_form(class_definition['reference'], prefixes)}"
181
+ f"({uri_to_short_form(cast(URIRef, property_uri), prefixes)})"
182
+ ),
179
183
  "comment": (
180
184
  f"Class <{class_id}> has property <{property_id}> with "
181
185
  f"value type <{value_type_id}> which occurs <1> times in the graph"
@@ -15,12 +15,14 @@ from cognite.neat.rules import issues
15
15
  from cognite.neat.rules.issues import IssueList
16
16
  from cognite.neat.rules.models import (
17
17
  RULES_PER_ROLE,
18
+ AssetRules,
18
19
  DMSRules,
19
20
  DomainRules,
20
21
  InformationRules,
21
22
  RoleTypes,
22
23
  SchemaCompleteness,
23
24
  )
25
+ from cognite.neat.rules.models.asset import AssetRulesInput
24
26
  from cognite.neat.rules.models.dms import DMSRulesInput
25
27
  from cognite.neat.rules.models.information import InformationRulesInput
26
28
  from cognite.neat.utils.auxiliary import local_import
@@ -35,6 +37,7 @@ SOURCE_SHEET__TARGET_FIELD__HEADERS = [
35
37
  {
36
38
  RoleTypes.domain_expert: "Property",
37
39
  RoleTypes.information_architect: "Property",
40
+ RoleTypes.asset_architect: "Property",
38
41
  RoleTypes.dms_architect: "View Property",
39
42
  },
40
43
  ),
@@ -266,6 +269,8 @@ class ExcelImporter(BaseImporter):
266
269
  rules = DMSRulesInput.load(sheets).as_rules()
267
270
  elif rules_cls is InformationRules:
268
271
  rules = InformationRulesInput.load(sheets).as_rules()
272
+ elif rules_cls is AssetRules:
273
+ rules = AssetRulesInput.load(sheets).as_rules()
269
274
  else:
270
275
  rules = rules_cls.model_validate(sheets) # type: ignore[attr-defined]
271
276
 
@@ -300,7 +305,7 @@ class GoogleSheetImporter(BaseImporter):
300
305
  import gspread # type: ignore[import]
301
306
 
302
307
  role = role or RoleTypes.domain_expert
303
- rules_model = cast(DomainRules | InformationRules | DMSRules, RULES_PER_ROLE[role])
308
+ rules_model = cast(DomainRules | InformationRules | AssetRules | DMSRules, RULES_PER_ROLE[role])
304
309
 
305
310
  client_google = gspread.service_account()
306
311
  google_sheet = client_google.open_by_key(self.sheet_id)
@@ -1,3 +1,4 @@
1
+ from cognite.neat.rules.models.asset import AssetRules
1
2
  from cognite.neat.rules.models.domain import DomainRules
2
3
  from cognite.neat.rules.models.information._rules import InformationRules
3
4
 
@@ -5,9 +6,10 @@ from ._base import DataModelType, ExtensionCategory, RoleTypes, SchemaCompletene
5
6
  from .dms._rules import DMSRules
6
7
  from .dms._schema import DMSSchema
7
8
 
8
- RULES_PER_ROLE: dict[RoleTypes, type[DomainRules] | type[InformationRules] | type[DMSRules]] = {
9
+ RULES_PER_ROLE: dict[RoleTypes, type[DomainRules] | type[InformationRules] | type[AssetRules] | type[DMSRules]] = {
9
10
  RoleTypes.domain_expert: DomainRules,
10
11
  RoleTypes.information_architect: InformationRules,
12
+ RoleTypes.asset_architect: AssetRules,
11
13
  RoleTypes.dms_architect: DMSRules,
12
14
  }
13
15
 
@@ -15,6 +17,7 @@ RULES_PER_ROLE: dict[RoleTypes, type[DomainRules] | type[InformationRules] | typ
15
17
  __all__ = [
16
18
  "DomainRules",
17
19
  "InformationRules",
20
+ "AssetRules",
18
21
  "DMSRules",
19
22
  "RULES_PER_ROLE",
20
23
  "DMSSchema",
@@ -151,6 +151,7 @@ class DataModelType(StrEnum):
151
151
  class RoleTypes(StrEnum):
152
152
  domain_expert = "domain expert"
153
153
  information_architect = "information architect"
154
+ asset_architect = "asset architect"
154
155
  dms_architect = "DMS Architect"
155
156
 
156
157
 
@@ -0,0 +1,10 @@
1
+ from ._rules import AssetClass, AssetMetadata, AssetProperty, AssetRules
2
+ from ._rules_input import AssetRulesInput
3
+
4
+ __all__ = [
5
+ "AssetRules",
6
+ "AssetMetadata",
7
+ "AssetClass",
8
+ "AssetProperty",
9
+ "AssetRulesInput",
10
+ ]
@@ -0,0 +1,4 @@
1
+ from cognite.neat.rules.models.information._converter import _InformationRulesConverter
2
+
3
+
4
+ class _AssetRulesConverter(_InformationRulesConverter): ...
@@ -0,0 +1,156 @@
1
+ import sys
2
+ from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
3
+
4
+ from pydantic import Field, field_validator, model_validator
5
+ from pydantic.main import IncEx
6
+ from rdflib import Namespace
7
+
8
+ from cognite.neat.constants import PREFIXES
9
+ from cognite.neat.issues import MultiValueError
10
+ from cognite.neat.rules import issues
11
+ from cognite.neat.rules.models._base import BaseRules, RoleTypes, SheetList
12
+ from cognite.neat.rules.models.domain import DomainRules
13
+ from cognite.neat.rules.models.entities import (
14
+ CdfResourceEntityList,
15
+ ClassEntity,
16
+ MultiValueTypeInfo,
17
+ ParentClassEntity,
18
+ Undefined,
19
+ )
20
+ from cognite.neat.rules.models.information import (
21
+ InformationClass,
22
+ InformationMetadata,
23
+ InformationProperty,
24
+ InformationRules,
25
+ )
26
+
27
+ if TYPE_CHECKING:
28
+ from cognite.neat.rules.models.dms._rules import DMSRules
29
+
30
+
31
+ if sys.version_info >= (3, 11):
32
+ from typing import Self
33
+ else:
34
+ from typing_extensions import Self
35
+
36
+
37
+ class AssetMetadata(InformationMetadata):
38
+ role: ClassVar[RoleTypes] = RoleTypes.asset_architect
39
+
40
+
41
+ class AssetClass(InformationClass): ...
42
+
43
+
44
+ class AssetProperty(InformationProperty):
45
+ """
46
+ A property is a characteristic of a class. It is a named attribute of a class that describes a range of values
47
+ or a relationship to another class.
48
+
49
+ Args:
50
+ class_: Class ID to which property belongs
51
+ property_: Property ID of the property
52
+ name: Property name.
53
+ value_type: Type of value property will hold (data or link to another class)
54
+ min_count: Minimum count of the property values. Defaults to 0
55
+ max_count: Maximum count of the property values. Defaults to None
56
+ default: Default value of the property
57
+ reference: Reference to the source of the information, HTTP URI
58
+ match_type: The match type of the resource being described and the source entity.
59
+ transformation: Actual rule for the transformation from source to target representation of
60
+ knowledge graph. Defaults to None (no transformation)
61
+ implementation: Details on how given class-property is implemented in the classic CDF
62
+ """
63
+
64
+ implementation: CdfResourceEntityList | None = Field(alias="Implementation", default=None)
65
+
66
+
67
+ class AssetRules(BaseRules):
68
+ metadata: AssetMetadata = Field(alias="Metadata")
69
+ properties: SheetList[AssetProperty] = Field(alias="Properties")
70
+ classes: SheetList[AssetClass] = Field(alias="Classes")
71
+ prefixes: dict[str, Namespace] = Field(default_factory=lambda: PREFIXES.copy())
72
+ last: "AssetRules | None" = Field(None, alias="Last")
73
+ reference: "AssetRules | None" = Field(None, alias="Reference")
74
+
75
+ @field_validator("prefixes", mode="before")
76
+ def parse_str(cls, values: Any) -> Any:
77
+ if isinstance(values, dict):
78
+ return {key: Namespace(value) if isinstance(value, str) else value for key, value in values.items()}
79
+ return values
80
+
81
+ @model_validator(mode="after")
82
+ def update_entities_prefix(self) -> Self:
83
+ # update expected_value_types
84
+ for property_ in self.properties:
85
+ if isinstance(property_.value_type, ClassEntity) and property_.value_type.prefix is Undefined:
86
+ property_.value_type.prefix = self.metadata.prefix
87
+
88
+ if isinstance(property_.value_type, MultiValueTypeInfo):
89
+ property_.value_type.set_default_prefix(self.metadata.prefix)
90
+
91
+ if property_.class_.prefix is Undefined:
92
+ property_.class_.prefix = self.metadata.prefix
93
+
94
+ # update parent classes
95
+ for class_ in self.classes:
96
+ if class_.parent:
97
+ for parent in cast(list[ParentClassEntity], class_.parent):
98
+ if not isinstance(parent.prefix, str):
99
+ parent.prefix = self.metadata.prefix
100
+ if class_.class_.prefix is Undefined:
101
+ class_.class_.prefix = self.metadata.prefix
102
+
103
+ return self
104
+
105
+ @model_validator(mode="after")
106
+ def post_validation(self) -> "AssetRules":
107
+ from ._validation import AssetPostValidation
108
+
109
+ issue_list = AssetPostValidation(cast(InformationRules, self)).validate()
110
+ if issue_list.warnings:
111
+ issue_list.trigger_warnings()
112
+ if issue_list.has_errors:
113
+ raise MultiValueError([error for error in issue_list if isinstance(error, issues.NeatValidationError)])
114
+ return self
115
+
116
+ def dump(
117
+ self,
118
+ mode: Literal["python", "json"] = "python",
119
+ by_alias: bool = False,
120
+ exclude: IncEx = None,
121
+ exclude_none: bool = False,
122
+ exclude_unset: bool = False,
123
+ exclude_defaults: bool = False,
124
+ as_reference: bool = False,
125
+ ) -> dict[str, Any]:
126
+ from ._serializer import _AssetRulesSerializer
127
+
128
+ dumped = self.model_dump(
129
+ mode=mode,
130
+ by_alias=by_alias,
131
+ exclude=exclude,
132
+ exclude_none=exclude_none,
133
+ exclude_unset=exclude_unset,
134
+ exclude_defaults=exclude_defaults,
135
+ )
136
+ prefix = self.metadata.prefix
137
+ serializer = _AssetRulesSerializer(by_alias, prefix)
138
+ cleaned = serializer.clean(dumped, as_reference)
139
+ last = "Last" if by_alias else "last"
140
+ if last_dump := cleaned.get(last):
141
+ cleaned[last] = serializer.clean(last_dump, False)
142
+ reference = "Reference" if by_alias else "reference"
143
+ if self.reference and (ref_dump := cleaned.get(reference)):
144
+ prefix = self.reference.metadata.prefix
145
+ cleaned[reference] = _AssetRulesSerializer(by_alias, prefix).clean(ref_dump, True)
146
+ return cleaned
147
+
148
+ def as_domain_rules(self) -> DomainRules:
149
+ from ._converter import _AssetRulesConverter
150
+
151
+ return _AssetRulesConverter(cast(InformationRules, self)).as_domain_rules()
152
+
153
+ def as_dms_architect_rules(self) -> "DMSRules":
154
+ from ._converter import _AssetRulesConverter
155
+
156
+ return _AssetRulesConverter(cast(InformationRules, self)).as_dms_architect_rules()