This commit is contained in:
Andrej730
2025-02-18 15:18:23 +05:00
parent 83a351b1c7
commit 17642ca4e9
117 changed files with 683 additions and 1005 deletions
@@ -25,8 +25,6 @@ from typing import Union
def copy_cost_item(
file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
# TODO: currently it never returns list of duplicated cost items
# though it is stated in the docs
"""Copies all cost items and related relationships
The following relationships are also duplicated:
@@ -36,9 +34,7 @@ def copy_cost_item(
* The copy will have duplicated nested cost items
:param cost_item: The cost item to be duplicated
:type cost_item: ifcopenshell.entity_instance
:return: The duplicated cost item or the list of duplicated cost items if the latter has children
:rtype: ifcopenshell.entity_instance or list[ifcopenshell.entity_instance]
Example:
.. code:: python
@@ -53,22 +49,29 @@ def copy_cost_item(
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"cost_item": cost_item}
return usecase.execute()
return usecase.execute(cost_item)
class Usecase:
def execute(self):
self.new_cost_items = []
return self.duplicate_cost_item(self.settings["cost_item"])
file: ifcopenshell.file
new_cost_items: list[ifcopenshell.entity_instance]
def duplicate_cost_item(self, cost_item):
def execute(
self, cost_item: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
self.new_cost_items = []
self.duplicate_cost_item(cost_item)
return self.new_cost_items[0] if len(self.new_cost_items) == 1 else self.new_cost_items
def duplicate_cost_item(self, cost_item: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
new_cost_item = ifcopenshell.util.element.copy_deep(self.file, cost_item)
self.new_cost_items.append(new_cost_item)
self.copy_indirect_attributes(cost_item, new_cost_item)
return new_cost_item
def copy_indirect_attributes(self, from_element, to_element):
def copy_indirect_attributes(
self, from_element: ifcopenshell.entity_instance, to_element: ifcopenshell.entity_instance
) -> None:
for inverse in self.file.get_inverse(from_element):
if inverse.is_a("IfcRelDefinesByProperties"):
inverse = ifcopenshell.util.element.copy(self.file, inverse)
@@ -28,11 +28,8 @@ def edit_cost_item(
IfcCostItem, consult the IFC documentation.
:param cost_item: The IfcCostItem entity you want to edit
:type cost_item: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict
:return: None
:rtype: None
Example:
@@ -42,7 +39,5 @@ def edit_cost_item(
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
ifcopenshell.api.cost.edit_cost_item(model, cost_item=item, attributes={"Name": "Foo"})
"""
settings = {"cost_item": cost_item, "attributes": attributes or {}}
for name, value in settings["attributes"].items():
setattr(settings["cost_item"], name, value)
for name, value in attributes.items():
setattr(cost_item, name, value)
@@ -28,11 +28,8 @@ def edit_cost_item_quantity(
IfcPhysicalQuantity, consult the IFC documentation.
:param physical_quantity: The IfcPhysicalQuantity entity you want to edit
:type physical_quantity: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict
:return: None
:rtype: None
Example:
@@ -50,7 +47,5 @@ def edit_cost_item_quantity(
ifcopenshell.api.cost.edit_cost_item_quantity(model,
physical_quantity=quantity, "attributes": {"VolumeValue": 3.0})
"""
settings = {"physical_quantity": physical_quantity, "attributes": attributes or {}}
for name, value in settings["attributes"].items():
setattr(settings["physical_quantity"], name, value)
for name, value in attributes.items():
setattr(physical_quantity, name, value)
@@ -28,11 +28,8 @@ def edit_cost_schedule(
IfcCostSchedule, consult the IFC documentation.
:param cost_schedule: The IfcCostSchedule entity you want to edit
:type cost_schedule: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict
:return: None
:rtype: None
Example:
@@ -42,8 +39,5 @@ def edit_cost_schedule(
ifcopenshell.api.cost.edit_cost_schedule(model,
cost_schedule=schedule, attributes={"Name": "Foo"})
"""
settings = {"cost_schedule": cost_schedule, "attributes": attributes or {}}
for name, value in settings["attributes"].items():
setattr(settings["cost_schedule"], name, value)
for name, value in attributes.items():
setattr(cost_schedule, name, value)
@@ -31,11 +31,8 @@ def edit_cost_value(
IfcCostValue, consult the IFC documentation.
:param cost_value: The IfcCostValue entity you want to edit
:type cost_value: ifcopenshell.entity_instance
:param attributes: a dictionary of attribute names and values.
:type attributes: dict
:return: None
:rtype: None
Example:
@@ -49,14 +46,12 @@ def edit_cost_value(
ifcopenshell.api.cost.edit_cost_value(model, cost_value=value,
attributes={"AppliedValue": 42.0})
"""
settings = {"cost_value": cost_value, "attributes": attributes or {}}
for name, value in settings["attributes"].items():
for name, value in attributes.items():
if name == "AppliedValue" and value is not None:
# TODO: support all applied value select types
value = file.createIfcMonetaryMeasure(value)
elif name == "UnitBasis":
old_unit_basis = settings["cost_value"].UnitBasis
old_unit_basis = cost_value.UnitBasis
if value:
value_component = file.create_entity(
ifcopenshell.util.unit.get_unit_measure_class(value["UnitComponent"].UnitType),
@@ -65,4 +60,4 @@ def edit_cost_value(
value = file.create_entity("IfcMeasureWithUnit", value_component, value["UnitComponent"])
if old_unit_basis and len(file.get_inverse(old_unit_basis)) == 0:
ifcopenshell.util.element.remove_deep(file, old_unit_basis)
setattr(settings["cost_value"], name, value)
setattr(cost_value, name, value)
@@ -30,12 +30,9 @@ def unassign_cost_item_quantity(
have any impact on the cost item.
:param cost_item: The IfcCostItem to remove quantities from
:type cost_item: ifcopenshell.entity_instance
:param products: A list of IfcProducts that may have parametrically
connected quantities to the cost item
:type products: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
Example:
@@ -69,38 +66,39 @@ def unassign_cost_item_quantity(
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"cost_item": cost_item, "products": products or []}
return usecase.execute()
return usecase.execute(cost_item, products or [])
class Usecase:
def execute(self):
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for quantity in self.settings["cost_item"].CostQuantities or []:
file: ifcopenshell.file
def execute(self, cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]) -> None:
quantities = set(cost_item.CostQuantities or [])
for quantity in cost_item.CostQuantities or []:
for inverse in self.file.get_inverse(quantity):
if not inverse.is_a("IfcElementQuantity"):
continue
for rel in inverse.DefinesOccurrence or []:
for related_object in rel.RelatedObjects:
if related_object in self.settings["products"]:
self.quantities.remove(quantity)
self.settings["cost_item"].CostQuantities = list(self.quantities)
for product in self.settings["products"]:
if related_object in products:
quantities.remove(quantity)
cost_item.CostQuantities = list(quantities)
for product in products:
ifcopenshell.api.control.unassign_control(
self.file,
related_object=product,
relating_control=self.settings["cost_item"],
relating_control=cost_item,
)
self.update_cost_item_count()
self.update_cost_item_count(cost_item)
def update_cost_item_count(self):
def update_cost_item_count(self, cost_item: ifcopenshell.entity_instance) -> None:
# This is a bold assumption
# https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
if len(self.settings["cost_item"].CostQuantities) == 1:
quantity = self.settings["cost_item"].CostQuantities[0]
if len(cost_item.CostQuantities) == 1:
quantity = cost_item.CostQuantities[0]
if quantity.is_a("IfcQuantityCount"):
count = 0
for rel in self.settings["cost_item"].Controls:
for rel in cost_item.Controls:
count += len(rel.RelatedObjects)
if count:
quantity[3] = count