From 74fe1b4eb92b6d1974043a13dc4b57a046dc32d7 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 20 Dec 2024 12:45:39 +0500 Subject: [PATCH] append_asset - ensure to update existing rels appending second asset E.g. in #5890 pset was shared by 2 elements and they it worked is 1) it appended first element and it's rel to pset 2) it appended second element and skipped updating it's rel as it was appended before. Now it's going to update it to ensure it encludes second element too. I believe, issue occurred after 8c28f52 when we stopped duplicating inverses and started to reuse them. --- .../ifcopenshell/api/project/append_asset.py | 26 ++++++++++++++----- .../test/api/project/test_append_asset.py | 18 +++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py index 5eb502ce99..0274521deb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/project/append_asset.py @@ -323,12 +323,16 @@ class Usecase: element_identity = element.wrapped_data.identity() - # check if inverse element were created before - if self.reuse_identities.get(element_identity) is not None: - return - - new = self.file.create_entity(element.is_a()) - self.reuse_identities[element_identity] = new + # Check if inverse element was created before. + # Still need to recreate it again - e.g. it could be some rel + # that now needs it's RelatingObjects to be extended by the current asset. + if (new := self.reuse_identities.get(element_identity)) is not None: + # Currently known cases requiring attributes reassignment are rels. + if not new.is_a("IfcRelationship"): + return + else: + new = self.file.create_entity(element.is_a()) + self.reuse_identities[element_identity] = new for i, attribute in enumerate(element): new_attribute = None @@ -346,11 +350,21 @@ class Usecase: new[i] = new_attribute def is_another_asset(self, element: ifcopenshell.entity_instance) -> bool: + """Is IFC entity from inverse attribute is another asset to append that should be skipped.""" + + def by_guid(guid: str) -> Union[ifcopenshell.entity_instance, None]: + try: + return self.file.by_guid(guid) + except RuntimeError: + return None + if element == self.settings["element"]: return False elif element.is_a("IfcFeatureElement"): # Feature elements match the target class but aren't considered "assets" return False + elif element.is_a("IfcRoot") and by_guid(element.GlobalId) is not None: + return False elif element.is_a(self.target_class): return True elif self.target_class == "IfcProduct" and element.is_a("IfcTypeProduct"): diff --git a/src/ifcopenshell-python/test/api/project/test_append_asset.py b/src/ifcopenshell-python/test/api/project/test_append_asset.py index e467bd4937..224d2f9d1b 100644 --- a/src/ifcopenshell-python/test/api/project/test_append_asset.py +++ b/src/ifcopenshell-python/test/api/project/test_append_asset.py @@ -433,6 +433,24 @@ class TestAppendAssetIFC2X3(test.bootstrap.IFC2X3): del shading_colour_info["id"], shading_colour_info["type"] assert shading_colour_info == shading_attrs["SurfaceColour"] + def test_update_rels_appending_subsequent_assets(self): + library = ifcopenshell.api.project.create_file(version=self.file.schema) + element1 = ifcopenshell.api.root.create_entity(library, ifc_class="IfcWall") + pset = ifcopenshell.api.pset.add_pset(library, product=element1, name="Test") + element2 = ifcopenshell.api.root.create_entity(library, ifc_class="IfcWall") + ifcopenshell.api.pset.assign_pset(library, pset=pset, products=[element2]) + + reuse_identities = {} + element1_ = ifcopenshell.api.project.append_asset( + self.file, library, element1, reuse_identities=reuse_identities + ) + element2_ = ifcopenshell.api.project.append_asset( + self.file, library, element2, reuse_identities=reuse_identities + ) + pset_data = ifcopenshell.util.element.get_psets(element1_) + assert "Test" in pset_data + assert ifcopenshell.util.element.get_psets(element2_) == pset_data + class TestAppendAssetIFC4(test.bootstrap.IFC4, TestAppendAssetIFC2X3): # NOTE: breaks in IFC2X3 since IfcProfileDef doesn't have "HasProperties" inverse in ifc2x3