- Improve ODS export

- Fix XLSX export bug
- Improve active cost item UI active cost item
- Fix missing tool cost function
This commit is contained in:
Sigma Dimensions
2023-02-16 20:15:54 +01:00
parent 937fa21cce
commit ba24f9b491
4 changed files with 68 additions and 43 deletions
@@ -175,7 +175,7 @@ class BIM_PT_cost_schedules(Panel):
row.prop(self.props, "cost_types", text="")
if self.props.cost_types == "CATEGORY":
row.prop(self.props, "cost_category", text="")
op = row.operator("bim.add_cost_value", text="", icon="ADD")
op = row.operator("bim.add_cost_value", text="Add Value", icon="ADD")
op.parent = self.props.active_cost_item_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
@@ -511,6 +511,8 @@ class BIM_UL_cost_items_trait:
self.draw_quantity_column(split2, cost_item)
self.draw_value_column(split2, cost_item)
if self.props.active_cost_item_id == item.ifc_definition_id:
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
for column in self.props.columns:
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
+6 -2
View File
@@ -314,6 +314,10 @@ class Cost(blenderbim.core.tool.Cost):
)
blenderbim.bim.helper.import_attributes2(cost_value, props.cost_value_attributes, callback=callback)
@classmethod
def calculate_applied_value(cls, cost_item, cost_value):
return ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value)
@classmethod
def is_active_schedule_of_rates(cls):
return (
@@ -336,7 +340,7 @@ class Cost(blenderbim.core.tool.Cost):
@classmethod
def load_cost_item_value_formula_attributes(cls, cost_value=None):
props = bpy.context.scene.BIMCostProperties
props.cost_value_attributes.clear() # why is this necessary?
props.cost_value_attributes.clear()
bpy.context.scene.BIMCostProperties.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value)
@classmethod
@@ -517,7 +521,7 @@ class Cost(blenderbim.core.tool.Cost):
elif format == "XLSX":
from ifc5d.ifc5Dspreadsheet import Ifc5DXlsxWriter
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path + ".xlsx")
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path)
writer.write()
@classmethod
+8 -4
View File
@@ -25,6 +25,8 @@ Planned (would you like to contribute? Please reach out!):
## Usage IFC to CSV, ODS, XSLS
#TODO
### CLI app for converting IFC files to CSV, ODS or XLSX format.
Usage:
@@ -39,12 +41,14 @@ Options:
-f, --format format_type (str): The output format to export in (csv/ods/xlsx). Default is csv.
Examples:
python ifc5Dspreadsheet.py "C:\Users\Username\Desktop\test_cost.ifc" rev_01_schedule -l error.log -f ods
python ifc5Dspreadsheet.py "C:\Users\Dev-Machine\Desktop\test_cost.ifc" "C:\Users\Dev-Machine\Desktop" -l error.log -f csv
python ifc5Dspreadsheet.py "C:\Users\Dev-Machine\Desktop\test_cost.ifc" rev_01_schedule -l error.log -f ODS
python ifc5Dspreadsheet.py "C:\Users\Username\Desktop\test_cost.ifc" "C:\Users\Username\Desktop" -l error.log -f CSV
### Scripting:
Example for ODS exports:
"""
```
import ifcopenshell
from ifc5d.ifc5Dspreadsheet import Ifc5DOdsWriter
@@ -54,4 +58,4 @@ path = "directory/cost_schedule"
writer = Ifc5DOdsWriter(file=file, output=path)
writer.write()
"""
```
+51 -36
View File
@@ -25,6 +25,7 @@ import logging
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.cost
import ifcopenshell.util.date
class IfcDataGetter:
@@ -34,6 +35,12 @@ class IfcDataGetter:
return None
return file.by_type("IfcCostSchedule")
@staticmethod
def canonicalise_time(time):
if not time:
return "-"
return time.strftime("%d/%m/%y")
@staticmethod
def get_root_costs(cost_schedule):
return [obj for rel in cost_schedule.Controls or [] for obj in rel.RelatedObjects or []]
@@ -78,7 +85,6 @@ class IfcDataGetter:
categories = set()
for cost_item in IfcDataGetter.get_root_costs(schedule):
IfcDataGetter.process_cost_item_categories(cost_item, categories)
print("COST CATEGORIES", categories)
return categories
@staticmethod
@@ -88,7 +94,7 @@ class IfcDataGetter:
quantity_data = IfcDataGetter.get_cost_item_quantity(file, cost_item)
cost_values_data = IfcDataGetter.get_cost_item_values(cost_item)
children = [obj for rel in cost_item.IsNestedBy or [] for obj in rel.RelatedObjects or []]
data = {
"Index": index,
"Hierarchy": hierarchy,
@@ -97,15 +103,13 @@ class IfcDataGetter:
"Description": cost_item.Name,
"Unit": cost_values_data[0]["unit"] if cost_values_data else "",
"Quantity": quantity_data["quantity"]["total_quantity"],
# "Total": str(quantity_data["total_quantity"] * cost_values_data[0]["applied_value"] if cost_values_data else 0),
# "Children": listToString(children),
"ChildrenData": [],
}
for cost_value in cost_values_data or []:
cost_category = "{}{}".format(cost_value["category"], " Cost")
data[cost_category] = cost_value["applied_value"]
if data.get("* Cost", None):
data["Total"] = data["* Cost"]
data["Total Price"] = data["* Cost"]
data["* Cost"] = ""
rate_subtotal = 0
for key, value in data.items():
@@ -182,9 +186,7 @@ class IfcDataGetter:
for quantity in cost_item.CostQuantities or []:
if not quantity in accounted_for:
total_cost_quantity += add_quantity(quantity, take_off_name)
print(cost_item.Name, quantity[0], quantity[3], "was not accounted for parametrically")
print("Total Cost Quantity Including Non Parametric", total_cost_quantity)
return {
"id": cost_item.id(),
"name": cost_item.Name,
@@ -215,6 +217,7 @@ class Ifc5Dwriter:
def parse(self):
self.column_indexes = []
self.used_names = []
for i in range(26):
self.column_indexes.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ"[i % 26])
for cost_schedule in self.cost_schedules:
@@ -230,11 +233,19 @@ class Ifc5Dwriter:
"Unit",
]
cost_rate_categories = IfcDataGetter.get_cost_rates_categories(cost_schedule)
self.sheet_data[sheet_id]["headers"].extend(list(cost_rate_categories))
self.sheet_data[sheet_id]["headers"].extend(["General Cost", "Rate Subtotal", "Total", "Children"])
self.sheet_data[sheet_id]["headers"].extend(list(cost_rate_categories)).extend(
["Rate Subtotal", "Total Price", "Children"]
)
self.sheet_data[sheet_id]["cost_items"] = IfcDataGetter.get_cost_items_data(self.file, cost_schedule)
print(self.sheet_data)
self.sheet_data[sheet_id]["UpdateDate"] = IfcDataGetter.canonicalise_time(
ifcopenshell.util.date.ifc2datetime(cost_schedule.UpdateDate)
)
self.sheet_data[sheet_id]["PredefinedType"] = cost_schedule.PredefinedType
schedule_name = cost_schedule.Name or "Unnamed"
if schedule_name in self.used_names:
schedule_name = "{}_{}".format(schedule_name, self.used_names.count(schedule_name))
self.sheet_data[sheet_id]["Name"] = schedule_name
self.used_names.append(schedule_name)
def multiply_cells(self, cell1, cell2):
return "={}*{}".format(cell1, cell2)
@@ -265,15 +276,13 @@ class Ifc5DCsvWriter(Ifc5Dwriter):
super().write()
for sheet, data in self.sheet_data.items():
print("-------------------------------------")
print(sheet, data)
print("-------------------------------------")
with open(os.path.join(self.output, "{}.csv".format(sheet)), "w", newline="", encoding="utf-8") as file:
with open(
os.path.join(self.output, "{}.csv".format(data["Name"])), "w", newline="", encoding="utf-8"
) as file:
writer = csv.writer(file)
writer.writerow(data["headers"])
row = []
for cost_item_data in data["cost_items"]:
print(cost_item_data)
writer.writerow([cost_item_data.get(column, "") for column in data["headers"]])
@@ -332,7 +341,7 @@ class Ifc5DOdsWriter(Ifc5Dwriter):
self.row_count += 1
for i, column in enumerate(self.sheet_data[cost_schedule.id()]["headers"]):
if column == "Total" and cost_data["Quantity"] != 0 and cost_data["Rate Subtotal"]:
if column == "Total Price" and cost_data["Quantity"] != 0 and cost_data["Rate Subtotal"]:
cell_quantity = self.get_cell_position(cost_schedule.id(), "Quantity")
cell_subtotal_rate = self.get_cell_position(cost_schedule.id(), "Rate Subtotal")
value = self.multiply_cells(cell_quantity, cell_subtotal_rate)
@@ -356,52 +365,58 @@ class Ifc5DOdsWriter(Ifc5Dwriter):
row.addElement(cell)
table.addElement(row)
table = Table(name=cost_schedule.Name)
table = Table(name=self.sheet_data[cost_schedule.id()].get("Name", ""))
new = row()
add_cell(type="text", value="Predefined Type:", row=new, style="fed8b1")
add_cell(
type="text", value=self.sheet_data[cost_schedule.id()].get("PredefinedType", ""), row=new, style="fed8b1"
)
table.addElement(new)
new = row()
add_cell(type="text", value="Cost Schedule:", row=new, style="fed8b1")
add_cell(type="text", value=cost_schedule.Name or "Unnamed Cost Schedule", row=new, style="fed8b1")
add_cell(type="text", value=self.sheet_data[cost_schedule.id()]["Name"], row=new, style="fed8b1")
table.addElement(new)
new = row()
add_cell(type="text", value="Update Date:", row=new, style="fed8b1")
add_cell(type="text", value=self.sheet_data[cost_schedule.id()].get("UpdateDate", ""), row=new, style="fed8b1")
table.addElement(new)
new = row()
table.addElement(new)
header_row = TableRow()
print("currently data is ", self.sheet_data)
for header in self.sheet_data[cost_schedule.id()]["headers"]:
add_cell(type="text", value=header, row=header_row, style="fed8b1")
table.addElement(header_row)
new = row()
new2 = row()
table.addElement(new)
table.addElement(new2)
self.row_count = 4
self.row_count = 5
for cost_item_data in self.sheet_data[cost_schedule.id()]["cost_items"]:
add_cost_item_rows(table, cost_item_data)
self.doc.spreadsheet.addElement(table)
class Ifc5DXlsxWriter(Ifc5Dwriter):
def write(self):
import xlsxwriter
super().write()
self.workbook = xlsxwriter.Workbook(self.output)
self.workbook = xlsxwriter.Workbook(self.output + ".xlsx")
self.used_names = []
for cost_schedule in self.cost_schedules:
self.write_table(cost_schedule)
self.workbook.close()
def write_table(self, cost_schedule):
schedule_name = cost_schedule.Name or "Unnamed Cost Schedule"
if schedule_name in self.used_names:
schedule_name = "{}_{}".format(schedule_name, self.used_names.count(schedule_name))
worksheet = self.workbook.add_worksheet(schedule_name)
self.used_names.append(schedule_name)
def write_table(self, cost_schedule):
worksheet = self.workbook.add_worksheet(self.sheet_data[cost_schedule.id()]["Name"])
headers = self.sheet_data[cost_schedule.id()]["headers"]
for i, header in enumerate(headers):
worksheet.write(0, i, header)
row = 1
for cost_item_data in self.sheet_data[cost_schedule.id()]["cost_items"]:
col = 0
@@ -417,7 +432,7 @@ if __name__ == "__main__":
parser.add_argument("output", help="The output directory for CSV or filename for other formats")
parser.add_argument("-l", "--log", type=str, help="Specify where errors should be logged", default="process.log")
parser.add_argument(
"-f", "--format", type=str, help="Choose which format to export in (csv/ods/xlsx)", default="csv"
"-f", "--format", type=str, help="Choose which format to export in (CSV/ODS/XLSX)", default="CSV"
)
args = vars(parser.parse_args())