Ifc5d can now parse cost category breakdowns in CSV form

This commit is contained in:
Dion Moult
2021-08-09 17:57:34 +10:00
parent d4fc135209
commit efec360a00
3 changed files with 77 additions and 39 deletions
+56 -24
View File
@@ -1,4 +1,3 @@
# Ifc5D - IFC costing utility
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -36,11 +35,21 @@ class Csv2Ifc:
def parse_csv(self):
self.parents = {}
self.headers = {}
with open(self.csv, "r") as csv_file:
reader = csv.reader(csv_file)
for row in reader:
if not row[0]:
continue
if row[0] == "Hierarchy":
self.has_categories = True
for i, col in enumerate(row):
if not col:
continue
if col == "Value":
self.has_categories = False
self.headers[col] = i
continue
cost_data = self.get_row_cost_data(row)
hierarchy_key = int(row[0])
if hierarchy_key == 1:
@@ -50,11 +59,23 @@ class Csv2Ifc:
self.parents[hierarchy_key] = cost_data
def get_row_cost_data(self, row):
name = row[self.headers["Name"]]
cost_quantities = row[self.headers["Quantity"]]
cost_quantities_unit = row[self.headers["Unit"]]
if self.has_categories:
cost_values = {
k: float(row[v])
for k, v in self.headers.items()
if k not in ["Hierarchy", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
}
else:
cost_values = row[self.headers["Value"]]
cost_values = float(cost_values) if cost_values else None
return {
"Name": str(row[1]) if row[1] else None,
"CostQuantities": float(row[2]) if row[2] else None,
"CostQuantitiesUnit": str(row[3]) if row[3] else None,
"CostValues": float(row[4]) if row[4] else None,
"Name": str(name) if name else None,
"CostQuantities": float(cost_quantities) if cost_quantities else None,
"CostQuantitiesUnit": str(cost_quantities_unit) if cost_quantities_unit else None,
"CostValues": cost_values,
"children": [],
}
@@ -67,26 +88,37 @@ class Csv2Ifc:
def create_cost_items(self, cost_items, parent=None):
for cost_item in cost_items:
if parent is None:
cost_item["ifc"] = ifcopenshell.api.run(
"cost.add_cost_item", self.file, cost_schedule=self.cost_schedule
)
else:
cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent)
cost_item["ifc"].Name = cost_item["Name"]
if cost_item["CostValues"]:
self.create_cost_item(cost_item, parent)
def create_cost_item(self, cost_item, parent):
if parent is None:
cost_item["ifc"] = ifcopenshell.api.run(
"cost.add_cost_item", self.file, cost_schedule=self.cost_schedule
)
else:
cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent)
cost_item["ifc"].Name = cost_item["Name"]
if not cost_item["CostValues"]:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.Category = "*"
elif self.has_categories:
for category, value in cost_item["CostValues"].items():
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcReal(cost_item["CostValues"])
else:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.Category = "*"
if cost_item["CostQuantities"]:
quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["CostQuantitiesUnit"])
quantity = ifcopenshell.api.run(
"cost.add_cost_item_quantity", self.file, cost_item=cost_item["ifc"], ifc_class=quantity_class
)
quantity[3] = cost_item["CostQuantities"]
self.create_cost_items(cost_item["children"], cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcReal(value)
cost_value.Category = category
else:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcReal(cost_item["CostValues"])
if cost_item["CostQuantities"]:
quantity_class = ifcopenshell.util.unit.get_symbol_quantity_class(cost_item["CostQuantitiesUnit"])
quantity = ifcopenshell.api.run(
"cost.add_cost_item_quantity", self.file, cost_item=cost_item["ifc"], ifc_class=quantity_class
)
quantity[3] = cost_item["CostQuantities"]
self.create_cost_items(cost_item["children"], cost_item["ifc"])
def create_boilerplate_ifc(self):
self.file = ifcopenshell.file(schema="IFC4")
+14 -10
View File
@@ -1,10 +1,14 @@
1,"Demolition",,,
2,"Building A",,,
3,"Soft strip",1,"m",5
3,"Hard Strip",2,"m2",6
3,"Hazmat",3,"m3",7
,,,,
2,"Building B",,,
3,"Soft strip",4,"hr",8
3,"Hard Strip",5,,9
3,"Hazmat",6,"kg",10
"Hierarchy","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal"
1,"Demolition",,,,,,301
,,,,,,,
2,"Building A",,,,,,96
,,,,,,,
3,"Soft strip",1,"m",5,4,3,12
3,"Hard Strip",2,"m2",6,5,4,30
3,"Hazmat",3,"m3",7,6,5,54
,,,,,,,
2,"Building B",,,,,,205
,,,,,,,
3,"Soft strip",4,"hr",8,7,,60
3,"Hard Strip",5,,9,,8,85
3,"Hazmat",6,"kg",10,,,60
1 1 Hierarchy Demolition Name Quantity Unit Foo Bar Baz Subtotal
2 2 1 Building A Demolition 301
3 3 Soft strip 1 m 5
4 3 2 Hard Strip Building A 2 m2 6 96
5 3 Hazmat 3 m3 7
6 3 Soft strip 1 m 5 4 3 12
7 2 3 Building B Hard Strip 2 m2 6 5 4 30
8 3 3 Soft strip Hazmat 4 3 hr m3 8 7 6 5 54
9 3 Hard Strip 5 9
10 3 2 Hazmat Building B 6 kg 10 205
11
12 3 Soft strip 4 hr 8 7 60
13 3 Hard Strip 5 9 8 85
14 3 Hazmat 6 kg 10 60
@@ -198,17 +198,19 @@ def get_property_unit(prop, ifc_file):
def get_symbol_quantity_class(symbol):
# Dumb, but everybody gets it, unlike regex golf
if not symbol:
return "IfcQuantityCount"
elif symbol[-1:] == "g":
symbol = symbol.lower()
if symbol in ["kg", "g", "mt", "kt", "t"]:
return "IfcQuantityWeight"
elif symbol[-1:] == "s" or symbol == "hr":
elif symbol in ["day", "d", "hour", "hr", "h", "minute", "min", "m", "second", "sec", "s"]:
return "IfcQuantityTime"
elif symbol[-1:] == "3":
elif symbol in ["km3", "m3", "cm3", "mm3", "cy", "cft", "cin"]:
return "IfcQuantityVolume"
elif symbol[-1:] == "2":
elif symbol in ["km2", "m2", "cm2", "mm2", "sqy", "sqft", "sqin"]:
return "IfcQuantityArea"
elif symbol[-1:] == "m" or symbol in ["in", "ft", "yd"]:
elif symbol in ["km", "m", "cm", "mm", "ly", "lf", "lin", "yd", "ft", "in"]:
return "IfcQuantityLength"
return "IfcQuantityCount"