diff --git a/src/speckleifc/converter/data_object_converter.py b/src/speckleifc/converter/data_object_converter.py index 9df9793..65333f2 100644 --- a/src/speckleifc/converter/data_object_converter.py +++ b/src/speckleifc/converter/data_object_converter.py @@ -12,12 +12,23 @@ def data_object_to_speckle( step_element: entity_instance, children: list[Base], current_storey: str | None = None, + parent_element: entity_instance | None = None, ) -> DataObject: guid = cast(str, step_element.GlobalId) name = cast(str, step_element.Name or guid) properties = extract_properties(step_element) + # Add parent ID only if element's parent is also a DataObject (not a Collection) + # Collections are: IfcProject and IfcSpatialStructureElement types + if ( + parent_element + and hasattr(parent_element, "GlobalId") + and not parent_element.is_a("IfcProject") + and not parent_element.is_a("IfcSpatialStructureElement") + ): + properties["parentApplicationId"] = parent_element.GlobalId + # Add building storey information if available and not a building storey itself if current_storey and not step_element.is_a("IfcBuildingStorey"): properties["Building Storey"] = current_storey diff --git a/src/speckleifc/importer.py b/src/speckleifc/importer.py index 43329a0..5a57e7b 100644 --- a/src/speckleifc/importer.py +++ b/src/speckleifc/importer.py @@ -44,9 +44,13 @@ class ImportJob: _display_value_cache: dict[int, list[Base]] = field(default_factory=dict) """Maps an instance step ID to a list of instances""" - def convert_element(self, step_element: entity_instance) -> Base: + def convert_element( + self, + step_element: entity_instance, + parent_element: entity_instance | None = None, + ) -> Base: try: - return self._convert_element(step_element) + return self._convert_element(step_element, parent_element) except SpeckleException: raise except Exception as ex: @@ -54,14 +58,18 @@ class ImportJob: f"Failed to convert {step_element.is_a()} #{step_element.id()}" ) from ex - def _convert_element(self, step_element: entity_instance) -> Base: + def _convert_element( + self, + step_element: entity_instance, + parent_element: entity_instance | None = None, + ) -> Base: # Track current storey context and store for level proxies previous_storey_data_object = self._current_storey_data_object if step_element.is_a("IfcBuildingStorey"): # Convert the building storey to a DataObject for the level proxy storey_display_value = self._display_value_cache.get(step_element.id(), []) self._current_storey_data_object = data_object_to_speckle( - storey_display_value, step_element, [] + storey_display_value, step_element, [], parent_element=None ) children = self._convert_children(step_element) @@ -86,7 +94,11 @@ class ImportJob: ) else: result = data_object_to_speckle( - display_value, step_element, children, current_storey_name + display_value, + step_element, + children, + current_storey_name, + parent_element, ) # Associate non-spatial elements with current storey for level proxies if self._current_storey_data_object is not None and result.applicationId: @@ -100,7 +112,7 @@ class ImportJob: def _convert_children(self, step_element: entity_instance) -> list[Base]: return [ - self.convert_element(i) + self.convert_element(i, parent_element=step_element) for i in get_children(step_element) if self._should_convert(i) ]