You can now edit nested arithmetic cost values by using spreadsheet-like formulas

This commit is contained in:
Dion Moult
2021-08-27 14:44:54 +10:00
parent 16b34f942a
commit b99c5036e6
6 changed files with 100 additions and 43 deletions
@@ -27,6 +27,7 @@ classes = (
operator.EditCostItem,
operator.EditCostItemQuantity,
operator.EditCostValue,
operator.EditCostValueFormula,
operator.EnableEditingCostSchedule,
operator.EnableEditingCostItems,
operator.EnableEditingCostItem,
@@ -34,6 +35,7 @@ classes = (
operator.EnableEditingCostItemQuantity,
operator.EnableEditingCostItemValues,
operator.EnableEditingCostItemValue,
operator.EnableEditingCostItemValueFormula,
operator.DisableEditingCostItem,
operator.DisableEditingCostSchedule,
operator.DisableEditingCostItemQuantity,
@@ -637,6 +637,7 @@ class EnableEditingCostItemValue(bpy.types.Operator):
self.props = context.scene.BIMCostProperties
self.props.cost_value_attributes.clear()
self.props.active_cost_item_value_id = self.cost_value
self.props.cost_value_editing_type = "ATTRIBUTES"
data = Data.cost_values[self.cost_value]
blenderbim.bim.helper.import_attributes(
@@ -706,12 +707,50 @@ class DisableEditingCostItemValue(bpy.types.Operator):
def execute(self, context):
props = context.scene.BIMCostProperties
props.active_cost_item_value_id = 0
props.cost_value_editing_type = ""
return {"FINISHED"}
class EnableEditingCostItemValueFormula(bpy.types.Operator):
bl_idname = "bim.enable_editing_cost_item_value_formula"
bl_label = "Enable Editing Cost Item Value Formula"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.cost_value_attributes.clear()
self.props.active_cost_item_value_id = self.cost_value
self.props.cost_value_editing_type = "FORMULA"
self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"]
return {"FINISHED"}
class EditCostValueFormula(bpy.types.Operator):
bl_idname = "bim.edit_cost_value_formula"
bl_label = "Edit Cost Value Formula"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_value_formula",
self.file,
**{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula},
)
Data.load(IfcStore.get_file())
bpy.ops.bim.disable_editing_cost_item_value()
return {"FINISHED"}
class EditCostValue(bpy.types.Operator):
bl_idname = "bim.edit_cost_value"
bl_label = "Edit Cost Item Value"
bl_label = "Edit Cost Value"
bl_options = {"REGISTER", "UNDO"}
cost_value: bpy.props.IntProperty()
@@ -245,7 +245,9 @@ class BIMCostProperties(PropertyGroup):
)
cost_category: StringProperty(name="Cost Category")
active_cost_item_value_id: IntProperty(name="Active Cost Item Value Id")
cost_value_editing_type: StringProperty(name="Cost Value Editing Type")
cost_value_attributes: CollectionProperty(name="Cost Value Attributes", type=Attribute)
cost_value_formula: StringProperty(name="Cost Value Formula")
cost_column: StringProperty(name="Cost Column")
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
columns: CollectionProperty(name="Columns", type=StrProperty)
+17 -40
View File
@@ -190,65 +190,42 @@ class BIM_PT_cost_schedules(Panel):
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
row = self.layout.row(align=True)
row.label(text=Data.cost_values[cost_value_id]["Formula"])
self.draw_readonly_cost_value_ui(row, cost_value_id)
if self.props.active_cost_item_value_id:
if self.props.cost_value_editing_type == "ATTRIBUTES":
box = self.layout.box()
self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_item_value_id])
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
# This UI is really poor. Delete and start again.
cost_value = Data.cost_values[cost_value_id]
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
if cost_value["Category"]:
cost_value_label += " ({})".format(cost_value["Category"])
layout.label(text="", icon="DISC")
if self.props.active_cost_item_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA":
layout.prop(self.props, "cost_value_formula", text="")
else:
cost_value_label = "{0:.2f}".format(cost_value["AppliedValue"])
cost_value_label += " = " + cost_value["Formula"]
layout.label(text=cost_value_label, icon="DISC")
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"])
def draw_readonly_component_cost_value_ui(self, layout, cost_value_id, parent_id, level=1):
self.draw_cost_value_operator_ui(layout, cost_value_id, parent_id)
cost_value = Data.cost_values[cost_value_id]
cost_value_label = ">" * level
cost_value_label += "{0:.2f}".format(cost_value["AppliedValue"])
if cost_value["Category"]:
cost_value_label += " ({})".format(cost_value["Category"])
layout.label(text=cost_value_label)
for component_id in cost_value["Components"] or []:
self.draw_readonly_component_cost_value_ui(layout, component_id, cost_value["id"], level + 1)
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
if self.props.active_cost_item_value_id and self.props.active_cost_item_value_id == cost_value_id:
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
if self.props.cost_value_editing_type == "ATTRIBUTES":
op = layout.operator("bim.edit_cost_value", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
elif self.props.cost_value_editing_type == "FORMULA":
op = layout.operator("bim.edit_cost_value_formula", text="", icon="CHECKMARK")
op.cost_value = cost_value_id
layout.operator("bim.disable_editing_cost_item_value", text="", icon="CANCEL")
elif self.props.active_cost_item_value_id:
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
else:
op = layout.operator("bim.enable_editing_cost_item_value_formula", text="", icon="CON_TRANSLIKE")
op.cost_value = cost_value_id
op = layout.operator("bim.enable_editing_cost_item_value", text="", icon="GREASEPENCIL")
op.cost_value = cost_value_id
op = layout.operator("bim.add_cost_value", text="", icon="ADD")
op.parent = cost_value_id
op.cost_type = self.props.cost_types
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
op = layout.operator("bim.remove_cost_item_value", text="", icon="X")
op.parent = parent_id
op.cost_value = cost_value_id
@@ -0,0 +1,34 @@
import ifcopenshell
import ifcopenshell.util.cost
import ifcopenshell.util.unit
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_value": None, "formula": {}}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
try:
data = ifcopenshell.util.cost.unserialise_cost_value(self.settings["formula"], self.settings["cost_value"])
except:
return
self.edit_cost_value(data)
def edit_cost_value(self, data, parent=None):
ifc = data.get("ifc", None)
if not ifc:
ifc = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent)
if "AppliedValue" in data:
if data["AppliedValue"]:
ifc.AppliedValue = self.file.createIfcMonetaryMeasure(data["AppliedValue"])
else:
ifc.AppliedValue = None
ifc.Category = data["Category"] if "Category" in data else None
ifc.ArithmeticOperator = data["ArithmeticOperator"] if "ArithmeticOperator" in data else None
if "Components" in data:
for component in data["Components"]:
self.edit_cost_value(component, ifc)
@@ -52,7 +52,7 @@ def unserialise_cost_value(formula, cost_value):
result["ifc"] = element
for i, component in enumerate(result.get("Components", [])):
if element.Components and i < len(element.Components):
map_element_to_formula(element.Components[i], result["Components"][i])
map_element_to_result(element.Components[i], result["Components"][i])
map_element_to_result(cost_value, result)
return result
@@ -116,11 +116,14 @@ class CostValueUnserialiser:
def get_operand(self, operand):
child = operand.children[0]
if child.data == "value":
return {"AppliedValue": self.get_value(child)}
value = self.get_value(child)
return {"AppliedValue": float(value) if value else None}
elif child.data == "category":
data = {}
category = self.get_category(child)
if category:
if category.lower() == "sum":
category = "*"
data["Category"] = category
formula = self.get_formula(operand.children[1])
if formula.get("Components"):