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
@@ -4,11 +4,23 @@ its sub-models and validators.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import math
7
8
  import sys
8
9
  import types
9
10
  from abc import ABC, abstractmethod
10
11
  from collections.abc import Callable, Hashable, Iterator, MutableSequence, Sequence
11
- from typing import Annotated, Any, ClassVar, Literal, SupportsIndex, TypeVar, get_args, get_origin, overload
12
+ from datetime import datetime
13
+ from typing import (
14
+ Annotated,
15
+ Any,
16
+ ClassVar,
17
+ Literal,
18
+ SupportsIndex,
19
+ TypeVar,
20
+ get_args,
21
+ get_origin,
22
+ overload,
23
+ )
12
24
 
13
25
  import pandas as pd
14
26
  from pydantic import (
@@ -23,15 +35,22 @@ from pydantic import (
23
35
  )
24
36
  from pydantic.main import IncEx
25
37
  from pydantic_core import core_schema
26
-
27
- from cognite.neat._rules.models._types import ClassEntityType, InformationPropertyType
38
+ from rdflib import Namespace, URIRef
39
+
40
+ from cognite.neat._constants import DEFAULT_NAMESPACE
41
+ from cognite.neat._rules.models._types import (
42
+ ClassEntityType,
43
+ DataModelExternalIdType,
44
+ InformationPropertyType,
45
+ SpaceType,
46
+ StrListType,
47
+ VersionType,
48
+ )
28
49
 
29
50
  if sys.version_info >= (3, 11):
30
51
  from enum import StrEnum
31
- from typing import Self
32
52
  else:
33
53
  from backports.strenum import StrEnum
34
- from typing_extensions import Self
35
54
 
36
55
 
37
56
  METADATA_VALUE_MAX_LENGTH = 5120
@@ -82,18 +101,18 @@ class DataModelType(StrEnum):
82
101
  enterprise = "enterprise"
83
102
 
84
103
 
104
+ class DataModelAspect(StrEnum):
105
+ conceptual = "conceptual"
106
+ logical = "logical"
107
+ physical = "physical"
108
+
109
+
85
110
  class RoleTypes(StrEnum):
86
111
  domain_expert = "domain expert"
87
112
  information = "information architect"
88
- asset = "asset architect"
89
113
  dms = "DMS Architect"
90
114
 
91
115
 
92
- class MatchType(StrEnum):
93
- exact = "exact"
94
- partial = "partial"
95
-
96
-
97
116
  class SchemaModel(BaseModel):
98
117
  model_config: ClassVar[ConfigDict] = ConfigDict(
99
118
  populate_by_name=True,
@@ -125,6 +144,46 @@ class BaseMetadata(SchemaModel):
125
144
  """
126
145
 
127
146
  role: ClassVar[RoleTypes]
147
+ aspect: ClassVar[DataModelAspect]
148
+ space: SpaceType = Field(alias="prefix")
149
+ external_id: DataModelExternalIdType = Field(alias="externalId")
150
+ version: VersionType
151
+
152
+ name: str | None = Field(
153
+ None,
154
+ description="Human readable name of the data model",
155
+ min_length=1,
156
+ max_length=255,
157
+ )
158
+
159
+ description: str | None = Field(None, min_length=1, max_length=1024)
160
+
161
+ creator: StrListType = Field(
162
+ description=(
163
+ "List of contributors to the data model creation, "
164
+ "typically information architects are considered as contributors."
165
+ ),
166
+ )
167
+
168
+ created: datetime = Field(
169
+ description=("Date of the data model creation"),
170
+ )
171
+
172
+ updated: datetime = Field(
173
+ description=("Date of the data model update"),
174
+ )
175
+
176
+ @field_validator("*", mode="before")
177
+ def strip_string(cls, value: Any) -> Any:
178
+ if isinstance(value, str):
179
+ return value.strip()
180
+ return value
181
+
182
+ @field_validator("description", mode="before")
183
+ def nan_as_none(cls, value):
184
+ if isinstance(value, float) and math.isnan(value):
185
+ return None
186
+ return value
128
187
 
129
188
  def to_pandas(self) -> pd.Series:
130
189
  """Converts Metadata to pandas Series."""
@@ -143,15 +202,30 @@ class BaseMetadata(SchemaModel):
143
202
  def include_role(self, serializer: Callable) -> dict:
144
203
  return {"role": self.role.value, **serializer(self)}
145
204
 
146
- @abstractmethod
205
+ @property
206
+ def prefix(self) -> str:
207
+ return self.space
208
+
147
209
  def as_identifier(self) -> str:
148
- """Returns a unique identifier for the metadata."""
149
- raise NotImplementedError()
210
+ return f"{self.prefix}:{self.external_id}"
150
211
 
151
- @abstractmethod
152
212
  def get_prefix(self) -> str:
153
- """Returns the prefix for the metadata."""
154
- raise NotImplementedError()
213
+ return self.prefix
214
+
215
+ @property
216
+ def identifier(self) -> URIRef:
217
+ """Globally unique identifier for the data model.
218
+
219
+ !!! note
220
+ Unlike namespace, the identifier does not end with "/" or "#".
221
+
222
+ """
223
+ return DEFAULT_NAMESPACE[f"data-model/verified/{self.aspect}/{self.space}/{self.external_id}/{self.version}"]
224
+
225
+ @property
226
+ def namespace(self) -> Namespace:
227
+ """Namespace for the data model used for the entities in the data model."""
228
+ return Namespace(f"{self.identifier}/")
155
229
 
156
230
 
157
231
  class BaseRules(SchemaModel, ABC):
@@ -169,7 +243,6 @@ class BaseRules(SchemaModel, ABC):
169
243
  """
170
244
 
171
245
  metadata: BaseMetadata
172
- reference: Self | None = Field(None, alias="Reference")
173
246
 
174
247
  @classmethod
175
248
  def headers_by_sheet(cls, by_alias: bool = False) -> dict[str, list[str]]:
@@ -207,7 +280,6 @@ class BaseRules(SchemaModel, ABC):
207
280
  def dump(
208
281
  self,
209
282
  entities_exclude_defaults: bool = True,
210
- as_reference: bool = False,
211
283
  mode: Literal["python", "json"] = "python",
212
284
  by_alias: bool = False,
213
285
  exclude: IncEx | None = None,
@@ -224,9 +296,6 @@ class BaseRules(SchemaModel, ABC):
224
296
  For example, given a class that is dumped as 'my_prefix:MyClass', if the prefix for the rules
225
297
  set in metadata.prefix = 'my_prefix', then this class will be dumped as 'MyClass' when this flag is set.
226
298
  Defaults to True.
227
- as_reference (bool, optional): Whether to dump as reference. For Information and DMS rules, this will
228
- set the reference column/field to the reference of that entity. This is used in the ExcelExporter
229
- to dump a reference model.
230
299
  mode: The mode in which `to_python` should run.
231
300
  If mode is 'json', the output will only contain JSON serializable types.
232
301
  If mode is 'python', the output may contain non-JSON-serializable Python objects.
@@ -242,25 +311,11 @@ class BaseRules(SchemaModel, ABC):
242
311
  if isinstance(value, SheetList):
243
312
  value.sort(key=lambda x: x._identifier())
244
313
 
245
- context: dict[str, Any] = {"as_reference": as_reference}
314
+ context: dict[str, Any] = {}
246
315
  if entities_exclude_defaults:
247
316
  context["metadata"] = self.metadata
248
317
 
249
- exclude_input: IncEx | None
250
- if self.reference is None:
251
- exclude_input = exclude
252
- else:
253
- # If the rules has a reference, we dump that separately with the as_reference flag set to True.
254
- # We don't want to include the reference in the main dump, so we exclude it here.
255
- # This is to include whatever is in the exclude set from the user.
256
- if isinstance(exclude, dict):
257
- exclude_input = exclude.copy()
258
- exclude_input["reference"] = {"__all__"} # type: ignore[index]
259
- elif isinstance(exclude, set):
260
- exclude_input = exclude.copy()
261
- exclude_input.add("reference") # type: ignore[arg-type]
262
- else:
263
- exclude_input = {"reference"}
318
+ exclude_input: IncEx | None = exclude
264
319
 
265
320
  output = self.model_dump(
266
321
  mode=mode,
@@ -271,20 +326,7 @@ class BaseRules(SchemaModel, ABC):
271
326
  exclude_defaults=exclude_defaults,
272
327
  context=context,
273
328
  )
274
- is_reference_user_excluded = isinstance(exclude, dict | set) and "reference" in exclude
275
- if self.reference is not None and not is_reference_user_excluded:
276
- # If the rules has a reference, we dump that separately with the as_reference flag set to True.
277
- # Unless the user has explicitly excluded the reference.
278
- output["Reference" if by_alias else "reference"] = self.reference.dump(
279
- mode=mode,
280
- by_alias=by_alias,
281
- exclude=exclude,
282
- exclude_none=exclude_none,
283
- exclude_unset=exclude_unset,
284
- exclude_defaults=exclude_defaults,
285
- entities_exclude_defaults=entities_exclude_defaults,
286
- as_reference=True,
287
- )
329
+
288
330
  return output
289
331
 
290
332
 
@@ -293,6 +335,10 @@ class SheetRow(SchemaModel):
293
335
  def _identifier(self) -> tuple[Hashable, ...]:
294
336
  raise NotImplementedError()
295
337
 
338
+ def __repr__(self) -> str:
339
+ # Simplified representation of the object for debugging
340
+ return f"{self.__class__.__name__}({self._identifier()})"
341
+
296
342
 
297
343
  T_SheetRow = TypeVar("T_SheetRow", bound=SheetRow)
298
344
 
@@ -1,7 +1,7 @@
1
1
  import warnings
2
2
  from collections import defaultdict
3
3
  from collections.abc import Collection, Hashable, Sequence
4
- from typing import Any, cast
4
+ from typing import Any
5
5
 
6
6
  from cognite.client.data_classes import data_modeling as dm
7
7
  from cognite.client.data_classes.data_modeling.containers import BTreeIndex
@@ -18,10 +18,7 @@ from cognite.neat._issues.warnings import NotSupportedWarning, PropertyNotFoundW
18
18
  from cognite.neat._issues.warnings.user_modeling import (
19
19
  EmptyContainerWarning,
20
20
  HasDataFilterOnNoPropertiesViewWarning,
21
- HasDataFilterOnViewWithReferencesWarning,
22
- NodeTypeFilterOnParentViewWarning,
23
21
  )
24
- from cognite.neat._rules.models._base_rules import DataModelType, ExtensionCategory, SchemaCompleteness
25
22
  from cognite.neat._rules.models.data_types import DataType, Double, Enum, Float
26
23
  from cognite.neat._rules.models.entities import (
27
24
  ClassEntity,
@@ -32,7 +29,6 @@ from cognite.neat._rules.models.entities import (
32
29
  EdgeEntity,
33
30
  HasDataFilter,
34
31
  NodeTypeFilter,
35
- ReferenceEntity,
36
32
  ReverseConnectionEntity,
37
33
  UnitEntity,
38
34
  ViewEntity,
@@ -64,60 +60,19 @@ class _DMSExporter:
64
60
  self.include_pipeline = include_pipeline
65
61
  self.instance_space = instance_space
66
62
  self.rules = rules
67
- self._ref_schema = rules.reference.as_schema() if rules.reference else None
63
+ self._ref_schema = None
68
64
  if self._ref_schema:
69
65
  # We skip version as that will always be missing in the reference
70
66
  self._ref_views_by_id = {
71
67
  dm.ViewId(view.space, view.external_id): view for view in self._ref_schema.views.values()
72
68
  }
73
69
  else:
74
- self._ref_views_by_id = {}
75
-
76
- self.is_addition = (
77
- rules.metadata.schema_ is SchemaCompleteness.extended
78
- and rules.metadata.extension is ExtensionCategory.addition
79
- )
80
- self.is_reshape = (
81
- rules.metadata.schema_ is SchemaCompleteness.extended
82
- and rules.metadata.extension is ExtensionCategory.reshape
83
- )
84
- self.is_rebuild = (
85
- rules.metadata.schema_ is SchemaCompleteness.extended
86
- and rules.metadata.extension is ExtensionCategory.rebuild
87
- )
70
+ self._ref_views_by_id = {} # type: ignore
88
71
 
89
72
  def to_schema(self) -> DMSSchema:
90
73
  rules = self.rules
91
74
  container_properties_by_id, view_properties_by_id = self._gather_properties(list(self.rules.properties))
92
75
 
93
- # If we are reshaping or rebuilding, and there are no properties in the current rules, we will
94
- # include those properties from the last rules.
95
- if rules.last and (self.is_reshape or self.is_rebuild):
96
- selected_views = {view.view for view in rules.views}
97
- selected_properties = [
98
- prop
99
- for prop in rules.last.properties
100
- if prop.view in selected_views and prop.view.as_id() not in view_properties_by_id
101
- ]
102
- self._update_with_properties(
103
- selected_properties, container_properties_by_id, view_properties_by_id, include_new_containers=True
104
- )
105
-
106
- # We need to include the properties from the last rules as well to create complete containers and view
107
- # depending on the type of extension.
108
- if rules.last and self.is_addition:
109
- selected_properties = [
110
- prop for prop in rules.last.properties if (prop.view.as_id() in view_properties_by_id)
111
- ]
112
- self._update_with_properties(selected_properties, container_properties_by_id, view_properties_by_id)
113
- elif rules.last and (self.is_reshape or self.is_rebuild):
114
- selected_properties = [
115
- prop
116
- for prop in rules.last.properties
117
- if prop.container and prop.container.as_id() in container_properties_by_id
118
- ]
119
- self._update_with_properties(selected_properties, container_properties_by_id, None)
120
-
121
76
  containers = self._create_containers(container_properties_by_id, rules.enum) # type: ignore[arg-type]
122
77
 
123
78
  view_properties_with_ancestors_by_id = self._gather_properties_with_ancestors(
@@ -136,30 +91,11 @@ class _DMSExporter:
136
91
  node_types = NodeApplyDict([dm.NodeApply(node.space, node.external_id) for node in view_node_type_filters])
137
92
 
138
93
  last_schema: DMSSchema | None = None
139
- if self.rules.last:
140
- last_schema = self.rules.last.as_schema()
141
- # Remove the views that are in the current model, last + current should equal the full model
142
- # without any duplicates
143
- last_schema.views = ViewApplyDict(
144
- {view_id: view for view_id, view in last_schema.views.items() if view_id not in views}
145
- )
146
- last_schema.containers = ContainerApplyDict(
147
- {
148
- container_id: container
149
- for container_id, container in last_schema.containers.items()
150
- if container_id not in containers
151
- }
152
- )
153
94
 
154
95
  views_not_in_model = {view.view.as_id() for view in rules.views if not view.in_model}
155
- if rules.last and self.is_addition:
156
- views_not_in_model.update({view.view.as_id() for view in rules.last.views if not view.in_model})
157
-
158
96
  data_model = rules.metadata.as_data_model()
159
97
 
160
98
  data_model_views = [view_id for view_id in views if view_id not in views_not_in_model]
161
- if last_schema and self.is_addition:
162
- data_model_views.extend([view_id for view_id in last_schema.views if view_id not in views_not_in_model])
163
99
 
164
100
  # Sorting to ensure deterministic order
165
101
  data_model.views = sorted(data_model_views, key=lambda v: v.as_tuple()) # type: ignore[union-attr]
@@ -207,17 +143,8 @@ class _DMSExporter:
207
143
  view_properties_with_ancestors_by_id: dict[dm.ViewId, list[DMSProperty]],
208
144
  ) -> tuple[ViewApplyDict, set[dm.NodeId]]:
209
145
  input_views = list(self.rules.views)
210
- if self.rules.last:
211
- existing = {view.view.as_id() for view in input_views}
212
- modified_views = [
213
- v
214
- for v in self.rules.last.views
215
- if v.view.as_id() in view_properties_by_id and v.view.as_id() not in existing
216
- ]
217
- input_views.extend(modified_views)
218
146
 
219
147
  views = ViewApplyDict([dms_view.as_view() for dms_view in input_views])
220
- dms_view_by_id = {dms_view.view.as_id(): dms_view for dms_view in input_views}
221
148
 
222
149
  for view_id, view in views.items():
223
150
  view.properties = {}
@@ -228,43 +155,9 @@ class _DMSExporter:
228
155
  if view_property is not None:
229
156
  view.properties[prop.view_property] = view_property
230
157
 
231
- data_model_type = self.rules.metadata.data_model_type
232
158
  unique_node_types: set[dm.NodeId] = set()
233
- parent_views = {parent for view in views.values() for parent in view.implements or []}
234
- for view_id, view in views.items():
235
- dms_view = dms_view_by_id.get(view_id)
236
- dms_properties = view_properties_by_id.get(view_id, [])
237
- view_filter = self._create_view_filter(view, dms_view, data_model_type, dms_properties)
238
-
239
- view.filter = view_filter.as_dms_filter()
240
- if isinstance(view_filter, NodeTypeFilter):
241
- unique_node_types.update(view_filter.nodes)
242
- if view.as_id() in parent_views:
243
- warnings.warn(
244
- NodeTypeFilterOnParentViewWarning(view.as_id()),
245
- stacklevel=2,
246
- )
247
-
248
- elif isinstance(view_filter, HasDataFilter) and data_model_type == DataModelType.solution:
249
- if dms_view and isinstance(dms_view.reference, ReferenceEntity):
250
- references = {dms_view.reference.as_view_id()}
251
- elif any(True for prop in dms_properties if isinstance(prop.reference, ReferenceEntity)):
252
- references = {
253
- prop.reference.as_view_id()
254
- for prop in dms_properties
255
- if isinstance(prop.reference, ReferenceEntity)
256
- }
257
- else:
258
- continue
259
- warnings.warn(
260
- HasDataFilterOnViewWithReferencesWarning(view.as_id(), frozenset(references)),
261
- stacklevel=2,
262
- )
263
-
264
- if data_model_type == DataModelType.enterprise:
265
- # Enterprise Model needs to create node types for all views,
266
- # as they are expected for the solution model.
267
- unique_node_types.add(dm.NodeId(space=view.space, external_id=view.external_id))
159
+ for view in views.values():
160
+ unique_node_types.add(dm.NodeId(space=view.space, external_id=view.external_id))
268
161
 
269
162
  return views, unique_node_types
270
163
 
@@ -272,9 +165,6 @@ class _DMSExporter:
272
165
  def _create_edge_type_from_prop(cls, prop: DMSProperty) -> dm.DirectRelationReference:
273
166
  if isinstance(prop.connection, EdgeEntity) and prop.connection.edge_type is not None:
274
167
  return prop.connection.edge_type.as_reference()
275
- elif isinstance(prop.reference, ReferenceEntity):
276
- ref_view_prop = prop.reference.as_view_property_id()
277
- return cls._create_edge_type_from_view_id(cast(dm.ViewId, ref_view_prop.source), ref_view_prop.property)
278
168
  elif isinstance(prop.value_type, ViewEntity):
279
169
  return cls._create_edge_type_from_view_id(prop.view.as_id(), prop.view_property)
280
170
  else:
@@ -298,14 +188,6 @@ class _DMSExporter:
298
188
  enum_values_by_collection[enum_value.collection].append(enum_value)
299
189
 
300
190
  containers = list(self.rules.containers or [])
301
- if self.rules.last:
302
- existing = {container.container.as_id() for container in containers}
303
- modified_containers = [
304
- c
305
- for c in self.rules.last.containers or []
306
- if c.container.as_id() in container_properties_by_id and c.container.as_id() not in existing
307
- ]
308
- containers.extend(modified_containers)
309
191
 
310
192
  containers = dm.ContainerApplyList([dms_container.as_container() for dms_container in containers])
311
193
  container_to_drop = set()
@@ -408,17 +290,6 @@ class _DMSExporter:
408
290
  views: Sequence[DMSView],
409
291
  ) -> dict[dm.ViewId, list[DMSProperty]]:
410
292
  all_view_properties_by_id = view_properties_by_id.copy()
411
- if self.rules.reference:
412
- # We need to include t
413
- ref_view_properties_by_id = self._gather_properties(self.rules.reference.properties)[1]
414
- for view_id, properties in ref_view_properties_by_id.items():
415
- if view_id not in all_view_properties_by_id:
416
- all_view_properties_by_id[view_id] = properties
417
- else:
418
- existing_properties = {prop._identifier() for prop in all_view_properties_by_id[view_id]}
419
- for prop in properties:
420
- if prop._identifier() not in existing_properties:
421
- all_view_properties_by_id[view_id].append(prop)
422
293
 
423
294
  view_properties_with_parents_by_id: dict[dm.ViewId, list[DMSProperty]] = defaultdict(list)
424
295
  view_by_view_id = {view.view.as_id(): view for view in views}
@@ -482,37 +353,13 @@ class _DMSExporter:
482
353
  self,
483
354
  view: dm.ViewApply,
484
355
  dms_view: DMSView | None,
485
- data_model_type: DataModelType,
486
- dms_properties: list[DMSProperty],
487
- ) -> DMSFilter:
356
+ ) -> DMSFilter | None:
488
357
  selected_filter_name = (dms_view and dms_view.filter_ and dms_view.filter_.name) or ""
489
358
 
490
359
  if dms_view and dms_view.filter_ and not dms_view.filter_.is_empty:
491
360
  # Has Explicit Filter, use it
492
361
  return dms_view.filter_
493
362
 
494
- if data_model_type == DataModelType.solution and selected_filter_name in [NodeTypeFilter.name, ""]:
495
- if (
496
- dms_view
497
- and isinstance(dms_view.reference, ReferenceEntity)
498
- and not dms_properties
499
- and (ref_view := self._ref_views_by_id.get(dms_view.reference.as_view_id()))
500
- and ref_view.filter
501
- ):
502
- # No new properties, only reference, reuse the reference filter
503
- return DMSFilter.from_dms_filter(ref_view.filter)
504
- else:
505
- referenced_node_ids = {
506
- prop.reference.as_node_entity()
507
- for prop in dms_properties
508
- if isinstance(prop.reference, ReferenceEntity)
509
- }
510
- if dms_view and isinstance(dms_view.reference, ReferenceEntity):
511
- referenced_node_ids.add(dms_view.reference.as_node_entity())
512
-
513
- if referenced_node_ids:
514
- return NodeTypeFilter(inner=list(referenced_node_ids))
515
-
516
363
  # Enterprise Model or (Solution + HasData)
517
364
  ref_containers = view.referenced_containers()
518
365
  if not ref_containers or selected_filter_name == HasDataFilter.name:
@@ -597,7 +444,7 @@ class _DMSExporter:
597
444
  (
598
445
  prop
599
446
  for prop in view_properties_with_ancestors_by_id.get(source_view_id, [])
600
- if prop.property_ == reverse_prop_id
447
+ if prop.view_property == reverse_prop_id
601
448
  ),
602
449
  None,
603
450
  )