From 98145fbbd993bcaf1bde4efc20e5f6b24fc6e681 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 20 Jun 2025 12:10:40 +0500 Subject: [PATCH] Purge/merge IfcPersonAndOrganizations --- .../bonsai/bim/module/debug/operator.py | 6 ++- src/bonsai/bonsai/bim/module/project/ui.py | 11 ++++- src/bonsai/bonsai/core/debug.py | 3 ++ src/bonsai/bonsai/tool/debug.py | 44 ++++++++++++++++--- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/debug/operator.py b/src/bonsai/bonsai/bim/module/debug/operator.py index d5dfeb61a0..27a0e2f3a6 100644 --- a/src/bonsai/bonsai/bim/module/debug/operator.py +++ b/src/bonsai/bonsai/bim/module/debug/operator.py @@ -766,10 +766,12 @@ class PurgeUnusedObjects(bpy.types.Operator, tool.Ifc.Operator): purged = core.purge_unused_elements(tool.Ifc, tool.Debug, "IfcApplication") elif object_type == "PERSON": purged = core.purge_unused_elements(tool.Ifc, tool.Debug, "IfcPerson") + elif object_type == "PERSON_AND_ORGANIZATION": + purged = core.purge_unused_elements(tool.Ifc, tool.Debug, "IfcPersonAndOrganization") else: assert_never(object_type) - self.report({"INFO"}, f"{purged} unused {object_type.lower()}s were purged.") + self.report({"INFO"}, f"{purged} unused {object_type.replace('_', ' ').lower()}s were purged.") if purged == 0: return @@ -798,7 +800,7 @@ class MergeIdenticalObjects(bpy.types.Operator, tool.Ifc.Operator): return {"CANCELLED"} merged_data = tool.Debug.merge_identical_objects(object_type) - plural_object_type = f"{object_type.lower()}s" + plural_object_type = f"{object_type.lower().replace('_', ' ')}s" if merged_data: for element_type, element_names in merged_data.items(): print(f"- {element_type}:") diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index b5c7fe5023..55585d324b 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -635,10 +635,17 @@ class BIM_PT_purge(Panel): layout.operator("bim.purge_unused_objects", text="Purge Unused Types").object_type = "TYPE" layout.operator("bim.purge_unused_openings", text="Purge Unused Openings in Selected Objects") - MERGEABLE_OBJECT_TYPES = ("MATERIAL", "STYLE", "ORGANIZATION", "APPLICATION", "PERSON") + MERGEABLE_OBJECT_TYPES = ( + "MATERIAL", + "STYLE", + "ORGANIZATION", + "APPLICATION", + "PERSON", + "PERSON_AND_ORGANIZATION", + ) for object_type in MERGEABLE_OBJECT_TYPES: row = layout.row(align=True) - row.label(text=f"{object_type.capitalize()}:") + row.label(text=f"{object_type.replace('_', ' ').capitalize()}:") row.operator("bim.purge_unused_objects", text="Purge Unused").object_type = object_type row.operator("bim.merge_identical_objects", text="Merge Identical").object_type = object_type diff --git a/src/bonsai/bonsai/core/debug.py b/src/bonsai/bonsai/core/debug.py index 6c1d02b1f6..2446a6e148 100644 --- a/src/bonsai/bonsai/core/debug.py +++ b/src/bonsai/bonsai/core/debug.py @@ -44,6 +44,9 @@ def purge_unused_elements(ifc: type[tool.Ifc], debug: type[tool.Debug], ifc_clas elif ifc_class == "IfcPerson": for element in unused_elements: ifc.run("owner.remove_person", person=element) + elif ifc_class == "IfcPersonAndOrganization": + for element in unused_elements: + ifc.run("owner.remove_person_and_organisation", person_and_organisation=element) else: debug.remove_unused_elements(unused_elements) return unused_elements_amount diff --git a/src/bonsai/bonsai/tool/debug.py b/src/bonsai/bonsai/tool/debug.py index 5cce67af4d..1999f84829 100644 --- a/src/bonsai/bonsai/tool/debug.py +++ b/src/bonsai/bonsai/tool/debug.py @@ -125,7 +125,14 @@ class Debug(bonsai.core.tool.Debug): @classmethod def merge_identical_objects( cls, - object_type: Literal["STYLE", "MATERIAL", "ORGANIZATION", "APPLICATION", "PERSON"], + object_type: Literal[ + "STYLE", + "MATERIAL", + "ORGANIZATION", + "APPLICATION", + "PERSON", + "PERSON_AND_ORGANIZATION", + ], ) -> dict[str, list[str]]: """Merge identical objects. @@ -137,6 +144,9 @@ class Debug(bonsai.core.tool.Debug): if object_type == "APPLICATION": # To avoid disruption let user merge organizations separately. data["ApplicationDeveloper"] = element.ApplicationDeveloper.id() + elif object_type == "PERSON_AND_ORGANIZATION": + data["ThePerson"] = element.ThePerson.id() + data["TheOrganization"] = element.TheOrganization.id() return hash(json.dumps(data, sort_keys=True)) ifc_file = tool.Ifc.get() @@ -155,9 +165,14 @@ class Debug(bonsai.core.tool.Debug): element_types = ["IfcApplication"] elif object_type == "PERSON": element_types = ["IfcPerson"] + elif object_type == "PERSON_AND_ORGANIZATION": + element_types = ["IfcPersonAndOrganization"] else: assert_never(object_type) + def get_person_name(person: ifcopenshell.entity_instance) -> str: + return f"{person.Identification} / {person.FamilyName} / {person.GivenName}" + for element_type in element_types: elements = ifc_file.by_type(element_type, include_subtypes=False) @@ -167,9 +182,11 @@ class Debug(bonsai.core.tool.Debug): # Except for styles, ignore unnamed elements as they may be not safe to merge merge_optional_names = ("STYLE", "PERSON") not_optional_name = ("APPLICATION", "ORGANIZATION") + has_no_name = ("PERSON_AND_ORGANIZATION",) if ( object_type not in merge_optional_names and object_type not in not_optional_name + and object_type not in has_no_name and not element.Name ): continue @@ -216,11 +233,15 @@ class Debug(bonsai.core.tool.Debug): elif object_type == "PERSON": for person in elements[1:]: ifcopenshell.util.element.replace_element(person, main_element) - merged_elements_names.append( - f"{person.Identification} / {person.FamilyName} / {person.GivenName}" - ) + merged_elements_names.append(get_person_name(person)) ifcopenshell.api.owner.remove_person(ifc_file, person) + elif object_type == "PERSON_AND_ORGANIZATION": + for pao in elements[1:]: + ifcopenshell.util.element.replace_element(pao, main_element) + merged_elements_names.append(f"{get_person_name(pao.ThePerson)} / {pao.TheOrganization.Name}") + ifcopenshell.api.owner.remove_person_and_organisation(ifc_file, pao) + else: assert_never(object_type) @@ -228,7 +249,16 @@ class Debug(bonsai.core.tool.Debug): merged_element_types[element_type] = merged_elements_names return merged_element_types - PurgeMergeObjectType = Literal["TYPE", "PROFILE", "STYLE", "MATERIAL", "ORGANIZATION", "APPLICATION", "PERSON"] + PurgeMergeObjectType = Literal[ + "TYPE", + "PROFILE", + "STYLE", + "MATERIAL", + "ORGANIZATION", + "APPLICATION", + "PERSON", + "PERSON_AND_ORGANIZATION", + ] @classmethod def refresh_ui_after_purge_merge(cls, object_type: PurgeMergeObjectType) -> None: @@ -258,5 +288,9 @@ class Debug(bonsai.core.tool.Debug): props = tool.Owner.get_owner_props() if tool.Ifc.get_entity_by_id(props.active_person_id) is None: tool.Owner.clear_person() + elif object_type == "PERSON_AND_ORGANIZATION": + props = tool.Owner.get_owner_props() + if tool.Ifc.get_entity_by_id(props.active_person_id) is None: + tool.Owner.clear_user() else: assert_never(object_type)