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.
- cognite/neat/_graph/loaders/__init__.py +1 -2
- cognite/neat/_graph/queries/_base.py +25 -4
- cognite/neat/_issues/warnings/_models.py +9 -0
- cognite/neat/_rules/_shared.py +3 -8
- cognite/neat/_rules/analysis/__init__.py +1 -2
- cognite/neat/_rules/analysis/_base.py +2 -23
- cognite/neat/_rules/analysis/_dms.py +4 -10
- cognite/neat/_rules/analysis/_information.py +2 -10
- cognite/neat/_rules/catalog/info-rules-imf.xlsx +0 -0
- cognite/neat/_rules/exporters/_rules2excel.py +15 -72
- cognite/neat/_rules/exporters/_rules2ontology.py +4 -4
- cognite/neat/_rules/importers/_base.py +3 -4
- cognite/neat/_rules/importers/_dms2rules.py +17 -40
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_converter.py +1 -7
- cognite/neat/_rules/importers/_dtdl2rules/dtdl_importer.py +7 -10
- cognite/neat/_rules/importers/_rdf/_base.py +17 -29
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2metadata.py +5 -10
- cognite/neat/_rules/importers/_rdf/_imf2rules/_imf2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_inference2rules.py +30 -18
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2classes.py +2 -2
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2metadata.py +5 -8
- cognite/neat/_rules/importers/_rdf/_owl2rules/_owl2properties.py +1 -2
- cognite/neat/_rules/importers/_rdf/_shared.py +25 -140
- cognite/neat/_rules/importers/_spreadsheet2rules.py +10 -41
- cognite/neat/_rules/models/__init__.py +2 -16
- cognite/neat/_rules/models/_base_rules.py +98 -52
- cognite/neat/_rules/models/dms/_exporter.py +7 -160
- cognite/neat/_rules/models/dms/_rules.py +18 -126
- cognite/neat/_rules/models/dms/_rules_input.py +20 -48
- cognite/neat/_rules/models/dms/_schema.py +11 -0
- cognite/neat/_rules/models/dms/_validation.py +9 -122
- cognite/neat/_rules/models/information/_rules.py +19 -114
- cognite/neat/_rules/models/information/_rules_input.py +32 -41
- cognite/neat/_rules/models/information/_validation.py +34 -102
- cognite/neat/_rules/transformers/__init__.py +1 -4
- cognite/neat/_rules/transformers/_converters.py +18 -195
- cognite/neat/_rules/transformers/_mapping.py +1 -5
- cognite/neat/_rules/transformers/_verification.py +0 -14
- cognite/neat/_session/_base.py +37 -13
- cognite/neat/_session/_collector.py +126 -0
- cognite/neat/_session/_inspect.py +5 -5
- cognite/neat/_session/_prepare.py +37 -11
- cognite/neat/_session/_read.py +62 -9
- cognite/neat/_session/_set.py +2 -2
- cognite/neat/_session/_show.py +11 -11
- cognite/neat/_session/_to.py +24 -11
- cognite/neat/_session/exceptions.py +20 -3
- cognite/neat/_store/_provenance.py +2 -2
- cognite/neat/_utils/auxiliary.py +19 -0
- cognite/neat/_version.py +1 -1
- cognite/neat/_workflows/steps/data_contracts.py +2 -10
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +6 -46
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +2 -7
- {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/METADATA +2 -1
- {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/RECORD +59 -65
- cognite/neat/_graph/loaders/_rdf2asset.py +0 -416
- cognite/neat/_rules/analysis/_asset.py +0 -173
- cognite/neat/_rules/models/asset/__init__.py +0 -13
- cognite/neat/_rules/models/asset/_rules.py +0 -109
- cognite/neat/_rules/models/asset/_rules_input.py +0 -101
- cognite/neat/_rules/models/asset/_validation.py +0 -45
- cognite/neat/_rules/models/domain.py +0 -136
- {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.97.2.dist-info → cognite_neat-0.98.0.dist-info}/WHEEL +0 -0
- {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_.
|
|
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_.
|
|
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_.
|
|
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_.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
267
|
+
# update implements
|
|
359
268
|
for class_ in self.classes:
|
|
360
|
-
if class_.
|
|
361
|
-
for parent in class_.
|
|
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.
|
|
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
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
-
|
|
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.
|
|
105
|
-
parent = [ClassEntity.load(parent, prefix=default_prefix) for parent in self.
|
|
106
|
-
elif isinstance(self.
|
|
107
|
-
parent = [ClassEntity.load(parent_, prefix=default_prefix) for parent_ in self.
|
|
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["
|
|
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.
|
|
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}"]
|