Skip to content

Commit

Permalink
fix: sorting views
Browse files Browse the repository at this point in the history
  • Loading branch information
doctrino committed Feb 11, 2025
1 parent c345f94 commit 61694b2
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 58 deletions.
44 changes: 8 additions & 36 deletions cognite/neat/_client/_api/schema.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from collections import defaultdict
from collections.abc import Mapping, Sequence
from graphlib import TopologicalSorter
from collections.abc import Iterable, Sequence
from typing import TYPE_CHECKING

from cognite.client import data_modeling as dm

from cognite.neat._client.data_classes.data_modeling import ContainerApplyDict, SpaceApplyDict, ViewApplyDict
from cognite.neat._client.data_classes.schema import DMSSchema
from cognite.neat._constants import is_hierarchy_property, is_readonly_property
from cognite.neat._constants import is_hierarchy_property
from cognite.neat._issues.errors import NeatValueError

if TYPE_CHECKING:
Expand Down Expand Up @@ -115,42 +114,15 @@ def retrieve_data_model(
)

@staticmethod
def order_views_by_container_dependencies(
views_by_id: Mapping[dm.ViewId, dm.View | dm.ViewApply],
containers: Sequence[dm.Container | dm.ContainerApply],
skip_readonly: bool = False,
) -> tuple[list[dm.ViewId], dict[dm.ViewId, set[str]]]:
def get_hierarchical_properties(
views: Iterable[dm.View | dm.ViewApply],
) -> dict[dm.ViewId, set[str]]:
"""Sorts the views by container constraints."""
container_by_id = {container.as_id(): container for container in containers}
views_by_container = defaultdict(set)
for view_id, view in views_by_id.items():
for container_id in view.referenced_containers():
views_by_container[container_id].add(view_id)

hierarchical_properties_by_view_id: dict[dm.ViewId, set[str]] = defaultdict(set)
view_id_by_dependencies: dict[dm.ViewId, set[dm.ViewId]] = {}
for view_id, view in views_by_id.items():
dependencies = set()
for view in views:
for prop_id, prop in (view.properties or {}).items():
if not isinstance(prop, dm.MappedProperty | dm.MappedPropertyApply):
continue
if skip_readonly and is_readonly_property(prop.container, prop.container_property_identifier):
continue
if is_hierarchy_property(prop.container, prop.container_property_identifier):
hierarchical_properties_by_view_id[view_id].add(prop_id)
continue
container = container_by_id[prop.container]
container_prop = container.properties[prop.container_property_identifier]
if not isinstance(container_prop.type, dm.DirectRelation):
continue
if prop.source == view_id:
hierarchical_properties_by_view_id[view_id].add(prop_id)
for constraint in container.constraints.values():
if isinstance(constraint, dm.RequiresConstraint):
view_dependencies = views_by_container.get(constraint.require, set()) - {view_id}
dependencies.update(view_dependencies)
view_id_by_dependencies[view_id] = dependencies

ordered_view_ids = list(TopologicalSorter(view_id_by_dependencies).static_order())

return list(ordered_view_ids), hierarchical_properties_by_view_id
hierarchical_properties_by_view_id[view.as_id()].add(prop_id)
return hierarchical_properties_by_view_id
24 changes: 13 additions & 11 deletions cognite/neat/_graph/loaders/_rdf2dms.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from rdflib import RDF

from cognite.neat._client import NeatClient
from cognite.neat._client._api_client import SchemaAPI
from cognite.neat._constants import DMS_DIRECT_RELATION_LIST_LIMIT, is_readonly_property
from cognite.neat._issues import IssueList, NeatIssue, catch_issues
from cognite.neat._issues.errors import ResourceCreationError, ResourceDuplicatedError, ResourceNotFoundError
Expand Down Expand Up @@ -171,32 +172,33 @@ def _create_view_iterations(self) -> tuple[list[_ViewIterator], IssueList]:
view_query_by_id = RulesAnalysis(self.info_rules, self.dms_rules).view_query_by_id
iterations_by_view_id = self._select_views_with_instances(view_query_by_id)
if self._client:
issues = IssueList()
views = self._client.data_modeling.views.retrieve(
list(iterations_by_view_id.keys()), include_inherited_properties=True
)
containers = self._client.data_modeling.containers.retrieve(list(views.referenced_containers()))
views_by_id = {view.as_id(): view for view in views}
ordered_view_ids, properties_dependent_on_self_by_view_id = (
self._client.schema.order_views_by_container_dependencies(views_by_id, containers, skip_readonly=True)
)
issues = IssueList()
else:
ordered_view_ids = list(iterations_by_view_id.keys())
properties_dependent_on_self_by_view_id = {}
views_by_id = {}
views = dm.ViewList([])
with catch_issues() as issues:
read_model = self.dms_rules.as_schema().as_read_model()
views_by_id.update({view.as_id(): view for view in read_model.views})
views.extend(read_model.views)
if issues.has_errors:
return [], issues
views_by_id = {view.as_id(): view for view in views}
hierarchical_properties_by_view_id = SchemaAPI.get_hierarchical_properties(views)

def sort_by_instance_type(id_: dm.ViewId) -> int:
if id_ not in views_by_id:
return 0
return {"node": 1, "all": 2, "edge": 3}.get(views_by_id[id_].used_for, 0)

ordered_view_ids = sorted(iterations_by_view_id.keys(), key=sort_by_instance_type)
view_iterations: list[_ViewIterator] = []
for view_id in ordered_view_ids:
if view_id not in iterations_by_view_id:
continue
view_iteration = iterations_by_view_id[view_id]
view_iteration.view = views_by_id.get(view_id)
view_iteration.hierarchical_properties = properties_dependent_on_self_by_view_id.get(view_id, set())
view_iteration.hierarchical_properties = hierarchical_properties_by_view_id.get(view_id, set())
view_iterations.append(view_iteration)
return view_iterations, issues

Expand Down
2 changes: 1 addition & 1 deletion tests/tests_unit/graph/test_loaders/test_dms_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def test_metadata_as_json_filed():
dict,
)

assert instances["Asset_4288662884680989"].type.external_id == "Asset"
assert instances["Asset_4288662884680989"].type.external_id == "MyAsset"
assert instances["Asset_4288662884680989"].type.space == "neat_space"


Expand Down
12 changes: 2 additions & 10 deletions tests/tests_unit/test_neat_client/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,7 @@


class TestSchemaAPI:
def test_order_views_by_container_dependencies(self, cognite_core_schema: DMSSchema) -> None:
def test_get_hierarchical_properties(self, cognite_core_schema: DMSSchema) -> None:
schema = cognite_core_schema
external_ids = ["CogniteAsset", "CogniteDescribable", "CogniteSourceable", "CogniteVisualizable"]
selected_views = {v_id: v for v_id, v in schema.views.items() if v_id.external_id in external_ids}
selected_containers = [c for c in schema.containers.values() if c.external_id in external_ids]

id_order, hierarchical_properties = SchemaAPI.order_views_by_container_dependencies(
selected_views, selected_containers, skip_readonly=True
)
# The three first ones are not dependent on any other view
assert id_order[-1].external_id == "CogniteAsset"
hierarchical_properties = SchemaAPI.get_hierarchical_properties(schema.views.values())
assert hierarchical_properties == {dm.ViewId("cdf_cdm", "CogniteAsset", "v1"): {"parent"}}

0 comments on commit 61694b2

Please sign in to comment.