See #1848. Refactor cost data class.

This commit is contained in:
Dion Moult
2023-01-30 20:53:02 +11:00
parent f9bff64f0d
commit ab6159f173
6 changed files with 966 additions and 447 deletions
@@ -0,0 +1,290 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
#
# This file is part of BlenderBIM Add-on.
#
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# BlenderBIM Add-on is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.util.cost
import ifcopenshell.util.element
import blenderbim.tool as tool
def refresh():
CostSchedulesData.is_loaded = False
CostItemRatesData.is_loaded = False
CostItemQuantitiesData.is_loaded = False
class CostSchedulesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"schedules": cls.schedules(),
"is_editing_rates": cls.is_editing_rates(),
"cost_items": cls.cost_items(),
"cost_quantities": cls.cost_quantities(),
"cost_values": cls.cost_values(),
"quantity_types": cls.quantity_types(),
}
cls.is_loaded = True
@classmethod
def schedules(cls):
results = []
if bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
schedule = tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
results.append(
{
"id": schedule.id(),
"name": schedule.Name or "Unnamed",
}
)
else:
for schedule in tool.Ifc.get().by_type("IfcCostSchedule"):
results.append(
{
"id": schedule.id(),
"name": schedule.Name or "Unnamed",
}
)
return results
@classmethod
def is_editing_rates(cls):
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_schedule_id
if not ifc_id:
return
return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES"
@classmethod
def cost_items(cls):
cls._cost_values = {}
results = {}
for cost_item in tool.Ifc.get().by_type("IfcCostItem"):
data = {}
cls._load_cost_item_quantities(cost_item, data)
cls._load_cost_values(cost_item, data)
results[cost_item.id()] = data
return results
@classmethod
def _load_cost_values(cls, root_element, data):
# data["CostValues"] = []
data["CategoryValues"] = {}
data["UnitBasisValueComponent"] = None
data["UnitBasisUnitSymbol"] = None
data["TotalAppliedValue"] = 0.0
data["TotalCost"] = 0.0
if root_element.is_a("IfcCostItem"):
values = root_element.CostValues
elif root_element.is_a("IfcConstructionResource"):
values = root_element.BaseCosts
for cost_value in values or []:
cls._load_cost_value(root_element, data, cost_value)
# data["CostValues"].append(cost_value.id())
data["TotalAppliedValue"] += cls._cost_values[cost_value.id()]["AppliedValue"]
if cost_value.UnitBasis:
cost_value_data = cls._cost_values[cost_value.id()]
data["UnitBasisValueComponent"] = cost_value_data["UnitBasis"]["ValueComponent"]
data["UnitBasisUnitSymbol"] = cost_value_data["UnitBasis"]["UnitSymbol"]
if data["UnitBasisValueComponent"]:
data["TotalCost"] = data["TotalCostQuantity"] / data["UnitBasisValueComponent"] * data["TotalAppliedValue"]
else:
data["TotalCost"] = data["TotalCostQuantity"] * data["TotalAppliedValue"]
@classmethod
def _load_cost_item_quantities(cls, cost_item, data):
parametric_quantities = []
for rel in cost_item.Controls:
for related_object in rel.RelatedObjects or []:
quantities = cls._get_object_quantities(cost_item, related_object)
# data["Controls"][related_object.id()] = quantities
parametric_quantities.extend(quantities)
# data["CostQuantities"] = []
data["TotalCostQuantity"] = ifcopenshell.util.cost.get_total_quantity(cost_item)
for quantity in cost_item.CostQuantities or []:
if quantity.id() in parametric_quantities:
continue
# quantity_data = quantity.get_info()
# del quantity_data["Unit"]
# cls.physical_quantities[quantity.id()] = quantity_data
# data["CostQuantities"].append(quantity.id())
# data["Unit"] = None
data["UnitSymbol"] = "?"
if cost_item.CostQuantities:
quantity = cost_item.CostQuantities[0]
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
if unit:
# data["Unit"] = unit.id()
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(unit)
else:
# data["Unit"] = None
data["UnitSymbol"] = None
@classmethod
def _get_object_quantities(cls, cost_item, element):
if not element.is_a("IfcObject"):
return []
results = []
for relationship in element.IsDefinedBy:
if not relationship.is_a("IfcRelDefinesByProperties"):
continue
qto = relationship.RelatingPropertyDefinition
if not qto.is_a("IfcElementQuantity"):
continue
for prop in qto.Quantities:
if prop in cost_item.CostQuantities or []:
results.append(prop.id())
return results
@classmethod
def _load_cost_value(cls, root_element, root_element_data, cost_value):
value_data = cost_value.get_info()
del value_data["AppliedValue"]
if value_data["UnitBasis"]:
data = cost_value.UnitBasis.get_info()
data["ValueComponent"] = data["ValueComponent"].wrappedValue
data["UnitComponent"] = data["UnitComponent"].id()
data["UnitSymbol"] = ifcopenshell.util.unit.get_unit_symbol(cost_value.UnitBasis.UnitComponent)
value_data["UnitBasis"] = data
if value_data["ApplicableDate"]:
value_data["ApplicableDate"] = ifcopenshell.util.date.ifc2datetime(value_data["ApplicableDate"])
if value_data["FixedUntilDate"]:
value_data["FixedUntilDate"] = ifcopenshell.util.date.ifc2datetime(value_data["FixedUntilDate"])
value_data["Components"] = [c.id() for c in value_data["Components"] or []]
value_data["AppliedValue"] = ifcopenshell.util.cost.calculate_applied_value(root_element, cost_value)
if cost_value.Category not in [None, "*"]:
root_element_data["CategoryValues"].setdefault(cost_value.Category, 0)
root_element_data["CategoryValues"][cost_value.Category] += value_data["AppliedValue"]
value_data["Formula"] = ifcopenshell.util.cost.serialise_cost_value(cost_value)
cls._cost_values[cost_value.id()] = value_data
for component in cost_value.Components or []:
cls._load_cost_value(root_element, root_element_data, component)
@classmethod
def cost_quantities(cls):
results = []
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
if not ifc_id:
return results
for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []:
results.append({"id": quantity.id(), "name": quantity.Name, "value": "{0:.2f}".format(quantity[3])})
return results
@classmethod
def cost_values(cls):
results = []
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id
if not ifc_id:
return results
cost_item = tool.Ifc.get().by_id(ifc_id)
for cost_value in cost_item.CostValues or []:
label = "{0:.2f}".format(ifcopenshell.util.cost.calculate_applied_value(cost_item, cost_value))
label += " = {}".format(ifcopenshell.util.cost.serialise_cost_value(cost_value))
results.append({"id": cost_value.id(), "label": label})
return results
@classmethod
def quantity_types(cls):
return [
(t.name(), t.name(), "")
for t in tool.Ifc.schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
]
class CostItemRatesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"schedule_of_rates": cls.schedule_of_rates(),
}
cls.is_loaded = True
@classmethod
def schedule_of_rates(cls):
return [
(str(s.id()), s.Name or "Unnamed", "")
for s in tool.Ifc.get().by_type("IfcCostSchedule")
if s.PredefinedType == "SCHEDULEOFRATES"
]
class CostItemQuantitiesData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.data = {
"product_quantity_names": cls.product_quantity_names(),
"process_quantity_names": cls.process_quantity_names(),
"resource_quantity_names": cls.resource_quantity_names(),
}
cls.is_loaded = True
@classmethod
def product_quantity_names(cls):
total_selected_objects = len(bpy.context.selected_objects)
names = set()
for obj in bpy.context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
potential_names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
potential_names.update(quantities.keys())
names = names.intersection(potential_names) if names else potential_names
return [(n, n, "") for n in names if n != "id"]
@classmethod
def process_quantity_names(cls):
active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index
total_tasks = len(bpy.context.scene.BIMTaskTreeProperties.tasks)
if not total_tasks or active_task_index >= total_tasks:
return []
ifc_definition_id = bpy.context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
element = tool.Ifc.get().by_id(ifc_definition_id)
names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
names = set(quantities.keys())
return [(n, n, "") for n in names if n != "id"]
@classmethod
def resource_quantity_names(cls):
active_resource_index = bpy.context.scene.BIMResourceProperties.active_resource_index
total_resources = len(bpy.context.scene.BIMResourceTreeProperties.resources)
if not total_resources or active_resource_index >= total_resources:
return []
ifc_definition_id = bpy.context.scene.BIMResourceTreeProperties.resources[active_resource_index].ifc_definition_id
element = tool.Ifc.get().by_id(ifc_definition_id)
names = set()
qtos = ifcopenshell.util.element.get_psets(element, qtos_only=True)
for qset, quantities in qtos.items():
names = set(quantities.keys())
return [(n, n, "") for n in names if n != "id"]
File diff suppressed because it is too large Load Diff
+19 -110
View File
@@ -18,10 +18,10 @@
import bpy
import ifcopenshell.api
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.classification.data import CostClassificationsData
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.pset.data import Data as PsetData
from blenderbim.bim.module.cost.data import CostSchedulesData, CostItemRatesData, CostItemQuantitiesData
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
@@ -36,44 +36,10 @@ from bpy.props import (
)
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def purge():
global quantitytypes_enum
global productquantitynames_enum
global productquantitynames_count
global processquantitynames_enum
global processquantitynames_id
global resourcequantitynames_enum
global resourcequantitynames_id
global scheduleofrates_enum
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def get_schedule_of_rates(self, context):
global scheduleofrates_enum
if len(scheduleofrates_enum) == 0:
scheduleofrates_enum.extend(
(str(ifc_definition_id), schedule["Name"] or "Unnamed", "")
for ifc_definition_id, schedule in Data.cost_schedules.items()
if schedule["PredefinedType"] == "SCHEDULEOFRATES"
)
return scheduleofrates_enum
if not CostItemRatesData.is_loaded:
CostItemRatesData.load()
return CostItemRatesData.data["schedule_of_rates"]
def update_schedule_of_rates(self, context):
@@ -81,87 +47,32 @@ def update_schedule_of_rates(self, context):
def get_quantity_types(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.extend(
[
(t.name(), t.name(), "")
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
]
)
return quantitytypes_enum
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
return CostSchedulesData.data["quantity_types"]
def get_product_quantity_names(self, context):
global productquantitynames_enum
global productquantitynames_count
ifc_file = IfcStore.get_file()
total_selected_objects = len(context.selected_objects)
if total_selected_objects != productquantitynames_count or total_selected_objects == 1:
productquantitynames_enum = []
productquantitynames_count = total_selected_objects
names = set()
for obj in context.selected_objects:
element_id = obj.BIMObjectProperties.ifc_definition_id
if not element_id:
continue
potential_names = set()
if element_id not in PsetData.products:
PsetData.load(ifc_file, element_id)
for qto_id in PsetData.products[element_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[potential_names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
names = names.intersection(potential_names) if names else potential_names
productquantitynames_enum.extend([(n, n, "") for n in names])
return productquantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["product_quantity_names"]
def get_process_quantity_names(self, context):
global processquantitynames_enum
global processquantitynames_id
ifc_file = IfcStore.get_file()
active_task_index = context.scene.BIMWorkScheduleProperties.active_task_index
total_tasks = len(context.scene.BIMTaskTreeProperties.tasks)
if not total_tasks or active_task_index >= total_tasks:
return []
ifc_definition_id = context.scene.BIMTaskTreeProperties.tasks[active_task_index].ifc_definition_id
if processquantitynames_id != ifc_definition_id:
processquantitynames_enum = []
processquantitynames_id = ifc_definition_id
names = set()
if ifc_definition_id not in PsetData.products:
PsetData.load(ifc_file, ifc_definition_id)
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
processquantitynames_enum.extend([(n, n, "") for n in names])
return processquantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["process_quantity_names"]
def get_resource_quantity_names(self, context):
global resourcequantitynames_enum
global resourcequantitynames_id
ifc_file = IfcStore.get_file()
active_resource_index = context.scene.BIMResourceProperties.active_resource_index
total_resources = len(context.scene.BIMResourceTreeProperties.resources)
if not total_resources or active_resource_index >= total_resources:
return []
ifc_definition_id = context.scene.BIMResourceTreeProperties.resources[active_resource_index].ifc_definition_id
if resourcequantitynames_id != ifc_definition_id:
resourcequantitynames_enum = []
resourcequantitynames_id = ifc_definition_id
names = set()
if ifc_definition_id not in PsetData.products:
PsetData.load(ifc_file, ifc_definition_id)
for qto_id in PsetData.products[ifc_definition_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
resourcequantitynames_enum.extend([(n, n, "") for n in names])
return resourcequantitynames_enum
if not CostItemQuantitiesData.is_loaded:
CostItemQuantitiesData.load()
return CostItemQuantitiesData.data["resource_quantity_names"]
def update_active_cost_item_index(self, context):
if Data.cost_schedules[self.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
schedule = tool.Ifc.get().by_id(self.active_cost_schedule_id)
if schedule.PredefinedType == "SCHEDULEOFRATES":
bpy.ops.bim.load_cost_item_types()
else:
bpy.ops.bim.load_cost_item_quantities()
@@ -178,7 +89,6 @@ def update_cost_item_identification(self, context):
self.file,
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
)
Data.load(self.file)
if props.active_cost_item_id == self.ifc_definition_id:
attribute = props.cost_item_attributes.get("Identification")
attribute.string_value = self.identification
@@ -194,7 +104,6 @@ def update_cost_item_name(self, context):
self.file,
**{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
)
Data.load(IfcStore.get_file())
if props.active_cost_item_id == self.ifc_definition_id:
attribute = props.cost_item_attributes.get("Name")
attribute.string_value = self.name
+47 -69
View File
@@ -20,8 +20,7 @@ import blenderbim.bim.helper
import blenderbim.bim.module.cost.prop as CostProp
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.unit.data import Data as UnitData
from blenderbim.bim.module.cost.data import CostSchedulesData
class BIM_PT_cost_schedules(Panel):
@@ -39,54 +38,46 @@ class BIM_PT_cost_schedules(Panel):
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
def draw(self, context):
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
self.props = context.scene.BIMCostProperties
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
row = self.layout.row()
row.operator("bim.add_cost_schedule", icon="ADD")
if self.props.active_cost_schedule_id:
self.draw_cost_schedule_ui(
self.props.active_cost_schedule_id, Data.cost_schedules[self.props.active_cost_schedule_id]
)
else:
for cost_schedule_id, cost_schedule in Data.cost_schedules.items():
self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule)
for schedule in CostSchedulesData.data["schedules"]:
self.draw_cost_schedule_ui(schedule)
def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule):
def draw_cost_schedule_ui(self, cost_schedule):
row = self.layout.row(align=True)
row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
row.label(text=cost_schedule["name"], icon="LINENUMBERS_ON")
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id:
if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule["id"]:
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="")
op.cost_schedule = cost_schedule_id
op.cost_schedule = cost_schedule["id"]
row.prop(self.props, "should_show_column_ui", text="", icon="SHORTDISPLAY")
if self.props.is_editing == "COST_SCHEDULE":
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
elif self.props.is_editing == "COST_ITEMS":
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id
row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule["id"]
row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL")
elif self.props.active_cost_schedule_id:
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
else:
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule_id
row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule["id"]
row.operator(
"bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL"
).cost_schedule = cost_schedule_id
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id
).cost_schedule = cost_schedule["id"]
row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule["id"]
if self.props.active_cost_schedule_id == cost_schedule_id:
if self.props.active_cost_schedule_id == cost_schedule["id"]:
if self.props.is_editing == "COST_SCHEDULE":
self.draw_editable_cost_schedule_ui()
elif self.props.is_editing == "COST_ITEMS":
if self.props.should_show_column_ui:
self.draw_column_ui()
self.draw_editable_cost_item_ui(cost_schedule_id)
self.draw_editable_cost_item_ui()
def draw_column_ui(self):
row = self.layout.row(align=True)
@@ -97,7 +88,7 @@ class BIM_PT_cost_schedules(Panel):
def draw_editable_cost_schedule_ui(self):
blenderbim.bim.helper.draw_attributes(self.props.cost_schedule_attributes, self.layout)
def draw_editable_cost_item_ui(self, cost_schedule_id):
def draw_editable_cost_item_ui(self):
row = self.layout.row(align=True)
row.alignment = "RIGHT"
ifc_definition_id = None
@@ -105,7 +96,7 @@ class BIM_PT_cost_schedules(Panel):
ifc_definition_id = self.props.cost_items[self.props.active_cost_item_index].ifc_definition_id
if ifc_definition_id:
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
if not CostSchedulesData.data["is_editing_rates"]:
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
op.cost_item = ifc_definition_id
@@ -150,33 +141,27 @@ class BIM_PT_cost_schedules(Panel):
op.cost_item = self.props.active_cost_item_id
op.ifc_class = self.props.quantity_types
for quantity_id in Data.cost_items[self.props.active_cost_item_id]["CostQuantities"]:
quantity = Data.physical_quantities[quantity_id]
value = quantity[[k for k in quantity.keys() if "Value" in k][0]]
for quantity in CostSchedulesData.data["cost_quantities"]:
row = self.layout.row(align=True)
row.label(text=quantity["Name"])
row.label(text="{0:.2f}".format(value))
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
row.label(text=quantity["name"])
row.label(text=quantity["value"])
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity["id"]:
op = row.operator("bim.edit_cost_item_quantity", text="", icon="CHECKMARK")
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
row.operator("bim.disable_editing_cost_item_quantity", text="", icon="CANCEL")
elif self.props.active_cost_item_quantity_id:
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
else:
op = row.operator("bim.enable_editing_cost_item_quantity", text="", icon="GREASEPENCIL")
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
op = row.operator("bim.remove_cost_item_quantity", text="", icon="X")
op.cost_item = self.props.active_cost_item_id
op.physical_quantity = quantity_id
op.physical_quantity = quantity["id"]
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity_id:
box = self.layout.box()
self.draw_editable_cost_item_quantity_ui(box)
def draw_editable_cost_item_quantity_ui(self, layout):
blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout)
if self.props.active_cost_item_quantity_id and self.props.active_cost_item_quantity_id == quantity["id"]:
blenderbim.bim.helper.draw_attributes(self.props.quantity_attributes, self.layout.box())
def draw_editable_cost_item_values_ui(self):
row = self.layout.row(align=True)
@@ -189,25 +174,20 @@ class BIM_PT_cost_schedules(Panel):
if self.props.cost_types == "CATEGORY":
op.cost_category = self.props.cost_category
for cost_value_id in Data.cost_items[self.props.active_cost_item_id]["CostValues"]:
for cost_value in CostSchedulesData.data["cost_values"]:
row = self.layout.row(align=True)
self.draw_readonly_cost_value_ui(row, cost_value_id)
self.draw_readonly_cost_value_ui(row, cost_value)
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_value_id])
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, self.layout.box())
def draw_readonly_cost_value_ui(self, layout, cost_value_id):
cost_value = Data.cost_values[cost_value_id]
if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA":
def draw_readonly_cost_value_ui(self, layout, cost_value):
if self.props.active_cost_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")
layout.label(text=cost_value["label"], icon="DISC")
self.draw_cost_value_operator_ui(layout, cost_value_id, self.props.active_cost_item_id)
self.draw_cost_value_operator_ui(layout, cost_value["id"], self.props.active_cost_item_id)
def draw_cost_value_operator_ui(self, layout, cost_value_id, parent_id):
if self.props.active_cost_value_id and self.props.active_cost_value_id == cost_value_id:
@@ -231,9 +211,6 @@ class BIM_PT_cost_schedules(Panel):
op.parent = parent_id
op.cost_value = cost_value_id
def draw_editable_cost_value_ui(self, layout, cost_value):
blenderbim.bim.helper.draw_attributes(self.props.cost_value_attributes, layout)
class BIM_PT_cost_item_types(Panel):
bl_label = "IFC Cost Item Types"
@@ -250,7 +227,9 @@ class BIM_PT_cost_item_types(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if not CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
@@ -336,18 +315,15 @@ class BIM_PT_cost_item_quantities(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
def draw(self, context):
if not Data.is_loaded:
Data.load(IfcStore.get_file())
if not UnitData.is_loaded:
UnitData.load(IfcStore.get_file())
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
@@ -485,7 +461,9 @@ class BIM_PT_cost_item_rates(Panel):
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if not CostSchedulesData.is_loaded:
return False
if CostSchedulesData.data["is_editing_rates"]:
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
@@ -514,7 +492,7 @@ class BIM_UL_cost_items_trait:
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
self.props = context.scene.BIMCostProperties
cost_item = Data.cost_items[item.ifc_definition_id]
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
row = layout.row(align=True)
self.draw_hierarchy(row, item)
@@ -550,7 +528,7 @@ class BIM_UL_cost_items_trait:
layout.label(text="{0:.2f}".format(cost_item["TotalCost"]))
def draw_quantity_column(self, layout, cost_item):
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] == "SCHEDULEOFRATES":
if CostSchedulesData.data["is_editing_rates"]:
self.draw_uom_column(layout, cost_item)
else:
self.draw_total_quantity_column(layout, cost_item)
+361 -2
View File
@@ -6,17 +6,376 @@ Scenario: Add cost schedule
When I press "bim.add_cost_schedule"
Then nothing happens
Scenario: Enable editing cost schedule
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
When I press "bim.enable_editing_cost_schedule(cost_schedule={cost_schedule})"
Then nothing happens
Scenario: Disable editing cost schedule
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_schedule(cost_schedule={cost_schedule})"
When I press "bim.disable_editing_cost_schedule"
Then nothing happens
Scenario: Edit cost schedule
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_schedule(cost_schedule={cost_schedule})"
When I press "bim.edit_cost_schedule"
Then nothing happens
Scenario: Remove cost schedule
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
When I press "bim.remove_cost_schedule(cost_schedule={cost_schedule})"
Then nothing happens
Scenario: Enable editing cost items
Given an empty IFC project
When I press "bim.add_cost_schedule"
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
When I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
Then nothing happens
Scenario: Disable editing cost schedule - after editing items
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
When I press "bim.disable_editing_cost_schedule"
Then nothing happens
Scenario: Add summary cost item
Given an empty IFC project
When I press "bim.add_cost_schedule"
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
When I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
Then nothing happens
Scenario: Remove cost item
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
When I press "bim.remove_cost_item(cost_item={cost_item})"
Then nothing happens
Scenario: Enable editing cost item
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
When I press "bim.enable_editing_cost_item(cost_item={cost_item})"
Then nothing happens
Scenario: Disable editing cost item
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item(cost_item={cost_item})"
When I press "bim.disable_editing_cost_item"
Then nothing happens
Scenario: Edit cost item
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item(cost_item={cost_item})"
When I press "bim.edit_cost_item"
Then nothing happens
Scenario: Add cost item
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
When I press "bim.add_cost_item(cost_item={cost_item})"
Then nothing happens
Scenario: Enable editing cost item quantities
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
When I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
Then nothing happens
Scenario: Add cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
When I press "bim.add_cost_item_quantity(cost_item={cost_item}, ifc_class='IfcQuantityArea')"
Then nothing happens
Scenario: Remove cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
And I press "bim.add_cost_item_quantity(cost_item={cost_item}, ifc_class='IfcQuantityArea')"
And the variable "quantity" is "{ifc}.by_type('IfcQuantityArea')[0].id()"
When I press "bim.remove_cost_item_quantity(cost_item={cost_item}, physical_quantity={quantity})"
Then nothing happens
Scenario: Enable editing cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
And I press "bim.add_cost_item_quantity(cost_item={cost_item}, ifc_class='IfcQuantityArea')"
And the variable "quantity" is "{ifc}.by_type('IfcQuantityArea')[0].id()"
When I press "bim.enable_editing_cost_item_quantity(physical_quantity={quantity})"
Then nothing happens
Scenario: Disable editing cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
And I press "bim.add_cost_item_quantity(cost_item={cost_item}, ifc_class='IfcQuantityArea')"
And the variable "quantity" is "{ifc}.by_type('IfcQuantityArea')[0].id()"
And I press "bim.enable_editing_cost_item_quantity(physical_quantity={quantity})"
When I press "bim.disable_editing_cost_item_quantity"
Then nothing happens
Scenario: Edit cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_quantities(cost_item={cost_item})"
And I press "bim.add_cost_item_quantity(cost_item={cost_item}, ifc_class='IfcQuantityArea')"
And the variable "quantity" is "{ifc}.by_type('IfcQuantityArea')[0].id()"
And I press "bim.enable_editing_cost_item_quantity(physical_quantity={quantity})"
And I set "scene.BIMCostProperties.quantity_attributes[2].float_value" to "3"
When I press "bim.edit_cost_item_quantity(physical_quantity={quantity})"
Then nothing happens
Scenario: Enable editing cost item quantity
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
When I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
Then nothing happens
Scenario: Add cost value - fixed
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
When I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
Then nothing happens
Scenario: Enable editing cost item value
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
And I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
And the variable "cost_value" is "{ifc}.by_type('IfcCostValue')[0].id()"
When I press "bim.enable_editing_cost_item_value(cost_value={cost_value})"
Then nothing happens
Scenario: Disable editing cost item value
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
And I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
And the variable "cost_value" is "{ifc}.by_type('IfcCostValue')[0].id()"
And I press "bim.enable_editing_cost_item_value(cost_value={cost_value})"
When I press "bim.disable_editing_cost_item_value"
Then nothing happens
Scenario: Edit cost item value
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
And I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
And the variable "cost_value" is "{ifc}.by_type('IfcCostValue')[0].id()"
And I press "bim.enable_editing_cost_item_value(cost_value={cost_value})"
When I press "bim.edit_cost_value(cost_value={cost_value})"
Then nothing happens
Scenario: Enable editing cost item value formula
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
And I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
And the variable "cost_value" is "{ifc}.by_type('IfcCostValue')[0].id()"
When I press "bim.enable_editing_cost_item_value_formula(cost_value={cost_value})"
Then nothing happens
Scenario: Edit cost item value formula
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I press "bim.enable_editing_cost_item_values(cost_item={cost_item})"
And I set "scene.BIMCostProperties.cost_types" to "FIXED"
And I press "bim.add_cost_value(parent={cost_item}, cost_type='FIXED')"
And the variable "cost_value" is "{ifc}.by_type('IfcCostValue')[0].id()"
And I press "bim.enable_editing_cost_item_value_formula(cost_value={cost_value})"
And I set "scene.BIMCostProperties.cost_value_formula" to "3 + 2"
When I press "bim.edit_cost_value_formula(cost_value={cost_value})"
Then nothing happens
Scenario: Add cost column
Given an empty IFC project
And I press "bim.add_cost_schedule"
And I set "scene.BIMCostProperties.should_show_column_ui" to "True"
And I set "scene.BIMCostProperties.cost_column" to "Foobar"
When I press "bim.add_cost_column(name='Foobar')"
Then nothing happens
Scenario: Remove cost column
Given an empty IFC project
And I press "bim.add_cost_schedule"
And I set "scene.BIMCostProperties.should_show_column_ui" to "True"
And I set "scene.BIMCostProperties.cost_column" to "Foobar"
And I press "bim.add_cost_column(name='Foobar')"
When I press "bim.remove_cost_column(name='Foobar')"
Then nothing happens
Scenario: Assign cost item quantity - count based
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
When I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
Then nothing happens
Scenario: Assign cost item quantity - quantity based
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
And I press "bim.add_qto(obj='IfcWall/Cube', obj_type='Object')"
And I press "bim.calculate_all_quantities"
When I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='NetVolume')"
Then nothing happens
Scenario: Unassign cost item quantity - selection based
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
When I press "bim.unassign_cost_item_quantity(cost_item={cost_item}, related_object=0)"
Then nothing happens
Scenario: Unassign cost item quantity - explicit object
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
And the variable "wall" is "{ifc}.by_type('IfcWall')[0].id()"
When I press "bim.unassign_cost_item_quantity(cost_item={cost_item}, related_object={wall})"
Then nothing happens
Scenario: Select cost item products
Given an empty IFC project
And I press "bim.add_cost_schedule"
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
And I add a cube
And the object "Cube" is selected
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
When I press "bim.select_cost_item_products(cost_item={cost_item})"
Then nothing happens
@@ -23,6 +23,82 @@ arithmetic_operator_symbols = {"ADD": "+", "DIVIDE": "/", "MULTIPLY": "*", "SUBT
symbol_arithmetic_operators = {"+": "ADD", "/": "DIVIDE", "*": "MULTIPLY", "-": "SUBTRACT"}
def get_primitive_applied_value(applied_value):
if not applied_value:
return 0.0
elif isinstance(applied_value, float):
return applied_value
elif hasattr(applied_value, "wrappedValue") and isinstance(applied_value.wrappedValue, float):
return applied_value.wrappedValue
elif applied_value.is_a("IfcMeasureWithUnit"):
return applied_value.ValueComponent
assert False, "Applied value {applied_value} not implemented"
def get_total_quantity(root_element):
if root_element.is_a("IfcCostItem"):
return sum([q[3] for q in root_element.CostQuantities or []]) or 1.0
elif root_element.is_a("IfcConstructionResource"):
return root_element.BaseQuantity[3] if root_element.BaseQuantity else 1.0
def calculate_applied_value(root_element, cost_value, category_filter=None):
if cost_value.ArithmeticOperator and cost_value.Components:
component_values = []
for component in cost_value.Components:
component_values.append(calculate_applied_value(root_element, component, category_filter))
if cost_value.ArithmeticOperator == "ADD":
return sum(component_values)
result = component_values.pop(0)
if cost_value.ArithmeticOperator == "DIVIDE":
for value in component_values:
try:
result /= value
except ZeroDivisionError:
pass
elif cost_value.ArithmeticOperator == "MULTIPLY":
for value in component_values:
result *= value
elif cost_value.ArithmeticOperator == "SUBTRACT":
for value in component_values:
result -= value
return result
if cost_value.Category is None:
return get_primitive_applied_value(cost_value.AppliedValue)
elif cost_value.Category == "*":
if root_element.IsNestedBy:
return sum_child_root_elements(root_element)
else:
return get_primitive_applied_value(cost_value.AppliedValue)
elif cost_value.Category:
if root_element.IsNestedBy:
return sum_child_root_elements(root_element, category_filter=cost_value.Category)
else:
return get_primitive_applied_value(cost_value.AppliedValue)
return 0
def sum_child_root_elements(root_element, category_filter=None):
result = 0
for rel in root_element.IsNestedBy:
for child_root_element in rel.RelatedObjects:
if root_element.is_a("IfcCostItem"):
values = child_root_element.CostValues
elif root_element.is_a("IfcConstructionResource"):
values = child_root_element.BaseCosts
for child_cost_value in values or []:
if category_filter and child_cost_value.Category != category_filter:
continue
child_applied_value = calculate_applied_value(child_root_element, child_cost_value)
child_quantity = get_total_quantity(child_root_element)
if child_cost_value.UnitBasis:
value_component = child_cost_value.UnitBasis.ValueComponent.wrappedValue
result += child_quantity / value_component * child_applied_value
else:
result += child_quantity * child_applied_value
return result
def serialise_cost_value(cost_value):
result = _serialise_cost_value(cost_value)
if result and result[0] == "(" and result[-1] == ")":