library.assign_reference - support batching #4474

This commit is contained in:
Andrej730
2024-04-15 16:00:40 +05:00
parent 03af1013a0
commit 81dcad6a14
12 changed files with 103 additions and 60 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ def assign_brick_reference(ifc, brick, element=None, library=None, brick_uri=Non
if not reference:
reference = ifc.run("library.add_reference", library=library)
ifc.run("library.edit_reference", reference=reference, attributes=brick.export_brick_attributes(brick_uri))
ifc.run("library.assign_reference", product=element, reference=reference)
ifc.run("library.assign_reference", products=[element], reference=reference)
project = brick.get_brickifc_project()
if not project:
project = brick.add_brickifc_project(brick.get_namespace(brick_uri))
+1 -1
View File
@@ -83,7 +83,7 @@ def edit_library_reference(ifc, library):
def assign_library_reference(ifc, obj=None, reference=None):
ifc.run("library.assign_reference", product=ifc.get_entity(obj), reference=reference)
ifc.run("library.assign_reference", products=[ifc.get_entity(obj)], reference=reference)
def unassign_library_reference(ifc, obj=None, reference=None):
+4 -4
View File
@@ -124,21 +124,21 @@ class TestAssignBrickReference:
ifc.run("library.add_reference", library="library").should_be_called().will_return("reference")
brick.export_brick_attributes("brick_uri").should_be_called().will_return("attributes")
ifc.run("library.edit_reference", reference="reference", attributes="attributes").should_be_called()
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
brick.get_brickifc_project().should_be_called().will_return("project")
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
def test_assigning_to_an_existing_reference(self, ifc, brick):
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
brick.get_brickifc_project().should_be_called().will_return("project")
brick.add_brickifc_reference("brick_uri", "element", "project").should_be_called()
subject.assign_brick_reference(ifc, brick, element="element", library="library", brick_uri="brick_uri")
def test_adding_a_brickifc_project_if_it_doesnt_exist(self, ifc, brick):
brick.get_library_brick_reference("library", "brick_uri").should_be_called().will_return("reference")
ifc.run("library.assign_reference", product="element", reference="reference").should_be_called()
ifc.run("library.assign_reference", products=["element"], reference="reference").should_be_called()
brick.get_brickifc_project().should_be_called().will_return(None)
brick.get_namespace("brick_uri").should_be_called().will_return("namespace")
brick.add_brickifc_project("namespace").should_be_called().will_return("project")
@@ -277,4 +277,4 @@ class TestSetBrickListRoot:
class TestRemoveBrickRelation:
def test_run(self, brick):
brick.remove_relation("brick_uri", "predicate", "object").should_be_called()
subject.remove_brick_relation(brick, brick_uri="brick_uri", predicate="predicate", object="object")
subject.remove_brick_relation(brick, brick_uri="brick_uri", predicate="predicate", object="object")
+1 -1
View File
@@ -114,7 +114,7 @@ class TestEditLibraryReference:
class TestAssignLibraryReference:
def test_run(self, ifc):
ifc.get_entity("obj").should_be_called().will_return("product")
ifc.run("library.assign_reference", product="product", reference="reference").should_be_called()
ifc.run("library.assign_reference", products=["product"], reference="reference").should_be_called()
subject.assign_library_reference(ifc, obj="obj", reference="reference")
@@ -93,6 +93,9 @@ ARGUMENTS_DEPRECATION = {
"classification.remove_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
"library.assign_reference": partial(
batching_argument_deprecation, prev_argument="product", new_argument="products"
),
}
@@ -17,23 +17,30 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.element
from typing import Union
class Usecase:
def __init__(self, file, product=None, reference=None):
"""Associates a product with a library reference
def __init__(
self, file: ifcopenshell.file, products: ifcopenshell.entity_instance, reference: ifcopenshell.entity_instance
):
"""Associates a list products with a library reference
A product may be associated with zero, one, or many references across
multiple libraries. See ifcopenshell.api.library.add_reference for more
detail about how references work.
:param product: The IfcProduct you want to associate with the reference
:type product: ifcopenshell.entity_instance.entity_instance
:param products: The list of IfcProducts you want to associate with the reference
:type products: list[ifcopenshell.entity_instance.entity_instance]
:param reference: The IfcLibraryReference you want the product to be
associated with.
:type reference: ifcopenshell.entity_instance.entity_instance
:return: The IfcRelAssociatesLibrary relationship entity
:rtype: ifcopenshell.entity_instance.entity_instance
or `None` if `products` was an empty list or all products were
already assigned to the `reference`.
:rtype: Union[ifcopenshell.entity_instance.entity_instance, None]
Example:
@@ -51,40 +58,46 @@ class Usecase:
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
ifcopenshell.api.run("library.assign_reference", model, reference=reference, products=[ahu])
"""
self.file = file
self.settings = {
"product": product,
"products": products,
"reference": reference,
}
def execute(self):
def execute(self) -> Union[ifcopenshell.entity_instance, None]:
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
referenced_elements = ifcopenshell.util.element.get_referenced_elements(self.settings["reference"])
products: set[ifcopenshell.entity_instance] = set(self.settings["products"])
products = products - referenced_elements
if not products:
return
if self.file.schema == "IFC2X3":
rels = self.get_ifc2x3_rels()
rel = next(
(
r
for r in self.file.by_type("IfcRelAssociatesLibrary")
if r.RelatingLibrary == self.settings["reference"]
),
None,
)
else:
rels = self.settings["reference"].LibraryRefForObjects
if not rels:
rel = next(iter(self.settings["reference"].LibraryRefForObjects), None)
if not rel:
return self.file.create_entity(
"IfcRelAssociatesLibrary",
GlobalId=ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
RelatedObjects=[self.settings["product"]],
RelatedObjects=list(products),
RelatingLibrary=self.settings["reference"],
)
for rel in rels:
if self.settings["product"] in rel.RelatedObjects:
return rel
rel = rels[0]
related_objects = list(rel.RelatedObjects)
related_objects.append(self.settings["product"])
rel.RelatedObjects = related_objects
related_objects = set(rel.RelatedObjects) | products
rel.RelatedObjects = list(related_objects)
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel)
return rel
def get_ifc2x3_rels(self):
return [
r for r in self.file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == self.settings["reference"]
]
@@ -49,7 +49,7 @@ class Usecase:
ifc_class="IfcUnitaryEquipment", predefined_type="AIRHANDLER")
# And now assign the IFC model's AHU with its Brickschema counterpart
ifcopenshell.api.run("library.assign_reference", model, reference=reference, product=ahu)
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)
@@ -20,39 +20,48 @@ import test.bootstrap
import ifcopenshell.api
def validate_ifc_file(ifc_file: ifcopenshell.file, use_json=True):
import ifcopenshell
import ifcopenshell.validate
if use_json:
logger = ifcopenshell.validate.json_logger()
else:
import logging
logger = logging.getLogger("validate")
logger.setLevel(logging.DEBUG)
ifcopenshell.validate.validate(ifc_file, logger, express_rules=True)
if use_json:
if logger.statements:
from pprint import pprint
pprint(logger.statements)
else:
print("IFC is completely valid.")
class TestAssignReference(test.bootstrap.IFC4):
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product, product2)
product3 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product2, product3], reference=reference)
assert set(rel.RelatedObjects) == set((product, product2, product3))
def test_not_assigning_twice(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
assert reference.LibraryRefForObjects[0].RelatedObjects == (product,)
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3):
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
ifcopenshell.api.run("library.assign_reference", self.file, product=product2, reference=reference)
assert rel.RelatedObjects == (product, product2)
def test_not_assigning_twice(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
rel = self.file.by_type("IfcRelAssociatesLibrary")[0]
assert rel.RelatedObjects == (product,)
class TestAssignReferenceIFC2X3(test.bootstrap.IFC2X3, TestAssignReference):
pass
@@ -24,7 +24,7 @@ class TestRemoveReference(test.bootstrap.IFC4):
def test_removing_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
ifcopenshell.api.run("library.assign_reference", self.file, products=[product], reference=reference)
ifcopenshell.api.run("library.remove_reference", self.file, reference=reference)
assert len(self.file.by_type("IfcLibraryReference")) == 0
assert len(self.file.by_type("IfcRelAssociatesLibrary")) == 0
@@ -24,6 +24,6 @@ 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, product=product, reference=reference)
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
@@ -236,3 +236,13 @@ class TestTemporarySupportForDeprecatedAPIArguments(test.bootstrap.IFC4):
ifcopenshell.api.run("classification.remove_reference", self.file, product=element, reference=reference)
assert len(ifcopenshell.util.classification.get_references(element)) == 0
assert len(self.file.by_type("IfcClassificationReference")) == 0
@deprecation_check
def test_assigning_a_reference(self):
reference = self.file.createIfcLibraryReference()
product = self.file.createIfcWall()
product2 = self.file.createIfcWall()
ifcopenshell.api.run("library.assign_reference", self.file, product=product, reference=reference)
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))
@@ -788,7 +788,6 @@ class TestGetNestIFC2X3(test.bootstrap.IFC2X3, TestGetNestIFC4):
class TestGetReferencedElements(test.bootstrap.IFC4):
# TODO: test other references:
# IfcDocumentReference
# IfcLibraryReference
# IfcExternallyDefinedHatchStyle
# IfcExternallyDefinedSurfaceStyle
# IfcExternallyDefinedTextFont
@@ -808,6 +807,15 @@ class TestGetReferencedElements(test.bootstrap.IFC4):
)
assert subject.get_referenced_elements(reference) == set(elements)
def test_get_elements_referenced_by_library_reference(self):
reference = self.file.createIfcLibraryReference()
elements = [
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall"),
]
ifcopenshell.api.run("library.assign_reference", self.file, reference=reference, products=elements)
assert subject.get_referenced_elements(reference) == set(elements)
class TestGetReferencedElementsIFC2X3(test.bootstrap.IFC2X3, TestGetReferencedElements):
pass