From 6fd629f70783c2f012ee7a1cfd5470e86c93816a Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Tue, 21 Nov 2023 12:34:53 +0500 Subject: [PATCH] Fixed issue reassigning class to type class with userdefined type #4018 When PredefinedType is not found in the related enum reassign_class is considering it USERDEFINED and trying to set .ObjectType. The problem was it was doing for type classes also, when it should have set .ElementType instead. Why it crashed BlenderBIM - in IfcClassData.data["has_entity"] we store currently active IFC entity which is recreated during `ifcopenshell.util.schema.reassign_class` making old entity invalid. Since `root.reassign_class` was failing in the process, related BBIM operator was failing too, IfcClassData wasn't updated and removed entity was accessed from UI leading to crash. --- .../ifcopenshell/api/root/reassign_class.py | 14 +++++++++++--- .../test/api/root/test_reassign_class.py | 10 +++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py index aef53b625f..46acc6903a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py +++ b/src/ifcopenshell-python/ifcopenshell/api/root/reassign_class.py @@ -73,13 +73,21 @@ class Usecase: element = ifcopenshell.util.schema.reassign_class( self.file, self.settings["product"], self.settings["ifc_class"] ) + is_type = element.is_a("IfcTypeProduct") if self.settings["predefined_type"] and hasattr(element, "PredefinedType"): try: element.PredefinedType = self.settings["predefined_type"] except: + # PredefinedType wasn't in the respective enum, assume it's actually USERDEFINED + # and set .ElementType / .ObjectType to the provided predefined type element.PredefinedType = "USERDEFINED" - element.ObjectType = self.settings["predefined_type"] - if element.is_a("IfcTypeProduct"): + if is_type: + element.ElementType = self.settings["predefined_type"] + else: + element.ObjectType = self.settings["predefined_type"] + + # reassign classes for occurences connected to the type + if is_type: for occurrence in ifcopenshell.util.element.get_types(element) or []: ifc_class = ifcopenshell.util.type.get_applicable_entities(self.settings["ifc_class"])[0] ifcopenshell.api.run( @@ -87,6 +95,6 @@ class Usecase: self.file, product=occurrence, ifc_class=ifc_class, - predefined_type= self.settings["predefined_type"] + predefined_type=self.settings["predefined_type"], ) return element diff --git a/src/ifcopenshell-python/test/api/root/test_reassign_class.py b/src/ifcopenshell-python/test/api/root/test_reassign_class.py index aef105d1ce..3049b4cd3b 100644 --- a/src/ifcopenshell-python/test/api/root/test_reassign_class.py +++ b/src/ifcopenshell-python/test/api/root/test_reassign_class.py @@ -35,10 +35,18 @@ class TestReassignClass(test.bootstrap.IFC4): ) assert new.PredefinedType == "FLOOR" - def test_falling_back_to_userdefined_if_the_predefined_type_cannot_be_reassigned(self): + def test_falling_back_to_userdefined_if_the_predefined_type_cannot_be_reassigned_for_occurrence_class(self): element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall") new = ifcopenshell.api.run( "root.reassign_class", self.file, product=element, ifc_class="IfcSlab", predefined_type="FOO" ) assert new.PredefinedType == "USERDEFINED" assert new.ObjectType == "FOO" + + def test_falling_back_to_userdefined_if_the_predefined_type_cannot_be_reassigned_for_type_class(self): + element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWallType") + new = ifcopenshell.api.run( + "root.reassign_class", self.file, product=element, ifc_class="IfcSlabType", predefined_type="FOO" + ) + assert new.PredefinedType == "USERDEFINED" + assert new.ElementType == "FOO"