cognite-neat 0.97.3__py3-none-any.whl → 0.99.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 (109) hide show
  1. cognite/neat/_client/__init__.py +4 -0
  2. cognite/neat/_client/_api/data_modeling_loaders.py +512 -0
  3. cognite/neat/_client/_api/schema.py +50 -0
  4. cognite/neat/_client/_api_client.py +17 -0
  5. cognite/neat/_client/data_classes/__init__.py +0 -0
  6. cognite/neat/{_utils/cdf/data_classes.py → _client/data_classes/data_modeling.py} +8 -135
  7. cognite/neat/{_rules/models/dms/_schema.py → _client/data_classes/schema.py} +32 -281
  8. cognite/neat/_graph/_shared.py +14 -15
  9. cognite/neat/_graph/extractors/_classic_cdf/_assets.py +14 -154
  10. cognite/neat/_graph/extractors/_classic_cdf/_base.py +154 -7
  11. cognite/neat/_graph/extractors/_classic_cdf/_classic.py +23 -12
  12. cognite/neat/_graph/extractors/_classic_cdf/_data_sets.py +17 -92
  13. cognite/neat/_graph/extractors/_classic_cdf/_events.py +13 -162
  14. cognite/neat/_graph/extractors/_classic_cdf/_files.py +15 -179
  15. cognite/neat/_graph/extractors/_classic_cdf/_labels.py +32 -100
  16. cognite/neat/_graph/extractors/_classic_cdf/_relationships.py +27 -178
  17. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +14 -139
  18. cognite/neat/_graph/extractors/_classic_cdf/_timeseries.py +15 -173
  19. cognite/neat/_graph/extractors/_rdf_file.py +6 -7
  20. cognite/neat/_graph/loaders/__init__.py +1 -2
  21. cognite/neat/_graph/queries/_base.py +17 -1
  22. cognite/neat/_graph/transformers/_classic_cdf.py +50 -134
  23. cognite/neat/_graph/transformers/_prune_graph.py +1 -1
  24. cognite/neat/_graph/transformers/_rdfpath.py +1 -1
  25. cognite/neat/_issues/warnings/__init__.py +6 -0
  26. cognite/neat/_issues/warnings/_external.py +8 -0
  27. cognite/neat/_issues/warnings/_models.py +9 -0
  28. cognite/neat/_issues/warnings/_properties.py +16 -0
  29. cognite/neat/_rules/_constants.py +7 -6
  30. cognite/neat/_rules/_shared.py +3 -8
  31. cognite/neat/_rules/analysis/__init__.py +1 -2
  32. cognite/neat/_rules/analysis/_base.py +10 -27
  33. cognite/neat/_rules/analysis/_dms.py +4 -10
  34. cognite/neat/_rules/analysis/_information.py +2 -10
  35. cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
  36. cognite/neat/_rules/exporters/_base.py +3 -4
  37. cognite/neat/_rules/exporters/_rules2dms.py +29 -40
  38. cognite/neat/_rules/exporters/_rules2excel.py +15 -72
  39. cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
  40. cognite/neat/_rules/importers/_base.py +3 -4
  41. cognite/neat/_rules/importers/_dms2rules.py +21 -45
  42. cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
  43. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
  44. cognite/neat/_rules/importers/_rdf/_base.py +17 -29
  45. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
  46. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
  47. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
  48. cognite/neat/_rules/importers/_rdf/_inference2rules.py +55 -51
  49. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
  50. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
  51. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
  52. cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
  53. cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
  54. cognite/neat/_rules/models/__init__.py +3 -17
  55. cognite/neat/_rules/models/_base_rules.py +118 -62
  56. cognite/neat/_rules/models/dms/__init__.py +2 -2
  57. cognite/neat/_rules/models/dms/_exporter.py +20 -178
  58. cognite/neat/_rules/models/dms/_rules.py +65 -128
  59. cognite/neat/_rules/models/dms/_rules_input.py +72 -56
  60. cognite/neat/_rules/models/dms/_validation.py +16 -109
  61. cognite/neat/_rules/models/entities/_single_value.py +32 -4
  62. cognite/neat/_rules/models/information/_rules.py +19 -122
  63. cognite/neat/_rules/models/information/_rules_input.py +32 -41
  64. cognite/neat/_rules/models/information/_validation.py +34 -102
  65. cognite/neat/_rules/models/mapping/__init__.py +2 -3
  66. cognite/neat/_rules/models/mapping/_classic2core.py +36 -146
  67. cognite/neat/_rules/models/mapping/_classic2core.yaml +339 -0
  68. cognite/neat/_rules/transformers/__init__.py +3 -6
  69. cognite/neat/_rules/transformers/_converters.py +128 -206
  70. cognite/neat/_rules/transformers/_mapping.py +105 -34
  71. cognite/neat/_rules/transformers/_verification.py +5 -16
  72. cognite/neat/_session/_base.py +83 -21
  73. cognite/neat/_session/_collector.py +126 -0
  74. cognite/neat/_session/_drop.py +35 -0
  75. cognite/neat/_session/_inspect.py +22 -10
  76. cognite/neat/_session/_mapping.py +39 -0
  77. cognite/neat/_session/_prepare.py +222 -27
  78. cognite/neat/_session/_read.py +109 -19
  79. cognite/neat/_session/_set.py +2 -2
  80. cognite/neat/_session/_show.py +11 -11
  81. cognite/neat/_session/_to.py +27 -14
  82. cognite/neat/_session/exceptions.py +20 -3
  83. cognite/neat/_store/_base.py +27 -24
  84. cognite/neat/_store/_provenance.py +2 -2
  85. cognite/neat/_utils/auxiliary.py +19 -0
  86. cognite/neat/_utils/rdf_.py +28 -1
  87. cognite/neat/_version.py +1 -1
  88. cognite/neat/_workflows/steps/data_contracts.py +2 -10
  89. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +14 -49
  90. cognite/neat/_workflows/steps/lib/current/rules_importer.py +4 -1
  91. cognite/neat/_workflows/steps/lib/current/rules_validator.py +5 -9
  92. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/METADATA +4 -3
  93. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/RECORD +97 -100
  94. cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
  95. cognite/neat/_rules/analysis/_asset.py +0 -173
  96. cognite/neat/_rules/models/asset/__init__.py +0 -13
  97. cognite/neat/_rules/models/asset/_rules.py +0 -109
  98. cognite/neat/_rules/models/asset/_rules_input.py +0 -101
  99. cognite/neat/_rules/models/asset/_validation.py +0 -45
  100. cognite/neat/_rules/models/domain.py +0 -136
  101. cognite/neat/_rules/models/mapping/_base.py +0 -131
  102. cognite/neat/_utils/cdf/loaders/__init__.py +0 -25
  103. cognite/neat/_utils/cdf/loaders/_base.py +0 -54
  104. cognite/neat/_utils/cdf/loaders/_data_modeling.py +0 -339
  105. cognite/neat/_utils/cdf/loaders/_ingestion.py +0 -167
  106. /cognite/neat/{_utils/cdf → _client/_api}/__init__.py +0 -0
  107. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/LICENSE +0 -0
  108. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/WHEEL +0 -0
  109. {cognite_neat-0.97.3.dist-info → cognite_neat-0.99.0.dist-info}/entry_points.txt +0 -0
@@ -3,11 +3,11 @@ from typing import Any, ClassVar, cast
3
3
 
4
4
  from cognite.client import data_modeling as dm
5
5
 
6
+ from cognite.neat._client.data_classes.schema import DMSSchema
6
7
  from cognite.neat._constants import COGNITE_MODELS, DMS_CONTAINER_PROPERTY_SIZE_LIMIT
7
8
  from cognite.neat._issues import IssueList, NeatError, NeatIssue, NeatIssueList
8
9
  from cognite.neat._issues.errors import (
9
10
  PropertyDefinitionDuplicatedError,
10
- ResourceChangedError,
11
11
  ResourceNotDefinedError,
12
12
  )
13
13
  from cognite.neat._issues.errors._properties import ReversedConnectionNotFeasibleError
@@ -20,7 +20,7 @@ from cognite.neat._issues.warnings.user_modeling import (
20
20
  NotNeatSupportedFilterWarning,
21
21
  ViewPropertyLimitWarning,
22
22
  )
23
- from cognite.neat._rules.models._base_rules import DataModelType, ExtensionCategory, SchemaCompleteness
23
+ from cognite.neat._rules.analysis import DMSAnalysis
24
24
  from cognite.neat._rules.models.data_types import DataType
25
25
  from cognite.neat._rules.models.entities import ContainerEntity, RawFilter
26
26
  from cognite.neat._rules.models.entities._single_value import (
@@ -29,7 +29,6 @@ from cognite.neat._rules.models.entities._single_value import (
29
29
  )
30
30
 
31
31
  from ._rules import DMSProperty, DMSRules
32
- from ._schema import DMSSchema
33
32
 
34
33
 
35
34
  class DMSPostValidation:
@@ -47,6 +46,7 @@ class DMSPostValidation:
47
46
  self.containers = rules.containers
48
47
  self.views = rules.views
49
48
  self.issue_list = IssueList()
49
+ self.probe = DMSAnalysis(rules)
50
50
 
51
51
  def validate(self) -> NeatIssueList:
52
52
  self._validate_raw_filter()
@@ -55,12 +55,7 @@ class DMSPostValidation:
55
55
  self._validate_reverse_connections()
56
56
 
57
57
  self._referenced_views_and_containers_are_existing_and_proper_size()
58
- if self.metadata.schema_ is SchemaCompleteness.extended:
59
- self._validate_extension()
60
- if self.metadata.schema_ is SchemaCompleteness.partial:
61
- return self.issue_list
62
58
  dms_schema = self.rules.as_schema()
63
- self.issue_list.extend(dms_schema.validate())
64
59
  self._validate_performance(dms_schema)
65
60
  return self.issue_list
66
61
 
@@ -160,9 +155,10 @@ class DMSPostValidation:
160
155
  self.issue_list.extend(errors)
161
156
 
162
157
  def _referenced_views_and_containers_are_existing_and_proper_size(self) -> None:
158
+ # TODO: Split this method and keep only validation that should be independent of
159
+ # whether view and/or container exist in the pydantic model instance
160
+ # other validation should be done through NeatSession.verify()
163
161
  defined_views = {view.view.as_id() for view in self.views}
164
- if self.metadata.schema_ is SchemaCompleteness.extended and self.rules.last:
165
- defined_views |= {view.view.as_id() for view in self.rules.last.views}
166
162
 
167
163
  property_count_by_view: dict[dm.ViewId, int] = defaultdict(int)
168
164
  errors: list[NeatIssue] = []
@@ -170,7 +166,7 @@ class DMSPostValidation:
170
166
  view_id = prop.view.as_id()
171
167
  if view_id not in defined_views:
172
168
  errors.append(
173
- ResourceNotDefinedError[dm.ViewId](
169
+ ResourceNotDefinedError(
174
170
  identifier=view_id,
175
171
  resource_type="view",
176
172
  location="Views Sheet",
@@ -184,103 +180,9 @@ class DMSPostValidation:
184
180
  for view_id, count in property_count_by_view.items():
185
181
  if count > DMS_CONTAINER_PROPERTY_SIZE_LIMIT:
186
182
  errors.append(ViewPropertyLimitWarning(view_id, count))
187
- if self.metadata.schema_ is SchemaCompleteness.complete:
188
- defined_containers = {container.container.as_id() for container in self.containers or []}
189
- if self.metadata.data_model_type == DataModelType.solution and self.rules.reference:
190
- defined_containers |= {
191
- container.container.as_id() for container in self.rules.reference.containers or []
192
- }
193
183
 
194
- for prop_no, prop in enumerate(self.properties):
195
- if prop.container and (container_id := prop.container.as_id()) not in defined_containers:
196
- errors.append(
197
- ResourceNotDefinedError(
198
- identifier=container_id,
199
- resource_type="container",
200
- location="Containers Sheet",
201
- column_name="Container",
202
- row_number=prop_no,
203
- sheet_name="Properties",
204
- )
205
- )
206
- for _container_no, container in enumerate(self.containers or []):
207
- for constraint_no, constraint in enumerate(container.constraint or []):
208
- if constraint.as_id() not in defined_containers:
209
- errors.append(
210
- ResourceNotDefinedError(
211
- identifier=constraint.as_id(),
212
- resource_type="container",
213
- location="Containers Sheet",
214
- column_name="Constraint",
215
- row_number=constraint_no,
216
- sheet_name="Properties",
217
- )
218
- )
219
184
  self.issue_list.extend(errors)
220
185
 
221
- def _validate_extension(self) -> None:
222
- if self.metadata.schema_ is not SchemaCompleteness.extended:
223
- return None
224
- if not self.rules.last:
225
- raise ValueError("The schema is set to 'extended', but no last rules are provided to validate against")
226
- if self.metadata.extension is ExtensionCategory.rebuild:
227
- # Everything is allowed
228
- return None
229
- user_schema = self.rules.as_schema()
230
- new_containers = user_schema.containers.copy()
231
-
232
- last_schema = self.rules.last.as_schema()
233
- existing_containers = last_schema.containers.copy()
234
-
235
- for container_id, container in new_containers.items():
236
- existing_container = existing_containers.get(container_id)
237
- if not existing_container or existing_container == container:
238
- # No problem
239
- continue
240
- new_dumped = container.dump()
241
- existing_dumped = existing_container.dump()
242
- changed_attributes, changed_properties = self._changed_attributes_and_properties(
243
- new_dumped, existing_dumped
244
- )
245
- self.issue_list.append(
246
- ResourceChangedError(
247
- container_id,
248
- "container",
249
- changed_properties=frozenset(changed_properties),
250
- changed_attributes=frozenset(changed_attributes),
251
- )
252
- )
253
-
254
- if self.metadata.extension is ExtensionCategory.reshape:
255
- # Reshape allows changes to views
256
- return None
257
-
258
- new_views = user_schema.views.copy()
259
- existing_views = last_schema.views.copy()
260
- for view_id, view in new_views.items():
261
- existing_view = existing_views.get(view_id)
262
- if not existing_view or existing_view == view:
263
- # No problem
264
- continue
265
- changed_attributes, changed_properties = self._changed_attributes_and_properties(
266
- view.dump(), existing_view.dump()
267
- )
268
- existing_properties = existing_view.properties or {}
269
- changed_properties = [prop for prop in changed_properties if prop in existing_properties]
270
- changed_attributes = [attr for attr in changed_attributes if attr not in self.changeable_view_attributes]
271
-
272
- if not changed_attributes and not changed_properties:
273
- # Only added new properties, no problem
274
- continue
275
- self.issue_list.append(
276
- ResourceChangedError(
277
- view_id,
278
- "view",
279
- changed_properties=frozenset(changed_properties),
280
- changed_attributes=frozenset(changed_attributes),
281
- )
282
- )
283
-
284
186
  def _validate_performance(self, dms_schema: DMSSchema) -> None:
285
187
  for view_id, view in dms_schema.views.items():
286
188
  mapped_containers = dms_schema._get_mapped_container_from_view(view_id)
@@ -320,7 +222,7 @@ class DMSPostValidation:
320
222
  UndefinedViewWarning(
321
223
  str(prop_.view),
322
224
  str(prop_.value_type),
323
- prop_.property_,
225
+ prop_.view_property,
324
226
  )
325
227
  )
326
228
 
@@ -329,7 +231,12 @@ class DMSPostValidation:
329
231
  if self.metadata.as_data_model_id() in COGNITE_MODELS:
330
232
  return None
331
233
 
332
- properties_by_ids = {f"{prop_.view!s}.{prop_.property_}": prop_ for prop_ in self.properties}
234
+ properties_by_ids = {
235
+ f"{prop_.view!s}.{prop_.view_property}": prop_
236
+ for properties in self.probe.classes_with_properties(True, True).values()
237
+ for prop_ in properties
238
+ }
239
+
333
240
  reversed_by_ids = {
334
241
  id_: prop_
335
242
  for id_, prop_ in properties_by_ids.items()
@@ -343,7 +250,7 @@ class DMSPostValidation:
343
250
  ReversedConnectionNotFeasibleError(
344
251
  id_,
345
252
  "reversed connection",
346
- prop_.property_,
253
+ prop_.view_property,
347
254
  str(prop_.view),
348
255
  str(prop_.value_type),
349
256
  cast(ReverseConnectionEntity, prop_.connection).property_,
@@ -355,7 +262,7 @@ class DMSPostValidation:
355
262
  ReversedConnectionNotFeasibleError(
356
263
  id_,
357
264
  "view property",
358
- prop_.property_,
265
+ prop_.view_property,
359
266
  str(prop_.view),
360
267
  str(prop_.value_type),
361
268
  cast(ReverseConnectionEntity, prop_.connection).property_,
@@ -3,7 +3,7 @@ import sys
3
3
  from abc import ABC, abstractmethod
4
4
  from functools import total_ordering
5
5
  from types import UnionType
6
- from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, cast, get_args, get_origin
6
+ from typing import Any, ClassVar, Generic, Literal, TypeVar, Union, cast, get_args, get_origin, overload
7
7
 
8
8
  from cognite.client.data_classes.data_modeling import DirectRelationReference
9
9
  from cognite.client.data_classes.data_modeling.data_types import UnitReference
@@ -22,6 +22,7 @@ from pydantic import (
22
22
  model_validator,
23
23
  )
24
24
 
25
+ from cognite.neat._issues.errors import NeatValueError
25
26
  from cognite.neat._utils.text import replace_non_alphanumeric_with_underscore
26
27
 
27
28
  if sys.version_info >= (3, 11):
@@ -56,14 +57,29 @@ class Entity(BaseModel, extra="ignore"):
56
57
  suffix: str
57
58
 
58
59
  @classmethod
59
- def load(cls: "type[T_Entity]", data: Any, **defaults) -> "T_Entity | UnknownEntity":
60
+ @overload
61
+ def load(cls: "type[T_Entity]", data: Any, strict: Literal[True], **defaults) -> "T_Entity": ...
62
+
63
+ @classmethod
64
+ @overload
65
+ def load(
66
+ cls: "type[T_Entity]", data: Any, strict: Literal[False] = False, **defaults
67
+ ) -> "T_Entity | UnknownEntity": ...
68
+
69
+ @classmethod
70
+ def load(cls: "type[T_Entity]", data: Any, strict: bool = False, **defaults) -> "T_Entity | UnknownEntity":
60
71
  if isinstance(data, cls):
61
72
  return data
62
73
  elif isinstance(data, str) and data == str(Unknown):
74
+ if strict:
75
+ raise NeatValueError(f"Failed to load entity {data!s}")
63
76
  return UnknownEntity(prefix=Undefined, suffix=Unknown)
64
77
  if defaults and isinstance(defaults, dict):
65
78
  # This is a trick to pass in default values
66
- return cls.model_validate({_PARSE: data, "defaults": defaults})
79
+ try:
80
+ return cls.model_validate({_PARSE: data, "defaults": defaults})
81
+ except ValueError:
82
+ raise
67
83
  else:
68
84
  return cls.model_validate(data)
69
85
 
@@ -291,9 +307,21 @@ class DMSEntity(Entity, Generic[T_ID], ABC):
291
307
  defaults["prefix"] = defaults.pop("space")
292
308
  return super().dump(**defaults)
293
309
 
310
+ @classmethod # type: ignore[override]
311
+ @overload
312
+ def load(cls: "type[T_DMSEntity]", data: Any, strict: Literal[True], **defaults) -> "T_DMSEntity": ...
313
+
314
+ @classmethod
315
+ @overload
316
+ def load(
317
+ cls: "type[T_DMSEntity]", data: Any, strict: Literal[False] = False, **defaults
318
+ ) -> "T_DMSEntity | DMSUnknownEntity": ...
319
+
294
320
  @classmethod
295
- def load(cls: "type[T_DMSEntity]", data: Any, **defaults) -> "T_DMSEntity | DMSUnknownEntity": # type: ignore[override]
321
+ def load(cls: "type[T_DMSEntity]", data: Any, strict: bool = False, **defaults) -> "T_DMSEntity | DMSUnknownEntity": # type: ignore[override]
296
322
  if isinstance(data, str) and data == str(Unknown):
323
+ if strict:
324
+ raise NeatValueError(f"Failed to load entity {data!s}")
297
325
  return DMSUnknownEntity.from_id(None)
298
326
  return cast(T_DMSEntity, super().load(data, **defaults))
299
327
 
@@ -1,7 +1,6 @@
1
1
  import math
2
2
  import sys
3
3
  from collections.abc import Hashable
4
- from datetime import datetime
5
4
  from typing import TYPE_CHECKING, Any, ClassVar
6
5
 
7
6
  import pandas as pd
@@ -9,20 +8,14 @@ from pydantic import Field, field_serializer, field_validator, model_validator
9
8
  from pydantic_core.core_schema import SerializationInfo
10
9
  from rdflib import Namespace, URIRef
11
10
 
12
- from cognite.neat._constants import DEFAULT_NAMESPACE, get_default_prefixes
11
+ from cognite.neat._constants import get_default_prefixes
13
12
  from cognite.neat._issues.errors import NeatValueError, PropertyDefinitionError
14
13
  from cognite.neat._rules._constants import EntityTypes
15
14
  from cognite.neat._rules.models._base_rules import (
16
15
  BaseMetadata,
17
16
  BaseRules,
18
- ClassRef,
19
- DataModelType,
20
- ExtensionCategory,
21
- ExtensionCategoryType,
22
- MatchType,
23
- PropertyRef,
17
+ DataModelAspect,
24
18
  RoleTypes,
25
- SchemaCompleteness,
26
19
  SheetList,
27
20
  SheetRow,
28
21
  )
@@ -35,10 +28,6 @@ from cognite.neat._rules.models._types import (
35
28
  ClassEntityType,
36
29
  InformationPropertyType,
37
30
  MultiValueTypeType,
38
- NamespaceType,
39
- PrefixType,
40
- StrListType,
41
- VersionType,
42
31
  )
43
32
  from cognite.neat._rules.models.data_types import DataType
44
33
  from cognite.neat._rules.models.entities import (
@@ -46,10 +35,8 @@ from cognite.neat._rules.models.entities import (
46
35
  ClassEntityList,
47
36
  Entity,
48
37
  MultiValueTypeInfo,
49
- ReferenceEntity,
50
38
  Undefined,
51
39
  UnknownEntity,
52
- URLEntity,
53
40
  )
54
41
 
55
42
  if TYPE_CHECKING:
@@ -64,62 +51,11 @@ else:
64
51
 
65
52
  class InformationMetadata(BaseMetadata):
66
53
  role: ClassVar[RoleTypes] = RoleTypes.information
67
- data_model_type: DataModelType = Field(DataModelType.enterprise, alias="dataModelType")
68
- schema_: SchemaCompleteness = Field(SchemaCompleteness.partial, alias="schema")
69
- extension: ExtensionCategoryType | None = ExtensionCategory.addition
70
-
71
- prefix: PrefixType
72
- namespace: NamespaceType
73
-
74
- name: str = Field(
75
- alias="title",
76
- description="Human readable name of the data model",
77
- min_length=1,
78
- max_length=255,
79
- )
80
- description: str | None = Field(None, min_length=1, max_length=1024)
81
- version: VersionType
82
-
83
- created: datetime = Field(
84
- description=("Date of the data model creation"),
85
- )
86
-
87
- updated: datetime = Field(
88
- description=("Date of the data model update"),
89
- )
90
- creator: StrListType = Field(
91
- description=(
92
- "List of contributors to the data model creation, "
93
- "typically information architects are considered as contributors."
94
- ),
95
- )
96
- license: str | None = None
97
- rights: str | None = None
98
-
99
- @model_validator(mode="after")
100
- def extension_none_but_schema_extend(self) -> Self:
101
- if self.extension is None:
102
- self.extension = ExtensionCategory.addition
103
- return self
104
- return self
105
-
106
- @field_validator("schema_", mode="plain")
107
- def as_enum_schema(cls, value: str) -> SchemaCompleteness:
108
- return SchemaCompleteness(value.strip())
109
-
110
- @field_validator("extension", mode="plain")
111
- def as_enum_extension(cls, value: str) -> ExtensionCategory:
112
- return ExtensionCategory(value.strip())
54
+ aspect: ClassVar[DataModelAspect] = DataModelAspect.logical
113
55
 
114
- @field_validator("data_model_type", mode="plain")
115
- def as_enum_model_type(cls, value: str) -> DataModelType:
116
- return DataModelType(value.strip())
117
-
118
- def as_identifier(self) -> str:
119
- return f"{self.prefix}:{self.name}"
120
-
121
- def get_prefix(self) -> str:
122
- return self.prefix
56
+ # Linking to Conceptual and Physical data model aspects
57
+ physical: URIRef | None = Field(None, description="Link to the logical data model aspect")
58
+ conceptual: URIRef | None = Field(None, description="Link to the logical data model aspect")
123
59
 
124
60
 
125
61
  def _get_metadata(context: Any) -> InformationMetadata | None:
@@ -135,48 +71,36 @@ class InformationClass(SheetRow):
135
71
  Args:
136
72
  class_: The class ID of the class.
137
73
  description: A description of the class.
138
- parent: The parent class of the class.
139
- reference: Reference of the source of the information for given resource
140
- match_type: The match type of the resource being described and the source entity.
74
+ implements: Which classes the current class implements.
141
75
  """
142
76
 
143
77
  class_: ClassEntityType = Field(alias="Class")
144
78
  name: str | None = Field(alias="Name", default=None)
145
79
  description: str | None = Field(alias="Description", default=None)
146
- parent: ClassEntityList | None = Field(alias="Parent Class", default=None)
147
- reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
148
- match_type: MatchType | None = Field(alias="Match Type", default=None)
149
- comment: str | None = Field(alias="Comment", default=None)
80
+ implements: ClassEntityList | None = Field(alias="Implements", default=None)
150
81
 
151
82
  def _identifier(self) -> tuple[Hashable, ...]:
152
83
  return (self.class_,)
153
84
 
154
- @field_serializer("reference", when_used="always")
155
- def set_reference(self, value: Any, info: SerializationInfo) -> str | None:
156
- if isinstance(info.context, dict) and info.context.get("as_reference") is True:
157
- return self.class_.dump()
158
- return str(value) if value is not None else None
159
-
160
85
  @field_serializer("class_", when_used="unless-none")
161
86
  def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
162
87
  if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
163
88
  return value.dump(prefix=metadata.prefix, version=metadata.version)
164
89
  return str(value)
165
90
 
166
- @field_serializer("parent", when_used="unless-none")
91
+ @field_serializer("implements", when_used="unless-none")
167
92
  def remove_default_prefixes(self, value: Any, info: SerializationInfo) -> str:
168
93
  if isinstance(value, list) and (metadata := _get_metadata(info.context)):
169
94
  return ",".join(
170
- parent.dump(prefix=metadata.prefix, version=metadata.version)
171
- if isinstance(parent, Entity)
172
- else str(parent)
173
- for parent in value
95
+ (
96
+ class_.dump(prefix=metadata.prefix, version=metadata.version)
97
+ if isinstance(class_, Entity)
98
+ else str(class_)
99
+ )
100
+ for class_ in value
174
101
  )
175
102
  return ",".join(str(value) for value in value)
176
103
 
177
- def as_reference(self) -> ClassRef:
178
- return ClassRef(Class=self.class_)
179
-
180
104
 
181
105
  class InformationProperty(SheetRow):
182
106
  """
@@ -191,8 +115,6 @@ class InformationProperty(SheetRow):
191
115
  min_count: Minimum count of the property values. Defaults to 0
192
116
  max_count: Maximum count of the property values. Defaults to None
193
117
  default: Default value of the property
194
- reference: Reference to the source of the information, HTTP URI
195
- match_type: The match type of the resource being described and the source entity.
196
118
  transformation: Actual rule for the transformation from source to target representation of
197
119
  knowledge graph. Defaults to None (no transformation)
198
120
  """
@@ -207,10 +129,7 @@ class InformationProperty(SheetRow):
207
129
  min_count: int | None = Field(alias="Min Count", default=None)
208
130
  max_count: int | float | None = Field(alias="Max Count", default=None)
209
131
  default: Any | None = Field(alias="Default", default=None)
210
- reference: URLEntity | ReferenceEntity | None = Field(alias="Reference", default=None, union_mode="left_to_right")
211
- match_type: MatchType | None = Field(alias="Match Type", default=None)
212
132
  transformation: RDFPath | None = Field(alias="Transformation", default=None)
213
- comment: str | None = Field(alias="Comment", default=None)
214
133
  inherited: bool = Field(
215
134
  default=False,
216
135
  exclude=True,
@@ -281,19 +200,6 @@ class InformationProperty(SheetRow):
281
200
  return None
282
201
  return value
283
202
 
284
- @field_serializer("reference", when_used="always")
285
- def set_reference(self, value: Any, info: SerializationInfo) -> str | None:
286
- # When rules as dumped as reference, we set the reference to the class
287
- if isinstance(info.context, dict) and info.context.get("as_reference") is True:
288
- return str(
289
- ReferenceEntity(
290
- prefix=str(self.class_.prefix),
291
- suffix=self.class_.suffix,
292
- property=self.property_,
293
- )
294
- )
295
- return str(value) if value is not None else None
296
-
297
203
  @field_serializer("class_", "value_type", when_used="unless-none")
298
204
  def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
299
205
  if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
@@ -322,17 +228,12 @@ class InformationProperty(SheetRow):
322
228
  isinstance(self.max_count, int | float) and self.max_count > 1
323
229
  )
324
230
 
325
- def as_reference(self) -> PropertyRef:
326
- return PropertyRef(Class=self.class_, Property=self.property_)
327
-
328
231
 
329
232
  class InformationRules(BaseRules):
330
233
  metadata: InformationMetadata = Field(alias="Metadata")
331
234
  properties: SheetList[InformationProperty] = Field(alias="Properties")
332
235
  classes: SheetList[InformationClass] = Field(alias="Classes")
333
236
  prefixes: dict[str, Namespace] = Field(default_factory=get_default_prefixes, alias="Prefixes")
334
- last: "InformationRules | None" = Field(None, alias="Last")
335
- reference: "InformationRules | None" = Field(None, alias="Reference")
336
237
 
337
238
  @field_validator("prefixes", mode="before")
338
239
  def parse_str(cls, values: Any) -> Any:
@@ -355,10 +256,10 @@ class InformationRules(BaseRules):
355
256
  if property_.class_.prefix is Undefined:
356
257
  property_.class_.prefix = self.metadata.prefix
357
258
 
358
- # update parent classes
259
+ # update implements
359
260
  for class_ in self.classes:
360
- if class_.parent:
361
- for parent in class_.parent:
261
+ if class_.implements:
262
+ for parent in class_.implements:
362
263
  if not isinstance(parent.prefix, str):
363
264
  parent.prefix = self.metadata.prefix
364
265
  if class_.class_.prefix is Undefined:
@@ -387,14 +288,10 @@ class InformationRules(BaseRules):
387
288
  "type": "Logical Data Model",
388
289
  "intended for": "Information Architect",
389
290
  "name": self.metadata.name,
390
- "external_id": self.metadata.prefix,
291
+ "external_id": self.metadata.external_id,
391
292
  "version": self.metadata.version,
392
293
  "classes": len(self.classes),
393
294
  "properties": len(self.properties),
394
295
  }
395
296
 
396
297
  return pd.DataFrame([summary]).T.rename(columns={0: ""})._repr_html_() # type: ignore
397
-
398
- @property
399
- def id_(self) -> URIRef:
400
- return DEFAULT_NAMESPACE[f"data-model/verified/info/{self.metadata.prefix}/{self.metadata.version}"]