See #6570. Formula column for ifc5d import from csv

Now it's possible to specify the Formula column in the csv in order to calculate cost item quantities
This commit is contained in:
Massimo Fabbro
2026-01-05 08:58:50 +01:00
committed by Massimo Fabbro
parent 2c2d0f2434
commit 528964ca56
2 changed files with 35 additions and 6 deletions
+32 -2
View File
@@ -55,6 +55,9 @@ class CsvHeader(TypedDict):
RateSchedule: NotRequired[str]
RateID: NotRequired[str]
# Formula
Formula: NotRequired[str]
#QuantityClass: NotRequired[str]
# Currently we assume that if column is not part of the main header,
# then it is a cost value category. So here we list any additional column
@@ -91,6 +94,8 @@ class CostItem(TypedDict):
Property: Union[str, None]
Query: Union[str, None]
Formula: Union[str, None]
#QuantityClass: Union[str, None]
class Csv2Ifc:
# Inputs.
@@ -108,6 +113,7 @@ class Csv2Ifc:
categories: dict[str, int]
has_categories: bool
has_rates: bool
has_formula: bool
def __init__(
self,
@@ -163,9 +169,13 @@ class Csv2Ifc:
if not self.headers:
self.has_categories = True
self.has_rates = False
self.has_categories = False
self.has_formula = False
self.headers = {col: i for i, col in enumerate(row) if col}
if "RateSchedule" in self.headers and "RateID" in self.headers:
self.has_rates = True
if "Formula" in self.headers:
self.has_formula = True
if "Value" in self.headers:
self.has_categories = False
else:
@@ -233,6 +243,11 @@ class Csv2Ifc:
else:
cost_rate = None
if self.has_formula:
cost_formula = row[(self.headers["Formula"])] if "Formula" in self.headers else None
else:
cost_formula = None
return {
"Identification": str(identification) if identification else None,
"Name": str(name) if name else None,
@@ -244,6 +259,7 @@ class Csv2Ifc:
"Query": query,
"children": [],
"CostRate": cost_rate,
"Formula": cost_formula,
}
def create_ifc(self) -> None:
@@ -381,17 +397,31 @@ class Csv2Ifc:
# and some query in "Query" column.
# If query is provided it will override the defined value
# due current behaviour in cost.assign_cost_item_quantity.
if results:
if results and not cost_item["Formula"]:
ifcopenshell.api.cost.assign_cost_item_quantity(
self.file,
cost_item=cost_item["ifc"],
products=results,
prop_name=prop_name,
)
elif not quantity:
elif not quantity and not cost_item["Formula"]:
quantity = ifcopenshell.api.cost.add_cost_item_quantity(
self.file, cost_item=cost_item["ifc"], ifc_class=quantity_class
)
if cost_item["Formula"]:
results = ifcopenshell.util.selector.filter_elements(self.file, cost_item["Query"])
results = [r for r in results]
ifc_quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["Unit"])
try:
quantity = ifcopenshell.api.cost.assign_cost_item_quantity(
self.file,
cost_item=cost_item["ifc"],
products=results,
formula=cost_item["Formula"],
ifc_class=ifc_quantity_class,
)
except:
quantity=0
self.create_cost_items(cost_item["children"], cost_item["ifc"])
@@ -125,14 +125,14 @@ class Usecase:
settings: dict[str, Any]
def execute(self):
if self.settings["prop_name"]:
if self.settings["prop_name"] or self.settings["formula"]:
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for product in self.settings["products"]:
if product.is_a("IfcSpatialElement"):
continue
self.assign_cost_control(related_object=product, cost_item=self.settings["cost_item"])
if self.settings["formula"]:
# not so much elegant, but simple and useful because variables can't has the dot in the name
# not so much elegant, but simple and useful because variables can't have the dot in the name
# and Psets have the dot
separator = "0"
variables = self.extract_variables(self.settings["formula"])
@@ -153,9 +153,8 @@ class Usecase:
result = eval(formula_modified, {}, variables_modified)
new_quantity = None
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for quantity in self.quantities:
if quantity.Formula == self.settings["formula"]:
if quantity.Formula == self.settings["formula"] and len(self.settings["products"]) == 1: #Todo improve it
new_quantity = quantity
self.settings["ifc_class"] = quantity.is_a()
continue