mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
library.assign_reference - support batching #4474
This commit is contained in:
@@ -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))
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user