diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index c768025826..068fe6d598 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -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, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 243fc1f049..efab5ff5f1 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -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() diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 6530781763..3d11601970 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -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) diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index c126582388..c0542b40c0 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py new file mode 100644 index 0000000000..057b7202fe --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_value_formula.py @@ -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) diff --git a/src/ifcopenshell-python/ifcopenshell/util/cost.py b/src/ifcopenshell-python/ifcopenshell/util/cost.py index acc57e9bdb..f73d5ecedf 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/cost.py +++ b/src/ifcopenshell-python/ifcopenshell/util/cost.py @@ -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"):