diff --git a/src/blenderbim/blenderbim/core/library.py b/src/blenderbim/blenderbim/core/library.py index c1103f5bf9..6b5ce7e767 100644 --- a/src/blenderbim/blenderbim/core/library.py +++ b/src/blenderbim/blenderbim/core/library.py @@ -87,4 +87,4 @@ def assign_library_reference(ifc, obj=None, reference=None): def unassign_library_reference(ifc, obj=None, reference=None): - ifc.run("library.unassign_reference", product=ifc.get_entity(obj), reference=reference) + ifc.run("library.unassign_reference", products=[ifc.get_entity(obj)], reference=reference) diff --git a/src/blenderbim/test/core/test_library.py b/src/blenderbim/test/core/test_library.py index 9e4f9ffee6..4722cde5c0 100644 --- a/src/blenderbim/test/core/test_library.py +++ b/src/blenderbim/test/core/test_library.py @@ -121,5 +121,5 @@ class TestAssignLibraryReference: class TestUnassignLibraryReference: def test_run(self, ifc): ifc.get_entity("obj").should_be_called().will_return("product") - ifc.run("library.unassign_reference", product="product", reference="reference").should_be_called() + ifc.run("library.unassign_reference", products=["product"], reference="reference").should_be_called() subject.unassign_library_reference(ifc, obj="obj", reference="reference") diff --git a/src/ifcopenshell-python/ifcopenshell/api/__init__.py b/src/ifcopenshell-python/ifcopenshell/api/__init__.py index b9e60e65a6..6d27dbebfd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/__init__.py +++ b/src/ifcopenshell-python/ifcopenshell/api/__init__.py @@ -96,6 +96,9 @@ ARGUMENTS_DEPRECATION = { "library.assign_reference": partial( batching_argument_deprecation, prev_argument="product", new_argument="products" ), + "library.unassign_reference": partial( + batching_argument_deprecation, prev_argument="product", new_argument="products" + ), } diff --git a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py index 5bd04bc581..b650ffba75 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py +++ b/src/ifcopenshell-python/ifcopenshell/api/library/unassign_reference.py @@ -18,18 +18,24 @@ import ifcopenshell import ifcopenshell.util.element +import ifcopenshell.api class Usecase: - def __init__(self, file, reference=None, product=None): - """Unassigns a product from a reference + def __init__( + self, + file: ifcopenshell.file, + reference: ifcopenshell.entity_instance, + products: list[ifcopenshell.entity_instance], + ): + """Unassigns a product of products from a reference If the product isn't assigned to the reference, nothing will happen. :param reference: The IfcLibraryReference to unassign from :type reference: ifcopenshell.entity_instance.entity_instance - :param product: A IfcProduct element to unassign from the reference - :type product: ifcopenshell.entity_instance.entity_instance + :param products: A list of IfcProduct elements to unassign from the reference + :type products: list[ifcopenshell.entity_instance.entity_instance] :return: None :rtype: None @@ -52,24 +58,33 @@ class Usecase: ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu]) # Let's change our mind and unassign it. - ifcopenshell.api.run("library.unassign_reference", model, reference=reference, product=ahu) + ifcopenshell.api.run("library.unassign_reference", model, reference=reference, products=[ahu]) """ self.file = file - self.settings = {"reference": reference, "product": product} + self.settings = {"reference": reference, "products": products} def execute(self): - rels = self.settings["reference"].LibraryRefForObjects - if not rels: - return - for rel in rels: - if self.settings["product"] in rel.RelatedObjects: - if len(rel.RelatedObjects) == 1: - history = rel.OwnerHistory - self.file.remove(rel) - if history: - ifcopenshell.util.element.remove_deep2(self.file, history) - continue - related_objects = list(rel.RelatedObjects) - related_objects.remove(self.settings["product"]) - rel.RelatedObjects = related_objects + # TODO: do we need to support non-ifcroot elements like we do in classification.add_reference? + + reference_rels: set[ifcopenshell.entity_instance] = set() + products = set(self.settings["products"]) + for product in products: + reference_rels.update(product.HasAssociations) + + reference_rels = { + rel + for rel in reference_rels + if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == self.settings["reference"] + } + + for rel in reference_rels: + related_objects = set(rel.RelatedObjects) - products + if related_objects: + rel.RelatedObjects = list(related_objects) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + else: + history = rel.OwnerHistory + self.file.remove(rel) + if history: + ifcopenshell.util.element.remove_deep2(self.file, history) diff --git a/src/ifcopenshell-python/test/api/library/test_unassign_reference.py b/src/ifcopenshell-python/test/api/library/test_unassign_reference.py index 598022cbab..8962bb686a 100644 --- a/src/ifcopenshell-python/test/api/library/test_unassign_reference.py +++ b/src/ifcopenshell-python/test/api/library/test_unassign_reference.py @@ -18,12 +18,20 @@ import test.bootstrap import ifcopenshell.api +import ifcopenshell.util.element class TestUnassignReference(test.bootstrap.IFC4): def test_unassigning_a_reference(self): reference = self.file.createIfcLibraryReference() - product = self.file.createIfcWall() - ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference) - ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference) + products = [self.file.createIfcWall() for i in range(3)] + ifcopenshell.api.run("library.assign_reference", self.file, products=products, reference=reference) + ifcopenshell.api.run("library.unassign_reference", self.file, products=products[:1], reference=reference) + assert ifcopenshell.util.element.get_referenced_elements(reference) == set(products[1:]) + + ifcopenshell.api.run("library.unassign_reference", self.file, products=products[1:], reference=reference) + assert ifcopenshell.util.element.get_referenced_elements(reference) == set() assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0 + +class TestUnassignReferenceIFC2X3(test.bootstrap.IFC2X3, TestUnassignReference): + pass diff --git a/src/ifcopenshell-python/test/api/test_api.py b/src/ifcopenshell-python/test/api/test_api.py index ee20435b33..a652a0de0c 100644 --- a/src/ifcopenshell-python/test/api/test_api.py +++ b/src/ifcopenshell-python/test/api/test_api.py @@ -246,3 +246,11 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4): assert reference.LibraryRefForObjects[0].RelatedObjects == (product,) ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference) assert set(reference.LibraryRefForObjects[0].RelatedObjects) == set((product, product2)) + + @deprecation_check + def test_unassigning_a_reference(self): + reference = self.file.createIfcLibraryReference() + product = self.file.createIfcWall() + ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference) + ifcopenshell.api.run("library.unassign_reference", self.file, product=product, reference=reference) + assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0