diff --git a/src/bonsai/bonsai/tool/loader.py b/src/bonsai/bonsai/tool/loader.py index ceb5cfd688..461bed1d4d 100644 --- a/src/bonsai/bonsai/tool/loader.py +++ b/src/bonsai/bonsai/tool/loader.py @@ -926,9 +926,32 @@ class Loader(bonsai.core.tool.Loader): return mesh @classmethod - def setup_active_bsdd_classification(cls): + def setup_active_bsdd_classification(cls) -> None: ifc_file = tool.Ifc.get() - attr_name = "Specification" if ifc_file.schema == "IFC4X3" else "Location" + schema = ifc_file.schema + + # In IFC2X3 IfcClassification doesn't have an attribute for uri. + if schema == "IFC2X3": + classifications = [c for c in ifc_file.by_type("IfcClassification") if c.Name] + if not classifications: + return + pattern = r"^https://identifier\.buildingsmart\.org/uri/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/([0-9]+\.[0-9]+)" + + # No inverse attribute in IFC2X3... + for ref in ifc_file.by_type("IfcClassificationReference"): + if ( + not (uri := ref.Location) + or not uri.startswith("https://identifier.buildingsmart.org/uri/") + or not (pattern_match := re.match(pattern, uri)) + or not (classification := ref.ReferencedSource) + or classification not in classifications + or not classification.is_a("IfcClassification") + ): + continue + tool.Bsdd.set_active_bsdd(classification.Name, pattern_match.group(0)) + return + + attr_name = "Specification" if schema == "IFC4X3" else "Location" bsdd_classification, uri, name = None, None, None for c in ifc_file.by_type("IfcClassification"): if ( diff --git a/src/bonsai/test/tool/test_loader.py b/src/bonsai/test/tool/test_loader.py index ba0d9a10b8..74e4839074 100644 --- a/src/bonsai/test/tool/test_loader.py +++ b/src/bonsai/test/tool/test_loader.py @@ -19,6 +19,7 @@ import bpy import bmesh import ifcopenshell +import ifcopenshell.util.schema import bonsai.core.tool import bonsai.tool as tool from test.bim.bootstrap import NewFile @@ -513,15 +514,32 @@ class TestLoadingIndexedMap(NewFile): class TestSetupActiveBsddClassification(NewFile): - def test_run(self): + def run_test(self, schema: ifcopenshell.util.schema.IFC_SCHEMA) -> None: + schema_ = "IFC4X3_ADD2" if schema == "IFC4X3" else schema + bpy.context.scene.BIMProjectProperties.export_schema = schema_ bpy.ops.bim.create_project() ifc_file = tool.Ifc.get() name = "CCI Construction" uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0" - ifc_file.create_entity("IfcClassification", Name=name, Location=uri) + if schema == "IFC2X3": + classification = ifc_file.create_entity("IfcClassification", Name=name) + ifc_file.create_entity("IfcClassificationReference", Location=uri, ReferencedSource=classification) + else: + attr_name = "Specification" if schema == "IFC4X3" else "Location" + classification = ifc_file.create_entity("IfcClassification", Name=name) + setattr(classification, attr_name, uri) filepath = "test/files/temp/test.ifc" ifc_file.write(filepath) bpy.ops.bim.load_project(filepath=filepath) props = bpy.context.scene.BIMBSDDProperties assert props.active_domain == name assert props.active_uri == uri + + def test_set_load_and_set_active_bsdd_ifc2x3(self): + self.run_test("IFC2X3") + + def test_set_load_and_set_active_bsdd_ifc4(self): + self.run_test("IFC4") + + def test_set_load_and_set_active_bsdd_ifc4x3(self): + self.run_test("IFC4X3")