mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
See #1848. Refactor cost data class.
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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] == ")":
|
||||
|
||||
Reference in New Issue
Block a user