Fix #7208. Merge duplicates patch recipe can now control merging by empty attributes.

This commit is contained in:
Dion Moult
2025-10-06 23:18:27 +11:00
parent 7cba63af9c
commit 25dacc8fed
2 changed files with 18 additions and 4 deletions
@@ -25,7 +25,7 @@ from typing import Any
class Patcher:
def __init__(self, file: ifcopenshell.file, logger: Logger, attribute: str = "Tag"):
def __init__(self, file: ifcopenshell.file, logger: Logger, attribute: str = "Tag", should_merge_null: bool = True):
"""Merge duplicate element types via the Tag or another attribute
Revit is notorious for creating many duplicate element types. Element
@@ -50,6 +50,8 @@ class Patcher:
:param attribute: The name of the attribute to merge element types based
on. Typically this will be "Tag" as it stores the unique ID from the
proprietary BIM software.
:param should_merge_null: If True, all elements with an empty attribute
will be merged. If False, they will be kept separate.
Example:
@@ -64,12 +66,15 @@ class Patcher:
self.file = file
self.logger = logger
self.attribute = attribute
self.should_merge_null = should_merge_null
def patch(self):
key = self.attribute
keys: dict[Any, ifcopenshell.entity_instance] = {}
for element_type in self.file.by_type("IfcTypeObject"):
original_type = keys.get(getattr(element_type, key), None)
key = getattr(element_type, self.attribute)
if not key and not self.should_merge_null:
continue
original_type = keys.get(key, None)
if original_type:
elements = ifcopenshell.util.element.get_types(element_type)
if elements:
@@ -78,7 +83,7 @@ class Patcher:
ifcopenshell.util.element.replace_attribute(inverse, element_type, original_type)
self.file.remove(element_type)
else:
keys[getattr(element_type, key)] = element_type
keys[key] = element_type
def assign_type(
self, related_objects: list[ifcopenshell.entity_instance], relating_type: ifcopenshell.entity_instance
@@ -74,6 +74,15 @@ class TestMergeDuplicateTypes(test.bootstrap.IFC4):
assert ifcopenshell.util.element.get_material(wall1, should_inherit=False) == None
assert ifcopenshell.util.element.get_material(wall2, should_inherit=False) == None
def test_not_merging_empty_attributes(self):
wall_type1 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWallType", name="")
wall_type2 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWallType", name="")
output = ifcpatch.execute({"file": self.file, "recipe": "MergeDuplicateTypes", "arguments": ["Name", True]})
assert len(output.by_type("IfcWallType")) == 1
wall_type3 = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWallType", name="")
output = ifcpatch.execute({"file": self.file, "recipe": "MergeDuplicateTypes", "arguments": ["Name", False]})
assert len(output.by_type("IfcWallType")) == 2
class TestMergeDuplicateTypesIFC2X3(test.bootstrap.IFC2X3, TestMergeDuplicateTypes):
pass