Skip to content

Commit

Permalink
[NEAT-492] 🦟Core import bug (#675)
Browse files Browse the repository at this point in the history
* docs; started on new tutorial

* docs; finished shell of tutorial

* refactor: started on prepare methods

* refactor: fix extension method

* fix: handle property ID

* refactor: implementd to excel

* fix: maximum column width

* refactor; remove cognite prefix

* refactor: added new prefix

* refactor: added view reduction

* fix: drop 3D property

* refactor; neat session use in memory by default

* refactor: setup dms analysis

* refactor: expose new analysis

* refactor: fix include all properties

* docs: finished doc tutorial

* docs; clear hidden output

* build: moved around dependencies

* style: happy mypy

* build: pin python-multipart

* docs; document

* tests: shell for test

* tests: added failing test

* fix: check parent views for property

* Ãrefactor: review feedback
  • Loading branch information
doctrino authored Oct 28, 2024
1 parent 35eb92b commit bf665b8
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 5 deletions.
58 changes: 53 additions & 5 deletions cognite/neat/_rules/models/dms/_exporter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import warnings
from collections import defaultdict
from collections.abc import Collection, Sequence
from collections.abc import Collection, Hashable, Sequence
from typing import Any, cast

from cognite.client.data_classes import data_modeling as dm
Expand Down Expand Up @@ -120,7 +120,13 @@ def to_schema(self) -> DMSSchema:

containers = self._create_containers(container_properties_by_id, rules.enum) # type: ignore[arg-type]

views, view_node_type_filters = self._create_views_with_node_types(view_properties_by_id)
view_properties_with_ancestors_by_id = self._gather_properties_with_ancestors(
view_properties_by_id, rules.views
)

views, view_node_type_filters = self._create_views_with_node_types(
view_properties_by_id, view_properties_with_ancestors_by_id
)
if rules.nodes:
node_types = NodeApplyDict(
[node.as_node() for node in rules.nodes]
Expand Down Expand Up @@ -198,6 +204,7 @@ def _create_spaces(
def _create_views_with_node_types(
self,
view_properties_by_id: dict[dm.ViewId, list[DMSProperty]],
view_properties_with_ancestors_by_id: dict[dm.ViewId, list[DMSProperty]],
) -> tuple[ViewApplyDict, set[dm.NodeId]]:
input_views = list(self.rules.views)
if self.rules.last:
Expand All @@ -217,7 +224,7 @@ def _create_views_with_node_types(
if not (view_properties := view_properties_by_id.get(view_id)):
continue
for prop in view_properties:
view_property = self._create_view_property(prop, view_properties_by_id)
view_property = self._create_view_property(prop, view_properties_with_ancestors_by_id)
if view_property is not None:
view.properties[prop.view_property] = view_property

Expand Down Expand Up @@ -395,6 +402,43 @@ def _gather_properties(

return container_properties_by_id, view_properties_by_id

@staticmethod
def _gather_properties_with_ancestors(
view_properties_by_id: dict[dm.ViewId, list[DMSProperty]],
views: Sequence[DMSView],
) -> dict[dm.ViewId, list[DMSProperty]]:
view_properties_with_parents_by_id: dict[dm.ViewId, list[DMSProperty]] = defaultdict(list)
view_by_view_id = {view.view.as_id(): view for view in views}
for view in views:
view_id = view.view.as_id()
seen: set[Hashable] = set()
if view_properties := view_properties_by_id.get(view_id):
view_properties_with_parents_by_id[view_id].extend(view_properties)
seen.update(prop._identifier() for prop in view_properties)
if not view.implements:
continue
parents = view.implements.copy()
seen_parents = set(parents)
while parents:
parent = parents.pop()
parent_view_id = parent.as_id()
if parent_view := view_by_view_id.get(parent_view_id):
for grandparent in parent_view.implements or []:
if grandparent not in seen_parents:
parents.append(grandparent)
seen_parents.add(grandparent)

if not (parent_view_properties := view_properties_by_id.get(parent_view_id)):
continue
for prop in parent_view_properties:
new_prop = prop.model_copy(update={"view": view.view})

if new_prop._identifier() not in seen:
view_properties_with_parents_by_id[view_id].append(new_prop)
seen.add(new_prop._identifier())

return view_properties_with_parents_by_id

@classmethod
def _update_with_properties(
cls,
Expand Down Expand Up @@ -472,7 +516,7 @@ def _create_view_filter(

@classmethod
def _create_view_property(
cls, prop: DMSProperty, view_properties_by_id: dict[dm.ViewId, list[DMSProperty]]
cls, prop: DMSProperty, view_properties_with_ancestors_by_id: dict[dm.ViewId, list[DMSProperty]]
) -> ViewPropertyApply | None:
if prop.container and prop.container_property:
container_prop_identifier = prop.container_property
Expand Down Expand Up @@ -538,7 +582,11 @@ def _create_view_property(
)
edge_source = None
reverse_prop = next(
(prop for prop in view_properties_by_id.get(source_view_id, []) if prop.property_ == reverse_prop_id),
(
prop
for prop in view_properties_with_ancestors_by_id.get(source_view_id, [])
if prop.property_ == reverse_prop_id
),
None,
)
if (
Expand Down
36 changes: 36 additions & 0 deletions tests/tests_unit/rules/test_models/test_dms_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
InformationToDMS,
MapOneToOne,
RulesPipeline,
VerifyDMSRules,
)
from cognite.neat._utils.cdf.data_classes import ContainerApplyDict, NodeApplyDict, SpaceApplyDict, ViewApplyDict
from tests.data import car
Expand Down Expand Up @@ -1642,6 +1643,41 @@ def test_metadata_int_version(self) -> None:

assert metadata.version == "14"

def test_reverse_property_in_parent(self) -> None:
sub_core = DMSInputRules(
DMSInputMetadata(
schema_="complete", space="my_space", external_id="my_data_model", creator="Anders", version="v42"
),
properties=[
DMSInputProperty(
view="CogniteVisualizable",
view_property="object3D",
value_type="Cognite3DObject",
connection="direct",
is_list=False,
container="CogniteVisualizable",
container_property="object3D",
),
DMSInputProperty(
view="Cognite3DObject",
view_property="asset",
value_type="CogniteAsset",
connection="reverse(property=object3D)",
),
],
views=[
DMSInputView(view="CogniteVisualizable"),
DMSInputView(view="CogniteAsset", implements="CogniteVisualizable"),
DMSInputView(view="Cognite3DObject"),
],
containers=[
DMSInputContainer("CogniteVisualizable"),
],
)
maybe_rules = VerifyDMSRules("continue").transform(sub_core)

assert not maybe_rules.issues


class TestDMSExporter:
def test_default_filters_using_olav_dms_rules(self, olav_dms_rules: DMSRules) -> None:
Expand Down

0 comments on commit bf665b8

Please sign in to comment.