cognite-neat 0.104.0__py3-none-any.whl → 0.105.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cognite-neat might be problematic. Click here for more details.
- cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
- cognite/neat/_client/_api/schema.py +2 -1
- cognite/neat/_client/data_classes/neat_sequence.py +261 -0
- cognite/neat/_client/data_classes/schema.py +5 -1
- cognite/neat/_client/testing.py +33 -0
- cognite/neat/_constants.py +57 -0
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
- cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/_graph/loaders/_rdf2dms.py +31 -5
- cognite/neat/_graph/transformers/__init__.py +3 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +39 -51
- cognite/neat/_graph/transformers/_rdfpath.py +14 -15
- cognite/neat/_graph/transformers/_value_type.py +72 -0
- cognite/neat/_issues/__init__.py +0 -2
- cognite/neat/_issues/_base.py +19 -35
- cognite/neat/_issues/warnings/__init__.py +6 -1
- cognite/neat/_issues/warnings/_general.py +7 -0
- cognite/neat/_issues/warnings/_properties.py +11 -0
- cognite/neat/_issues/warnings/_resources.py +11 -0
- cognite/neat/_rules/exporters/_rules2dms.py +35 -1
- cognite/neat/_rules/exporters/_rules2excel.py +2 -2
- cognite/neat/_rules/importers/_dms2rules.py +66 -55
- cognite/neat/_rules/models/_base_rules.py +4 -1
- cognite/neat/_rules/models/entities/_wrapped.py +10 -5
- cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
- cognite/neat/_rules/transformers/__init__.py +8 -2
- cognite/neat/_rules/transformers/_converters.py +271 -188
- cognite/neat/_rules/transformers/_mapping.py +75 -59
- cognite/neat/_rules/transformers/_verification.py +2 -3
- cognite/neat/_session/_inspect.py +3 -1
- cognite/neat/_session/_prepare.py +112 -24
- cognite/neat/_session/_read.py +33 -70
- cognite/neat/_session/_state.py +2 -2
- cognite/neat/_session/_to.py +2 -2
- cognite/neat/_store/_rules_store.py +4 -8
- cognite/neat/_utils/reader/_base.py +27 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/METADATA +4 -3
- cognite_neat-0.105.1.dist-info/RECORD +179 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/WHEEL +1 -1
- cognite/neat/_app/api/__init__.py +0 -0
- cognite/neat/_app/api/asgi/metrics.py +0 -4
- cognite/neat/_app/api/configuration.py +0 -98
- cognite/neat/_app/api/context_manager/__init__.py +0 -3
- cognite/neat/_app/api/context_manager/manager.py +0 -16
- cognite/neat/_app/api/data_classes/__init__.py +0 -0
- cognite/neat/_app/api/data_classes/rest.py +0 -59
- cognite/neat/_app/api/explorer.py +0 -66
- cognite/neat/_app/api/routers/configuration.py +0 -25
- cognite/neat/_app/api/routers/crud.py +0 -102
- cognite/neat/_app/api/routers/metrics.py +0 -10
- cognite/neat/_app/api/routers/workflows.py +0 -224
- cognite/neat/_app/api/utils/__init__.py +0 -0
- cognite/neat/_app/api/utils/data_mapping.py +0 -17
- cognite/neat/_app/api/utils/logging.py +0 -26
- cognite/neat/_app/api/utils/query_templates.py +0 -92
- cognite/neat/_app/main.py +0 -17
- cognite/neat/_app/monitoring/__init__.py +0 -0
- cognite/neat/_app/monitoring/metrics.py +0 -69
- cognite/neat/_app/ui/index.html +0 -1
- cognite/neat/_app/ui/neat-app/.gitignore +0 -23
- cognite/neat/_app/ui/neat-app/README.md +0 -70
- cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
- cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/build/index.html +0 -1
- cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
- cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
- cognite/neat/_app/ui/neat-app/package.json +0 -62
- cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/public/index.html +0 -43
- cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/src/App.css +0 -38
- cognite/neat/_app/ui/neat-app/src/App.js +0 -17
- cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
- cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
- cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
- cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
- cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
- cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
- cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
- cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
- cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
- cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
- cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
- cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
- cognite/neat/_app/ui/neat-app/src/index.css +0 -13
- cognite/neat/_app/ui/neat-app/src/index.js +0 -13
- cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
- cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
- cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
- cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
- cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
- cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
- cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
- cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
- cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
- cognite/neat/_workflows/__init__.py +0 -17
- cognite/neat/_workflows/base.py +0 -590
- cognite/neat/_workflows/cdf_store.py +0 -393
- cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
- cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
- cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
- cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
- cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
- cognite/neat/_workflows/manager.py +0 -292
- cognite/neat/_workflows/model.py +0 -203
- cognite/neat/_workflows/steps/__init__.py +0 -0
- cognite/neat/_workflows/steps/data_contracts.py +0 -109
- cognite/neat/_workflows/steps/lib/__init__.py +0 -0
- cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
- cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
- cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
- cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -323
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
- cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
- cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
- cognite/neat/_workflows/steps/step_model.py +0 -79
- cognite/neat/_workflows/steps_registry.py +0 -218
- cognite/neat/_workflows/tasks.py +0 -18
- cognite/neat/_workflows/triggers.py +0 -169
- cognite/neat/_workflows/utils.py +0 -19
- cognite_neat-0.104.0.dist-info/RECORD +0 -276
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/LICENSE +0 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/entry_points.txt +0 -0
|
@@ -46,6 +46,7 @@ from cognite.neat._rules.models.entities import (
|
|
|
46
46
|
DMSUnknownEntity,
|
|
47
47
|
EdgeEntity,
|
|
48
48
|
Entity,
|
|
49
|
+
HasDataFilter,
|
|
49
50
|
MultiValueTypeInfo,
|
|
50
51
|
ReverseConnectionEntity,
|
|
51
52
|
T_Entity,
|
|
@@ -58,7 +59,6 @@ from cognite.neat._rules.models.information._rules_input import (
|
|
|
58
59
|
InformationInputProperty,
|
|
59
60
|
InformationInputRules,
|
|
60
61
|
)
|
|
61
|
-
from cognite.neat._utils.collection_ import remove_list_elements
|
|
62
62
|
from cognite.neat._utils.text import to_camel
|
|
63
63
|
|
|
64
64
|
from ._base import RulesTransformer
|
|
@@ -302,107 +302,259 @@ class SetIDDMSModel(RulesTransformer[DMSRules, DMSRules]):
|
|
|
302
302
|
return DMSRules.model_validate(DMSInputRules.load(dump).dump())
|
|
303
303
|
|
|
304
304
|
|
|
305
|
-
class
|
|
305
|
+
class ToExtensionModel(RulesTransformer[DMSRules, DMSRules], ABC):
|
|
306
|
+
type_: ClassVar[str]
|
|
307
|
+
|
|
308
|
+
def __init__(self, new_model_id: DataModelIdentifier, org_name: str, dummy_property: str | None = None) -> None:
|
|
309
|
+
self.new_model_id = DataModelId.load(new_model_id)
|
|
310
|
+
if not self.new_model_id.version:
|
|
311
|
+
raise NeatValueError("Version is required for the new model.")
|
|
312
|
+
self.org_name = org_name
|
|
313
|
+
self.dummy_property = dummy_property
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def description(self) -> str:
|
|
317
|
+
return f"Prepared data model {self.new_model_id} to be {self.type_.replace('_', ' ')} data model."
|
|
318
|
+
|
|
319
|
+
def _create_new_views(
|
|
320
|
+
self, rules: DMSRules
|
|
321
|
+
) -> tuple[SheetList[DMSView], SheetList[DMSContainer], SheetList[DMSProperty]]:
|
|
322
|
+
"""Creates new views for the new model.
|
|
323
|
+
|
|
324
|
+
If the dummy property is provided, it will also create a new container for each view
|
|
325
|
+
with a single property that is the dummy property.
|
|
326
|
+
"""
|
|
327
|
+
new_views = SheetList[DMSView]()
|
|
328
|
+
new_containers = SheetList[DMSContainer]()
|
|
329
|
+
new_properties = SheetList[DMSProperty]()
|
|
330
|
+
|
|
331
|
+
for definition in rules.views:
|
|
332
|
+
view_entity = self._remove_cognite_affix(definition.view)
|
|
333
|
+
view_entity.version = cast(str, self.new_model_id.version)
|
|
334
|
+
view_entity.prefix = self.new_model_id.space
|
|
335
|
+
|
|
336
|
+
new_views.append(
|
|
337
|
+
DMSView(
|
|
338
|
+
view=view_entity,
|
|
339
|
+
implements=[definition.view],
|
|
340
|
+
in_model=True,
|
|
341
|
+
name=definition.name,
|
|
342
|
+
)
|
|
343
|
+
)
|
|
344
|
+
if self.dummy_property is None:
|
|
345
|
+
continue
|
|
346
|
+
|
|
347
|
+
container_entity = ContainerEntity(space=view_entity.prefix, externalId=view_entity.external_id)
|
|
348
|
+
|
|
349
|
+
container = DMSContainer(container=container_entity)
|
|
350
|
+
|
|
351
|
+
prefix = to_camel(view_entity.suffix)
|
|
352
|
+
property_ = DMSProperty(
|
|
353
|
+
view=view_entity,
|
|
354
|
+
view_property=f"{prefix}{self.dummy_property}",
|
|
355
|
+
value_type=String(),
|
|
356
|
+
nullable=True,
|
|
357
|
+
immutable=False,
|
|
358
|
+
is_list=False,
|
|
359
|
+
container=container_entity,
|
|
360
|
+
container_property=f"{prefix}{self.dummy_property}",
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
new_properties.append(property_)
|
|
364
|
+
new_containers.append(container)
|
|
365
|
+
|
|
366
|
+
return new_views, new_containers, new_properties
|
|
367
|
+
|
|
368
|
+
def _remove_cognite_affix(self, entity: _T_Entity) -> _T_Entity:
|
|
369
|
+
"""This method removes `Cognite` affix from the entity."""
|
|
370
|
+
new_suffix = entity.suffix.replace("Cognite", self.org_name or "")
|
|
371
|
+
if isinstance(entity, ViewEntity):
|
|
372
|
+
return ViewEntity(space=entity.space, externalId=new_suffix, version=entity.version) # type: ignore[return-value]
|
|
373
|
+
elif isinstance(entity, ClassEntity):
|
|
374
|
+
return ClassEntity(prefix=entity.prefix, suffix=new_suffix, version=entity.version) # type: ignore[return-value]
|
|
375
|
+
raise ValueError(f"Unsupported entity type: {type(entity)}")
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class ToEnterpriseModel(ToExtensionModel):
|
|
379
|
+
type_: ClassVar[str] = "enterprise"
|
|
380
|
+
|
|
306
381
|
def __init__(
|
|
307
382
|
self,
|
|
308
383
|
new_model_id: DataModelIdentifier,
|
|
309
384
|
org_name: str = "My",
|
|
310
|
-
type_: Literal["enterprise", "solution", "data_product"] = "enterprise",
|
|
311
|
-
mode: Literal["read", "write"] = "read",
|
|
312
385
|
dummy_property: str = "GUID",
|
|
313
386
|
move_connections: bool = False,
|
|
314
|
-
include: Literal["same-space", "all"] = "same-space",
|
|
315
387
|
):
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
raise NeatValueError("Version is required for the new model.")
|
|
388
|
+
super().__init__(new_model_id, org_name, dummy_property)
|
|
389
|
+
self.move_connections = move_connections
|
|
319
390
|
|
|
320
|
-
|
|
391
|
+
def transform(self, rules: DMSRules) -> DMSRules:
|
|
392
|
+
reference_model_id = rules.metadata.as_data_model_id()
|
|
393
|
+
if reference_model_id not in COGNITE_MODELS:
|
|
394
|
+
warnings.warn(
|
|
395
|
+
EnterpriseModelNotBuildOnTopOfCDMWarning(reference_model_id=reference_model_id).as_message(),
|
|
396
|
+
stacklevel=2,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
return self._to_enterprise(rules)
|
|
400
|
+
|
|
401
|
+
def _to_enterprise(self, reference_model: DMSRules) -> DMSRules:
|
|
402
|
+
dump = reference_model.dump()
|
|
403
|
+
|
|
404
|
+
# This will create reference model components in the enterprise model space
|
|
405
|
+
enterprise_model = DMSRules.model_validate(DMSInputRules.load(dump).dump())
|
|
406
|
+
|
|
407
|
+
# Post validation metadata update:
|
|
408
|
+
enterprise_model.metadata.name = self.type_
|
|
409
|
+
enterprise_model.metadata.name = f"{self.org_name} {self.type_} data model"
|
|
410
|
+
enterprise_model.metadata.space = self.new_model_id.space
|
|
411
|
+
enterprise_model.metadata.external_id = self.new_model_id.external_id
|
|
412
|
+
enterprise_model.metadata.version = cast(str, self.new_model_id.version)
|
|
413
|
+
|
|
414
|
+
# Here we are creating enterprise views with a single container with a dummy property
|
|
415
|
+
# for each view
|
|
416
|
+
enterprise_views, enterprise_containers, enterprise_properties = self._create_new_views(enterprise_model)
|
|
417
|
+
|
|
418
|
+
# We keep the reference views, and adding new enterprise views...
|
|
419
|
+
enterprise_model.views.extend(enterprise_views)
|
|
420
|
+
|
|
421
|
+
if self.move_connections:
|
|
422
|
+
# Move connections from reference model to new enterprise model
|
|
423
|
+
enterprise_properties.extend(self._move_connections(enterprise_model))
|
|
424
|
+
|
|
425
|
+
# ... however, we do not want to keep the reference containers and properties
|
|
426
|
+
enterprise_model.containers = enterprise_containers
|
|
427
|
+
enterprise_model.properties = enterprise_properties
|
|
428
|
+
|
|
429
|
+
return enterprise_model
|
|
430
|
+
|
|
431
|
+
@staticmethod
|
|
432
|
+
def _move_connections(rules: DMSRules) -> SheetList[DMSProperty]:
|
|
433
|
+
implements: dict[ViewEntity, list[ViewEntity]] = defaultdict(list)
|
|
434
|
+
new_properties = SheetList[DMSProperty]()
|
|
435
|
+
|
|
436
|
+
for view in rules.views:
|
|
437
|
+
if view.view.space == rules.metadata.space and view.implements:
|
|
438
|
+
for implemented_view in view.implements:
|
|
439
|
+
implements.setdefault(implemented_view, []).append(view.view)
|
|
440
|
+
|
|
441
|
+
# currently only supporting single implementation of reference view in enterprise view
|
|
442
|
+
# connections that do not have properties
|
|
443
|
+
if all(len(v) == 1 for v in implements.values()):
|
|
444
|
+
for prop_ in rules.properties:
|
|
445
|
+
if (
|
|
446
|
+
prop_.view.space != rules.metadata.space
|
|
447
|
+
and prop_.connection
|
|
448
|
+
and isinstance(prop_.value_type, ViewEntity)
|
|
449
|
+
and implements.get(prop_.view)
|
|
450
|
+
and implements.get(prop_.value_type)
|
|
451
|
+
):
|
|
452
|
+
if isinstance(prop_.connection, EdgeEntity) and prop_.connection.properties:
|
|
453
|
+
continue
|
|
454
|
+
new_property = prop_.model_copy(deep=True)
|
|
455
|
+
new_property.view = implements[prop_.view][0]
|
|
456
|
+
new_property.value_type = implements[prop_.value_type][0]
|
|
457
|
+
new_properties.append(new_property)
|
|
458
|
+
|
|
459
|
+
return new_properties
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class ToSolutionModel(ToExtensionModel):
|
|
463
|
+
"""Creates a solution data model based on an existing data model.
|
|
464
|
+
|
|
465
|
+
The solution data model will create a new view for each view in the existing model.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
new_model_id: DataData model identifier for the new model.
|
|
469
|
+
org_name: If the existing model is a Cognite Data Model, this will replace the "Cognite" affix.
|
|
470
|
+
mode: The mode of the solution model. Either "read" or "write". A "write" model will create a new
|
|
471
|
+
container for each view with a dummy property. Read mode will only inherit the view filter from the
|
|
472
|
+
original model.
|
|
473
|
+
dummy_property: Only applicable if mode='write'. The identifier of the dummy property in the newly created
|
|
474
|
+
container.
|
|
475
|
+
exclude_views_in_other_spaces: Whether to exclude views that are not in the same space as the existing model,
|
|
476
|
+
when creating the solution model.
|
|
477
|
+
filter_type: If mode="read", this is the type of filter to apply to the new views. The filter is used to
|
|
478
|
+
ensure that the new views will return the same instance as the original views. The view filter is the
|
|
479
|
+
simplest filter, but it has limitation in the fusion UI. The container filter is in essence a more
|
|
480
|
+
verbose version of the view filter, and it has better support in the fusion UI. The default is "container".
|
|
481
|
+
|
|
482
|
+
"""
|
|
483
|
+
|
|
484
|
+
type_: ClassVar[str] = "solution"
|
|
485
|
+
|
|
486
|
+
def __init__(
|
|
487
|
+
self,
|
|
488
|
+
new_model_id: DataModelIdentifier,
|
|
489
|
+
org_name: str = "My",
|
|
490
|
+
mode: Literal["read", "write"] = "read",
|
|
491
|
+
dummy_property: str | None = "GUID",
|
|
492
|
+
exclude_views_in_other_spaces: bool = True,
|
|
493
|
+
filter_type: Literal["container", "view"] = "container",
|
|
494
|
+
):
|
|
495
|
+
super().__init__(new_model_id, org_name, dummy_property if mode == "write" else None)
|
|
321
496
|
self.mode = mode
|
|
322
|
-
self.
|
|
323
|
-
self.
|
|
324
|
-
self.move_connections = move_connections
|
|
325
|
-
self.include = include
|
|
497
|
+
self.exclude_views_in_other_spaces = exclude_views_in_other_spaces
|
|
498
|
+
self.filter_type = filter_type
|
|
326
499
|
|
|
327
500
|
def transform(self, rules: DMSRules) -> DMSRules:
|
|
328
|
-
# Copy to ensure immutability
|
|
329
501
|
reference_model = rules
|
|
330
502
|
reference_model_id = reference_model.metadata.as_data_model_id()
|
|
331
503
|
|
|
332
504
|
# if model is solution then we need to get correct space for views and containers
|
|
333
|
-
if self.
|
|
334
|
-
|
|
335
|
-
raise NeatValueError(f"Unsupported mode: {self.mode}")
|
|
336
|
-
|
|
337
|
-
if reference_model_id in COGNITE_MODELS:
|
|
338
|
-
warnings.warn(
|
|
339
|
-
SolutionModelBuildOnTopOfCDMWarning(reference_model_id=reference_model_id),
|
|
340
|
-
stacklevel=2,
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
return self._to_solution(reference_model)
|
|
344
|
-
|
|
345
|
-
elif self.type_ == "enterprise":
|
|
346
|
-
if reference_model_id not in COGNITE_MODELS:
|
|
347
|
-
warnings.warn(
|
|
348
|
-
EnterpriseModelNotBuildOnTopOfCDMWarning(reference_model_id=reference_model_id).as_message(),
|
|
349
|
-
stacklevel=2,
|
|
350
|
-
)
|
|
505
|
+
if self.mode not in ["read", "write"]:
|
|
506
|
+
raise NeatValueError(f"Unsupported mode: {self.mode}")
|
|
351
507
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
[prop for prop in expanded.properties if prop.view.space == expanded.metadata.space]
|
|
358
|
-
)
|
|
359
|
-
expanded.views = SheetList[DMSView](
|
|
360
|
-
[view for view in expanded.views if view.view.space == expanded.metadata.space]
|
|
361
|
-
)
|
|
362
|
-
return self._to_solution(expanded, remove_views_in_other_space=False)
|
|
508
|
+
if reference_model_id in COGNITE_MODELS:
|
|
509
|
+
warnings.warn(
|
|
510
|
+
SolutionModelBuildOnTopOfCDMWarning(reference_model_id=reference_model_id),
|
|
511
|
+
stacklevel=2,
|
|
512
|
+
)
|
|
363
513
|
|
|
364
|
-
|
|
365
|
-
raise NeatValueError(f"Unsupported data model type: {self.type_}")
|
|
514
|
+
return self._to_solution(reference_model)
|
|
366
515
|
|
|
367
|
-
|
|
516
|
+
@staticmethod
|
|
517
|
+
def _has_views_in_multiple_space(rules: DMSRules) -> bool:
|
|
368
518
|
return any(view.view.space != rules.metadata.space for view in rules.views)
|
|
369
519
|
|
|
370
|
-
def _to_solution(self, reference_rules: DMSRules
|
|
520
|
+
def _to_solution(self, reference_rules: DMSRules) -> DMSRules:
|
|
371
521
|
"""For creation of solution data model / rules specifically for mapping over existing containers."""
|
|
372
522
|
|
|
373
|
-
dump = reference_rules.dump()
|
|
523
|
+
dump = reference_rules.dump(entities_exclude_defaults=True)
|
|
374
524
|
|
|
375
525
|
# Prepare new model metadata prior validation
|
|
526
|
+
# Since we dropped the defaults, all entities will update the space and version
|
|
527
|
+
# to the new model space and version
|
|
376
528
|
dump["metadata"]["name"] = f"{self.org_name} {self.type_} data model"
|
|
377
529
|
dump["metadata"]["space"] = self.new_model_id.space
|
|
378
530
|
dump["metadata"]["external_id"] = self.new_model_id.external_id
|
|
379
531
|
dump["metadata"]["version"] = self.new_model_id.version
|
|
380
532
|
|
|
381
|
-
# Set implement to NONE for all views
|
|
382
|
-
for view in dump["views"]:
|
|
383
|
-
view["implements"] = None
|
|
384
|
-
|
|
385
|
-
if remove_views_in_other_space and self._has_views_in_multiple_space(reference_rules):
|
|
386
|
-
views_to_remove = []
|
|
387
|
-
for view in dump["views"]:
|
|
388
|
-
if ":" in view["view"]:
|
|
389
|
-
views_to_remove.append(view)
|
|
390
|
-
|
|
391
|
-
dump["views"] = remove_list_elements(dump["views"], views_to_remove)
|
|
392
|
-
|
|
393
533
|
solution_model = DMSRules.model_validate(DMSInputRules.load(dump).dump())
|
|
394
534
|
|
|
395
|
-
#
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
# We want to map properties to existing containers allowing extension
|
|
535
|
+
# This is not desirable for the containers, so we manually fix that here.
|
|
536
|
+
# It is easier to change the space for all entities and then revert the containers, than
|
|
537
|
+
# to change the space for all entities except the containers.
|
|
399
538
|
for prop in solution_model.properties:
|
|
400
539
|
if prop.container and prop.container.space == self.new_model_id.space:
|
|
540
|
+
# If the container is in the new model space, we want to map it to the reference model space
|
|
541
|
+
# This is reverting the .dump() -> .load() above.
|
|
401
542
|
prop.container = ContainerEntity(
|
|
402
543
|
space=reference_rules.metadata.space,
|
|
403
544
|
externalId=prop.container.suffix,
|
|
404
545
|
)
|
|
405
546
|
|
|
547
|
+
for view in solution_model.views:
|
|
548
|
+
view.implements = None
|
|
549
|
+
|
|
550
|
+
if self.exclude_views_in_other_spaces and self._has_views_in_multiple_space(reference_rules):
|
|
551
|
+
solution_model.views = SheetList[DMSView](
|
|
552
|
+
[view for view in solution_model.views if view.view.space == solution_model.metadata.space]
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Dropping containers coming from reference model
|
|
556
|
+
solution_model.containers = None
|
|
557
|
+
|
|
406
558
|
# If reference model on which we are mapping one of Cognite Data Models
|
|
407
559
|
# since we want to affix these with the organization name
|
|
408
560
|
if reference_rules.metadata.as_data_model_id() in COGNITE_MODELS:
|
|
@@ -413,50 +565,73 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
|
|
|
413
565
|
prop.value_type = self._remove_cognite_affix(prop.value_type)
|
|
414
566
|
for view in solution_model.views:
|
|
415
567
|
view.view = self._remove_cognite_affix(view.view)
|
|
568
|
+
if view.implements:
|
|
569
|
+
view.implements = [self._remove_cognite_affix(implemented) for implemented in view.implements]
|
|
416
570
|
|
|
417
571
|
if self.mode == "write":
|
|
418
|
-
_, new_containers, new_properties = self.
|
|
419
|
-
|
|
572
|
+
_, new_containers, new_properties = self._create_new_views(solution_model)
|
|
420
573
|
# Here we add ONLY dummy properties of the solution model and
|
|
421
574
|
# corresponding solution model space containers to hold them
|
|
422
575
|
solution_model.containers = new_containers
|
|
423
576
|
solution_model.properties.extend(new_properties)
|
|
577
|
+
elif self.mode == "read":
|
|
578
|
+
# Inherit view filter from original model to ensure the same instances are returned
|
|
579
|
+
# when querying the new view.
|
|
580
|
+
ref_views_by_external_id = {
|
|
581
|
+
view.view.external_id: view
|
|
582
|
+
for view in reference_rules.views
|
|
583
|
+
if view.view.space == reference_rules.metadata.space
|
|
584
|
+
}
|
|
585
|
+
ref_containers_by_ref_view = defaultdict(set)
|
|
586
|
+
for prop in reference_rules.properties:
|
|
587
|
+
if prop.container:
|
|
588
|
+
ref_containers_by_ref_view[prop.view].add(prop.container)
|
|
589
|
+
for view in solution_model.views:
|
|
590
|
+
if ref_view := ref_views_by_external_id.get(view.view.external_id):
|
|
591
|
+
if self.filter_type == "view":
|
|
592
|
+
view.filter_ = HasDataFilter(inner=[ref_view.view])
|
|
593
|
+
elif self.filter_type == "container" and (
|
|
594
|
+
ref_containers := ref_containers_by_ref_view.get(ref_view.view)
|
|
595
|
+
):
|
|
596
|
+
# Sorting to ensure deterministic order
|
|
597
|
+
view.filter_ = HasDataFilter(inner=sorted(ref_containers))
|
|
424
598
|
|
|
425
599
|
return solution_model
|
|
426
600
|
|
|
427
|
-
def _to_enterprise(self, reference_model: DMSRules) -> DMSRules:
|
|
428
|
-
dump = reference_model.dump()
|
|
429
601
|
|
|
430
|
-
|
|
431
|
-
|
|
602
|
+
class ToDataProductModel(ToSolutionModel):
|
|
603
|
+
type_: ClassVar[str] = "data_product"
|
|
432
604
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
enterprise_views, enterprise_containers, enterprise_properties = self._get_new_components(enterprise_model)
|
|
442
|
-
|
|
443
|
-
# And we are adding them to the enterprise model
|
|
444
|
-
# extending reference views with new ones
|
|
445
|
-
enterprise_model.views.extend(enterprise_views)
|
|
446
|
-
|
|
447
|
-
# Move connections from reference model to enterprise model
|
|
448
|
-
if self.move_connections:
|
|
449
|
-
enterprise_connections = self._move_connections(enterprise_model)
|
|
450
|
-
else:
|
|
451
|
-
enterprise_connections = SheetList[DMSProperty]()
|
|
452
|
-
|
|
453
|
-
# while overwriting containers and properties with new ones
|
|
454
|
-
enterprise_model.containers = enterprise_containers
|
|
455
|
-
enterprise_model.properties = enterprise_properties
|
|
605
|
+
def __init__(
|
|
606
|
+
self,
|
|
607
|
+
new_model_id: DataModelIdentifier,
|
|
608
|
+
org_name: str = "My",
|
|
609
|
+
include: Literal["same-space", "all"] = "same-space",
|
|
610
|
+
):
|
|
611
|
+
super().__init__(new_model_id, org_name, mode="read", dummy_property=None, exclude_views_in_other_spaces=False)
|
|
612
|
+
self.include = include
|
|
456
613
|
|
|
457
|
-
|
|
614
|
+
def transform(self, rules: DMSRules) -> DMSRules:
|
|
615
|
+
# Copy to ensure immutability
|
|
616
|
+
expanded = self._expand_properties(rules.model_copy(deep=True))
|
|
617
|
+
if self.include == "same-space":
|
|
618
|
+
expanded.views = SheetList[DMSView](
|
|
619
|
+
[view for view in expanded.views if view.view.space == expanded.metadata.space]
|
|
620
|
+
)
|
|
621
|
+
used_view_entities = {view.view for view in expanded.views}
|
|
622
|
+
expanded.properties = SheetList[DMSProperty](
|
|
623
|
+
[
|
|
624
|
+
prop
|
|
625
|
+
for prop in expanded.properties
|
|
626
|
+
if prop.view.space == expanded.metadata.space
|
|
627
|
+
and (
|
|
628
|
+
(isinstance(prop.value_type, ViewEntity) and prop.value_type in used_view_entities)
|
|
629
|
+
or not isinstance(prop.value_type, ViewEntity)
|
|
630
|
+
)
|
|
631
|
+
]
|
|
632
|
+
)
|
|
458
633
|
|
|
459
|
-
return
|
|
634
|
+
return self._to_solution(expanded)
|
|
460
635
|
|
|
461
636
|
@staticmethod
|
|
462
637
|
def _expand_properties(rules: DMSRules) -> DMSRules:
|
|
@@ -480,97 +655,6 @@ class ToExtension(RulesTransformer[DMSRules, DMSRules]):
|
|
|
480
655
|
property_ids.add(prop.view_property)
|
|
481
656
|
return rules
|
|
482
657
|
|
|
483
|
-
def _remove_cognite_affix(self, entity: _T_Entity) -> _T_Entity:
|
|
484
|
-
"""This method removes `Cognite` affix from the entity."""
|
|
485
|
-
new_suffix = entity.suffix.replace("Cognite", self.org_name or "")
|
|
486
|
-
if isinstance(entity, ViewEntity):
|
|
487
|
-
return ViewEntity(space=entity.space, externalId=new_suffix, version=entity.version) # type: ignore[return-value]
|
|
488
|
-
elif isinstance(entity, ClassEntity):
|
|
489
|
-
return ClassEntity(prefix=entity.prefix, suffix=new_suffix, version=entity.version) # type: ignore[return-value]
|
|
490
|
-
raise ValueError(f"Unsupported entity type: {type(entity)}")
|
|
491
|
-
|
|
492
|
-
def _get_new_components(
|
|
493
|
-
self, rules: DMSRules
|
|
494
|
-
) -> tuple[SheetList[DMSView], SheetList[DMSContainer], SheetList[DMSProperty]]:
|
|
495
|
-
new_views = SheetList[DMSView]()
|
|
496
|
-
new_containers = SheetList[DMSContainer]()
|
|
497
|
-
new_properties = SheetList[DMSProperty]()
|
|
498
|
-
|
|
499
|
-
for definition in rules.views:
|
|
500
|
-
view_entity = self._remove_cognite_affix(definition.view)
|
|
501
|
-
|
|
502
|
-
view_entity.version = cast(str, self.new_model_id.version)
|
|
503
|
-
view_entity.prefix = self.new_model_id.space
|
|
504
|
-
container_entity = ContainerEntity(space=view_entity.prefix, externalId=view_entity.external_id)
|
|
505
|
-
|
|
506
|
-
view = DMSView(
|
|
507
|
-
view=view_entity,
|
|
508
|
-
implements=[definition.view],
|
|
509
|
-
in_model=True,
|
|
510
|
-
name=definition.name,
|
|
511
|
-
)
|
|
512
|
-
|
|
513
|
-
container = DMSContainer(
|
|
514
|
-
container=container_entity,
|
|
515
|
-
)
|
|
516
|
-
|
|
517
|
-
property_ = DMSProperty(
|
|
518
|
-
view=view_entity,
|
|
519
|
-
view_property=f"{to_camel(view_entity.suffix)}{self.dummy_property}",
|
|
520
|
-
value_type=String(),
|
|
521
|
-
nullable=True,
|
|
522
|
-
immutable=False,
|
|
523
|
-
is_list=False,
|
|
524
|
-
container=container_entity,
|
|
525
|
-
container_property=f"{to_camel(view_entity.suffix)}{self.dummy_property}",
|
|
526
|
-
)
|
|
527
|
-
|
|
528
|
-
new_properties.append(property_)
|
|
529
|
-
new_views.append(view)
|
|
530
|
-
new_containers.append(container)
|
|
531
|
-
|
|
532
|
-
return new_views, new_containers, new_properties
|
|
533
|
-
|
|
534
|
-
def _move_connections(self, rules: DMSRules) -> SheetList[DMSProperty]:
|
|
535
|
-
implements: dict[ViewEntity, list[ViewEntity]] = defaultdict(list)
|
|
536
|
-
new_properties = SheetList[DMSProperty]()
|
|
537
|
-
|
|
538
|
-
for view in rules.views:
|
|
539
|
-
if view.view.space == rules.metadata.space and view.implements:
|
|
540
|
-
for implemented_view in view.implements:
|
|
541
|
-
implements.setdefault(implemented_view, []).append(view.view)
|
|
542
|
-
|
|
543
|
-
# currently only supporting single implementation of reference view in enterprise view
|
|
544
|
-
# connections that do not have properties
|
|
545
|
-
if all(len(v) == 1 for v in implements.values()):
|
|
546
|
-
for prop_ in rules.properties:
|
|
547
|
-
if (
|
|
548
|
-
prop_.view.space != rules.metadata.space
|
|
549
|
-
and prop_.connection
|
|
550
|
-
and isinstance(prop_.value_type, ViewEntity)
|
|
551
|
-
and implements.get(prop_.view)
|
|
552
|
-
and implements.get(prop_.value_type)
|
|
553
|
-
):
|
|
554
|
-
if isinstance(prop_.connection, EdgeEntity) and prop_.connection.properties:
|
|
555
|
-
continue
|
|
556
|
-
new_property = prop_.model_copy(deep=True)
|
|
557
|
-
new_property.view = implements[prop_.view][0]
|
|
558
|
-
new_property.value_type = implements[prop_.value_type][0]
|
|
559
|
-
new_properties.append(new_property)
|
|
560
|
-
|
|
561
|
-
return new_properties
|
|
562
|
-
|
|
563
|
-
@property
|
|
564
|
-
def description(self) -> str:
|
|
565
|
-
if self.type_ == "enterprise":
|
|
566
|
-
return f"Prepared data model {self.new_model_id} to be enterprise data model."
|
|
567
|
-
elif self.type_ == "solution":
|
|
568
|
-
return f"Prepared data model {self.new_model_id} to be solution data model."
|
|
569
|
-
elif self.type_ == "data_product":
|
|
570
|
-
return f"Prepared data model {self.new_model_id} to be data product model."
|
|
571
|
-
else:
|
|
572
|
-
return f"Unsupported data model type: {self.type_}"
|
|
573
|
-
|
|
574
658
|
|
|
575
659
|
class ReduceCogniteModel(RulesTransformer[DMSRules, DMSRules]):
|
|
576
660
|
_ASSET_VIEW = ViewId("cdf_cdm", "CogniteAsset", "v1")
|
|
@@ -662,7 +746,7 @@ class IncludeReferenced(RulesTransformer[DMSRules, DMSRules]):
|
|
|
662
746
|
|
|
663
747
|
def transform(self, rules: DMSRules) -> DMSRules:
|
|
664
748
|
dms_rules = rules
|
|
665
|
-
view_ids, container_ids = DMSValidation(dms_rules
|
|
749
|
+
view_ids, container_ids = DMSValidation(dms_rules).imported_views_and_containers_ids()
|
|
666
750
|
if not (view_ids or container_ids):
|
|
667
751
|
warnings.warn(
|
|
668
752
|
NeatValueWarning(
|
|
@@ -717,7 +801,6 @@ class AddClassImplements(RulesTransformer[InformationRules, InformationRules]):
|
|
|
717
801
|
for class_ in output.classes:
|
|
718
802
|
if class_.class_.suffix.endswith(self.suffix):
|
|
719
803
|
class_.implements = [ClassEntity(prefix=class_.class_.prefix, suffix=self.implements)]
|
|
720
|
-
output.metadata.version = f"{output.metadata.version}.implements_{self.implements}"
|
|
721
804
|
return output
|
|
722
805
|
|
|
723
806
|
@property
|