cognite-neat 0.97.2__py3-none-any.whl → 0.98.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 (66) hide show
  1. cognite/neat/_graph/loaders/__init__.py +1 -2
  2. cognite/neat/_graph/queries/_base.py +25 -4
  3. cognite/neat/_issues/warnings/_models.py +9 -0
  4. cognite/neat/_rules/_shared.py +3 -8
  5. cognite/neat/_rules/analysis/__init__.py +1 -2
  6. cognite/neat/_rules/analysis/_base.py +2 -23
  7. cognite/neat/_rules/analysis/_dms.py +4 -10
  8. cognite/neat/_rules/analysis/_information.py +2 -10
  9. cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
  10. cognite/neat/_rules/exporters/_rules2excel.py +15 -72
  11. cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
  12. cognite/neat/_rules/importers/_base.py +3 -4
  13. cognite/neat/_rules/importers/_dms2rules.py +17 -40
  14. cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
  15. cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
  16. cognite/neat/_rules/importers/_rdf/_base.py +17 -29
  17. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
  18. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
  19. cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
  20. cognite/neat/_rules/importers/_rdf/_inference2rules.py +30 -18
  21. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
  22. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
  23. cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
  24. cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
  25. cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
  26. cognite/neat/_rules/models/__init__.py +2 -16
  27. cognite/neat/_rules/models/_base_rules.py +98 -52
  28. cognite/neat/_rules/models/dms/_exporter.py +7 -160
  29. cognite/neat/_rules/models/dms/_rules.py +18 -126
  30. cognite/neat/_rules/models/dms/_rules_input.py +20 -48
  31. cognite/neat/_rules/models/dms/_schema.py +11 -0
  32. cognite/neat/_rules/models/dms/_validation.py +9 -122
  33. cognite/neat/_rules/models/information/_rules.py +19 -114
  34. cognite/neat/_rules/models/information/_rules_input.py +32 -41
  35. cognite/neat/_rules/models/information/_validation.py +34 -102
  36. cognite/neat/_rules/transformers/__init__.py +1 -4
  37. cognite/neat/_rules/transformers/_converters.py +18 -195
  38. cognite/neat/_rules/transformers/_mapping.py +1 -5
  39. cognite/neat/_rules/transformers/_verification.py +0 -14
  40. cognite/neat/_session/_base.py +37 -13
  41. cognite/neat/_session/_collector.py +126 -0
  42. cognite/neat/_session/_inspect.py +5 -5
  43. cognite/neat/_session/_prepare.py +37 -11
  44. cognite/neat/_session/_read.py +62 -9
  45. cognite/neat/_session/_set.py +2 -2
  46. cognite/neat/_session/_show.py +11 -11
  47. cognite/neat/_session/_to.py +24 -11
  48. cognite/neat/_session/exceptions.py +20 -3
  49. cognite/neat/_store/_provenance.py +2 -2
  50. cognite/neat/_utils/auxiliary.py +19 -0
  51. cognite/neat/_version.py +1 -1
  52. cognite/neat/_workflows/steps/data_contracts.py +2 -10
  53. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +6 -46
  54. cognite/neat/_workflows/steps/lib/current/rules_validator.py +2 -7
  55. {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/METADATA +2 -1
  56. {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/RECORD +59 -65
  57. cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
  58. cognite/neat/_rules/analysis/_asset.py +0 -173
  59. cognite/neat/_rules/models/asset/__init__.py +0 -13
  60. cognite/neat/_rules/models/asset/_rules.py +0 -109
  61. cognite/neat/_rules/models/asset/_rules_input.py +0 -101
  62. cognite/neat/_rules/models/asset/_validation.py +0 -45
  63. cognite/neat/_rules/models/domain.py +0 -136
  64. {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/LICENSE +0 -0
  65. {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/WHEEL +0 -0
  66. {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/entry_points.txt +0 -0
@@ -7,7 +7,6 @@ from cognite.neat._constants import COGNITE_MODELS, DMS_CONTAINER_PROPERTY_SIZE_
7
7
  from cognite.neat._issues import IssueList, NeatError, NeatIssue, NeatIssueList
8
8
  from cognite.neat._issues.errors import (
9
9
  PropertyDefinitionDuplicatedError,
10
- ResourceChangedError,
11
10
  ResourceNotDefinedError,
12
11
  )
13
12
  from cognite.neat._issues.errors._properties import ReversedConnectionNotFeasibleError
@@ -20,7 +19,6 @@ from cognite.neat._issues.warnings.user_modeling import (
20
19
  NotNeatSupportedFilterWarning,
21
20
  ViewPropertyLimitWarning,
22
21
  )
23
- from cognite.neat._rules.models._base_rules import DataModelType, ExtensionCategory, SchemaCompleteness
24
22
  from cognite.neat._rules.models.data_types import DataType
25
23
  from cognite.neat._rules.models.entities import ContainerEntity, RawFilter
26
24
  from cognite.neat._rules.models.entities._single_value import (
@@ -55,12 +53,7 @@ class DMSPostValidation:
55
53
  self._validate_reverse_connections()
56
54
 
57
55
  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
56
  dms_schema = self.rules.as_schema()
63
- self.issue_list.extend(dms_schema.validate())
64
57
  self._validate_performance(dms_schema)
65
58
  return self.issue_list
66
59
 
@@ -157,27 +150,13 @@ class DMSPostValidation:
157
150
  )
158
151
  )
159
152
 
160
- # This sets the container definition for all the properties where it is not defined.
161
- # This allows the user to define the container only once.
162
- value_type = next(iter(value_types))
163
- list_definition = next(iter(list_definitions)) if list_definitions else None
164
- nullable_definition = next(iter(nullable_definitions)) if nullable_definitions else None
165
- default_definition = next(iter(default_definitions)) if default_definitions else None
166
- index_definition = next(iter(index_definitions)).split(",") if index_definitions else None
167
- constraint_definition = next(iter(constraint_definitions)).split(",") if constraint_definitions else None
168
- for _, prop in properties:
169
- prop.value_type = value_type
170
- prop.is_list = prop.is_list or list_definition
171
- prop.nullable = prop.nullable or nullable_definition
172
- prop.default = prop.default or default_definition
173
- prop.index = prop.index or index_definition
174
- prop.constraint = prop.constraint or constraint_definition
175
153
  self.issue_list.extend(errors)
176
154
 
177
155
  def _referenced_views_and_containers_are_existing_and_proper_size(self) -> None:
156
+ # TODO: Split this method and keep only validation that should be independent of
157
+ # whether view and/or container exist in the pydantic model instance
158
+ # other validation should be done through NeatSession.verify()
178
159
  defined_views = {view.view.as_id() for view in self.views}
179
- if self.metadata.schema_ is SchemaCompleteness.extended and self.rules.last:
180
- defined_views |= {view.view.as_id() for view in self.rules.last.views}
181
160
 
182
161
  property_count_by_view: dict[dm.ViewId, int] = defaultdict(int)
183
162
  errors: list[NeatIssue] = []
@@ -199,103 +178,9 @@ class DMSPostValidation:
199
178
  for view_id, count in property_count_by_view.items():
200
179
  if count > DMS_CONTAINER_PROPERTY_SIZE_LIMIT:
201
180
  errors.append(ViewPropertyLimitWarning(view_id, count))
202
- if self.metadata.schema_ is SchemaCompleteness.complete:
203
- defined_containers = {container.container.as_id() for container in self.containers or []}
204
- if self.metadata.data_model_type == DataModelType.solution and self.rules.reference:
205
- defined_containers |= {
206
- container.container.as_id() for container in self.rules.reference.containers or []
207
- }
208
181
 
209
- for prop_no, prop in enumerate(self.properties):
210
- if prop.container and (container_id := prop.container.as_id()) not in defined_containers:
211
- errors.append(
212
- ResourceNotDefinedError(
213
- identifier=container_id,
214
- resource_type="container",
215
- location="Containers Sheet",
216
- column_name="Container",
217
- row_number=prop_no,
218
- sheet_name="Properties",
219
- )
220
- )
221
- for _container_no, container in enumerate(self.containers or []):
222
- for constraint_no, constraint in enumerate(container.constraint or []):
223
- if constraint.as_id() not in defined_containers:
224
- errors.append(
225
- ResourceNotDefinedError(
226
- identifier=constraint.as_id(),
227
- resource_type="container",
228
- location="Containers Sheet",
229
- column_name="Constraint",
230
- row_number=constraint_no,
231
- sheet_name="Properties",
232
- )
233
- )
234
182
  self.issue_list.extend(errors)
235
183
 
236
- def _validate_extension(self) -> None:
237
- if self.metadata.schema_ is not SchemaCompleteness.extended:
238
- return None
239
- if not self.rules.last:
240
- raise ValueError("The schema is set to 'extended', but no last rules are provided to validate against")
241
- if self.metadata.extension is ExtensionCategory.rebuild:
242
- # Everything is allowed
243
- return None
244
- user_schema = self.rules.as_schema()
245
- new_containers = user_schema.containers.copy()
246
-
247
- last_schema = self.rules.last.as_schema()
248
- existing_containers = last_schema.containers.copy()
249
-
250
- for container_id, container in new_containers.items():
251
- existing_container = existing_containers.get(container_id)
252
- if not existing_container or existing_container == container:
253
- # No problem
254
- continue
255
- new_dumped = container.dump()
256
- existing_dumped = existing_container.dump()
257
- changed_attributes, changed_properties = self._changed_attributes_and_properties(
258
- new_dumped, existing_dumped
259
- )
260
- self.issue_list.append(
261
- ResourceChangedError(
262
- container_id,
263
- "container",
264
- changed_properties=frozenset(changed_properties),
265
- changed_attributes=frozenset(changed_attributes),
266
- )
267
- )
268
-
269
- if self.metadata.extension is ExtensionCategory.reshape:
270
- # Reshape allows changes to views
271
- return None
272
-
273
- new_views = user_schema.views.copy()
274
- existing_views = last_schema.views.copy()
275
- for view_id, view in new_views.items():
276
- existing_view = existing_views.get(view_id)
277
- if not existing_view or existing_view == view:
278
- # No problem
279
- continue
280
- changed_attributes, changed_properties = self._changed_attributes_and_properties(
281
- view.dump(), existing_view.dump()
282
- )
283
- existing_properties = existing_view.properties or {}
284
- changed_properties = [prop for prop in changed_properties if prop in existing_properties]
285
- changed_attributes = [attr for attr in changed_attributes if attr not in self.changeable_view_attributes]
286
-
287
- if not changed_attributes and not changed_properties:
288
- # Only added new properties, no problem
289
- continue
290
- self.issue_list.append(
291
- ResourceChangedError(
292
- view_id,
293
- "view",
294
- changed_properties=frozenset(changed_properties),
295
- changed_attributes=frozenset(changed_attributes),
296
- )
297
- )
298
-
299
184
  def _validate_performance(self, dms_schema: DMSSchema) -> None:
300
185
  for view_id, view in dms_schema.views.items():
301
186
  mapped_containers = dms_schema._get_mapped_container_from_view(view_id)
@@ -335,7 +220,7 @@ class DMSPostValidation:
335
220
  UndefinedViewWarning(
336
221
  str(prop_.view),
337
222
  str(prop_.value_type),
338
- prop_.property_,
223
+ prop_.view_property,
339
224
  )
340
225
  )
341
226
 
@@ -344,7 +229,7 @@ class DMSPostValidation:
344
229
  if self.metadata.as_data_model_id() in COGNITE_MODELS:
345
230
  return None
346
231
 
347
- properties_by_ids = {f"{prop_.view!s}.{prop_.property_}": prop_ for prop_ in self.properties}
232
+ properties_by_ids = {f"{prop_.view!s}.{prop_.view_property}": prop_ for prop_ in self.properties}
348
233
  reversed_by_ids = {
349
234
  id_: prop_
350
235
  for id_, prop_ in properties_by_ids.items()
@@ -354,11 +239,12 @@ class DMSPostValidation:
354
239
  for id_, prop_ in reversed_by_ids.items():
355
240
  source_id = f"{prop_.value_type!s}." f"{cast(ReverseConnectionEntity, prop_.connection).property_}"
356
241
  if source_id not in properties_by_ids:
242
+ print(f"source_id: {source_id}, first issue")
357
243
  self.issue_list.append(
358
244
  ReversedConnectionNotFeasibleError(
359
245
  id_,
360
246
  "reversed connection",
361
- prop_.property_,
247
+ prop_.view_property,
362
248
  str(prop_.view),
363
249
  str(prop_.value_type),
364
250
  cast(ReverseConnectionEntity, prop_.connection).property_,
@@ -366,11 +252,12 @@ class DMSPostValidation:
366
252
  )
367
253
 
368
254
  elif source_id in properties_by_ids and properties_by_ids[source_id].value_type != prop_.view:
255
+ print(f"source_id: {source_id}, second issue")
369
256
  self.issue_list.append(
370
257
  ReversedConnectionNotFeasibleError(
371
258
  id_,
372
259
  "view property",
373
- prop_.property_,
260
+ prop_.view_property,
374
261
  str(prop_.view),
375
262
  str(prop_.value_type),
376
263
  cast(ReverseConnectionEntity, prop_.connection).property_,
@@ -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,16 @@ 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
17
  ClassRef,
19
- DataModelType,
20
- ExtensionCategory,
21
- ExtensionCategoryType,
22
- MatchType,
18
+ DataModelAspect,
23
19
  PropertyRef,
24
20
  RoleTypes,
25
- SchemaCompleteness,
26
21
  SheetList,
27
22
  SheetRow,
28
23
  )
@@ -35,10 +30,6 @@ from cognite.neat._rules.models._types import (
35
30
  ClassEntityType,
36
31
  InformationPropertyType,
37
32
  MultiValueTypeType,
38
- NamespaceType,
39
- PrefixType,
40
- StrListType,
41
- VersionType,
42
33
  )
43
34
  from cognite.neat._rules.models.data_types import DataType
44
35
  from cognite.neat._rules.models.entities import (
@@ -46,10 +37,8 @@ from cognite.neat._rules.models.entities import (
46
37
  ClassEntityList,
47
38
  Entity,
48
39
  MultiValueTypeInfo,
49
- ReferenceEntity,
50
40
  Undefined,
51
41
  UnknownEntity,
52
- URLEntity,
53
42
  )
54
43
 
55
44
  if TYPE_CHECKING:
@@ -64,62 +53,11 @@ else:
64
53
 
65
54
  class InformationMetadata(BaseMetadata):
66
55
  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())
56
+ aspect: ClassVar[DataModelAspect] = DataModelAspect.logical
109
57
 
110
- @field_validator("extension", mode="plain")
111
- def as_enum_extension(cls, value: str) -> ExtensionCategory:
112
- return ExtensionCategory(value.strip())
113
-
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
58
+ # Linking to Conceptual and Physical data model aspects
59
+ physical: URIRef | None = Field(None, description="Link to the logical data model aspect")
60
+ conceptual: URIRef | None = Field(None, description="Link to the logical data model aspect")
123
61
 
124
62
 
125
63
  def _get_metadata(context: Any) -> InformationMetadata | None:
@@ -135,42 +73,33 @@ class InformationClass(SheetRow):
135
73
  Args:
136
74
  class_: The class ID of the class.
137
75
  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.
76
+ implements: Which classes the current class implements.
141
77
  """
142
78
 
143
79
  class_: ClassEntityType = Field(alias="Class")
144
80
  name: str | None = Field(alias="Name", default=None)
145
81
  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)
82
+ implements: ClassEntityList | None = Field(alias="Implements", default=None)
150
83
 
151
84
  def _identifier(self) -> tuple[Hashable, ...]:
152
85
  return (self.class_,)
153
86
 
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
87
  @field_serializer("class_", when_used="unless-none")
161
88
  def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
162
89
  if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
163
90
  return value.dump(prefix=metadata.prefix, version=metadata.version)
164
91
  return str(value)
165
92
 
166
- @field_serializer("parent", when_used="unless-none")
93
+ @field_serializer("implements", when_used="unless-none")
167
94
  def remove_default_prefixes(self, value: Any, info: SerializationInfo) -> str:
168
95
  if isinstance(value, list) and (metadata := _get_metadata(info.context)):
169
96
  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
97
+ (
98
+ class_.dump(prefix=metadata.prefix, version=metadata.version)
99
+ if isinstance(class_, Entity)
100
+ else str(class_)
101
+ )
102
+ for class_ in value
174
103
  )
175
104
  return ",".join(str(value) for value in value)
176
105
 
@@ -191,8 +120,6 @@ class InformationProperty(SheetRow):
191
120
  min_count: Minimum count of the property values. Defaults to 0
192
121
  max_count: Maximum count of the property values. Defaults to None
193
122
  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
123
  transformation: Actual rule for the transformation from source to target representation of
197
124
  knowledge graph. Defaults to None (no transformation)
198
125
  """
@@ -207,10 +134,7 @@ class InformationProperty(SheetRow):
207
134
  min_count: int | None = Field(alias="Min Count", default=None)
208
135
  max_count: int | float | None = Field(alias="Max Count", default=None)
209
136
  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
137
  transformation: RDFPath | None = Field(alias="Transformation", default=None)
213
- comment: str | None = Field(alias="Comment", default=None)
214
138
  inherited: bool = Field(
215
139
  default=False,
216
140
  exclude=True,
@@ -281,19 +205,6 @@ class InformationProperty(SheetRow):
281
205
  return None
282
206
  return value
283
207
 
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
208
  @field_serializer("class_", "value_type", when_used="unless-none")
298
209
  def remove_default_prefix(self, value: Any, info: SerializationInfo) -> str:
299
210
  if (metadata := _get_metadata(info.context)) and isinstance(value, Entity):
@@ -331,8 +242,6 @@ class InformationRules(BaseRules):
331
242
  properties: SheetList[InformationProperty] = Field(alias="Properties")
332
243
  classes: SheetList[InformationClass] = Field(alias="Classes")
333
244
  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
245
 
337
246
  @field_validator("prefixes", mode="before")
338
247
  def parse_str(cls, values: Any) -> Any:
@@ -355,10 +264,10 @@ class InformationRules(BaseRules):
355
264
  if property_.class_.prefix is Undefined:
356
265
  property_.class_.prefix = self.metadata.prefix
357
266
 
358
- # update parent classes
267
+ # update implements
359
268
  for class_ in self.classes:
360
- if class_.parent:
361
- for parent in class_.parent:
269
+ if class_.implements:
270
+ for parent in class_.implements:
362
271
  if not isinstance(parent.prefix, str):
363
272
  parent.prefix = self.metadata.prefix
364
273
  if class_.class_.prefix is Undefined:
@@ -387,14 +296,10 @@ class InformationRules(BaseRules):
387
296
  "type": "Logical Data Model",
388
297
  "intended for": "Information Architect",
389
298
  "name": self.metadata.name,
390
- "external_id": self.metadata.prefix,
299
+ "external_id": self.metadata.external_id,
391
300
  "version": self.metadata.version,
392
301
  "classes": len(self.classes),
393
302
  "properties": len(self.properties),
394
303
  }
395
304
 
396
305
  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}"]
@@ -1,6 +1,6 @@
1
1
  from dataclasses import dataclass, field
2
2
  from datetime import datetime
3
- from typing import Any, Literal
3
+ from typing import Any
4
4
 
5
5
  import pandas as pd
6
6
  from rdflib import Namespace, URIRef
@@ -25,19 +25,16 @@ from ._rules import (
25
25
 
26
26
  @dataclass
27
27
  class InformationInputMetadata(InputComponent[InformationMetadata]):
28
- schema_: Literal["complete", "partial", "extended"]
29
- prefix: str
30
- namespace: str
28
+ space: str
29
+ external_id: str
31
30
  version: str
32
31
  creator: str
33
- data_model_type: Literal["solution", "enterprise"] = "enterprise"
34
- extension: Literal["addition", "reshape", "rebuild"] = "addition"
35
32
  name: str | None = None
36
33
  description: str | None = None
37
34
  created: datetime | str | None = None
38
35
  updated: datetime | str | None = None
39
- license: str | None = None
40
- rights: str | None = None
36
+ physical: str | None = None
37
+ conceptual: str | None = None
41
38
 
42
39
  @classmethod
43
40
  def _get_verified_cls(cls) -> type[InformationMetadata]:
@@ -51,6 +48,25 @@ class InformationInputMetadata(InputComponent[InformationMetadata]):
51
48
  output["updated"] = datetime.now()
52
49
  return output
53
50
 
51
+ @property
52
+ def prefix(self) -> str:
53
+ return self.space
54
+
55
+ @property
56
+ def identifier(self) -> URIRef:
57
+ """Globally unique identifier for the data model.
58
+
59
+ !!! note
60
+ Unlike namespace, the identifier does not end with "/" or "#".
61
+
62
+ """
63
+ return DEFAULT_NAMESPACE[f"data-model/unverified/logical/{self.space}/{self.external_id}/{self.version}"]
64
+
65
+ @property
66
+ def namespace(self) -> Namespace:
67
+ """Namespace for the data model used for the entities in the data model."""
68
+ return Namespace(f"{self.identifier}/")
69
+
54
70
 
55
71
  @dataclass
56
72
  class InformationInputProperty(InputComponent[InformationProperty]):
@@ -59,12 +75,9 @@ class InformationInputProperty(InputComponent[InformationProperty]):
59
75
  value_type: DataType | ClassEntity | MultiValueTypeInfo | UnknownEntity | str
60
76
  name: str | None = None
61
77
  description: str | None = None
62
- comment: str | None = None
63
78
  min_count: int | None = None
64
79
  max_count: int | float | None = None
65
80
  default: Any | None = None
66
- reference: str | None = None
67
- match_type: str | None = None
68
81
  transformation: str | None = None
69
82
  # Only used internally
70
83
  inherited: bool = False
@@ -85,10 +98,7 @@ class InformationInputClass(InputComponent[InformationClass]):
85
98
  class_: ClassEntity | str
86
99
  name: str | None = None
87
100
  description: str | None = None
88
- comment: str | None = None
89
- parent: str | list[ClassEntity] | None = None
90
- reference: str | None = None
91
- match_type: str | None = None
101
+ implements: str | list[ClassEntity] | None = None
92
102
 
93
103
  @classmethod
94
104
  def _get_verified_cls(cls) -> type[InformationClass]:
@@ -101,12 +111,12 @@ class InformationInputClass(InputComponent[InformationClass]):
101
111
  def dump(self, default_prefix: str, **kwargs) -> dict[str, Any]: # type: ignore[override]
102
112
  output = super().dump()
103
113
  parent: list[ClassEntity] | None = None
104
- if isinstance(self.parent, str):
105
- parent = [ClassEntity.load(parent, prefix=default_prefix) for parent in self.parent.split(",")]
106
- elif isinstance(self.parent, list):
107
- parent = [ClassEntity.load(parent_, prefix=default_prefix) for parent_ in self.parent]
114
+ if isinstance(self.implements, str):
115
+ parent = [ClassEntity.load(parent, prefix=default_prefix) for parent in self.implements.split(",")]
116
+ elif isinstance(self.implements, list):
117
+ parent = [ClassEntity.load(parent_, prefix=default_prefix) for parent_ in self.implements]
108
118
  output["Class"] = ClassEntity.load(self.class_, prefix=default_prefix)
109
- output["Parent Class"] = parent
119
+ output["Implements"] = parent
110
120
  return output
111
121
 
112
122
 
@@ -116,8 +126,6 @@ class InformationInputRules(InputRules[InformationRules]):
116
126
  properties: list[InformationInputProperty] = field(default_factory=list)
117
127
  classes: list[InformationInputClass] = field(default_factory=list)
118
128
  prefixes: dict[str, Namespace] | None = None
119
- last: "InformationInputRules | None" = None
120
- reference: "InformationInputRules | None" = None
121
129
 
122
130
  @classmethod
123
131
  def _get_verified_cls(cls) -> type[InformationRules]:
@@ -125,26 +133,12 @@ class InformationInputRules(InputRules[InformationRules]):
125
133
 
126
134
  def dump(self) -> dict[str, Any]:
127
135
  default_prefix = self.metadata.prefix
128
- reference: dict[str, Any] | None = None
129
- if isinstance(self.reference, InformationInputRules):
130
- reference = self.reference.dump()
131
- elif isinstance(self.reference, InformationRules):
132
- # We need to load through the InformationRulesInput to set the correct default space and version
133
- reference = InformationInputRules.load(self.reference.model_dump()).dump()
134
- last: dict[str, Any] | None = None
135
- if isinstance(self.last, InformationInputRules):
136
- last = self.last.dump()
137
- elif isinstance(self.last, InformationRules):
138
- # We need to load through the InformationRulesInput to set the correct default space and version
139
- last = InformationInputRules.load(self.last.model_dump()).dump()
140
136
 
141
137
  return dict(
142
138
  Metadata=self.metadata.dump(),
143
139
  Properties=[prop.dump(default_prefix) for prop in self.properties],
144
140
  Classes=[class_.dump(default_prefix) for class_ in self.classes],
145
141
  Prefixes=self.prefixes,
146
- Last=last,
147
- Reference=reference,
148
142
  )
149
143
 
150
144
  def _repr_html_(self) -> str:
@@ -152,14 +146,11 @@ class InformationInputRules(InputRules[InformationRules]):
152
146
  "type": "Logical Data Model",
153
147
  "intended for": "Information Architect",
154
148
  "name": self.metadata.name,
155
- "external_id": self.metadata.prefix,
149
+ "external_id": self.metadata.external_id,
150
+ "space": self.metadata.space,
156
151
  "version": self.metadata.version,
157
152
  "classes": len(self.classes),
158
153
  "properties": len(self.properties),
159
154
  }
160
155
 
161
156
  return pd.DataFrame([summary]).T.rename(columns={0: ""})._repr_html_() # type: ignore
162
-
163
- @property
164
- def id_(self) -> URIRef:
165
- return DEFAULT_NAMESPACE[f"data-model/unverified/info/{self.metadata.prefix}/{self.metadata.version}"]