You can now browse a schedule of rates alongside a cost schedule

This commit is contained in:
Dion Moult
2021-08-17 11:01:41 +10:00
parent f2aec9440f
commit 899a0c6fb5
4 changed files with 178 additions and 29 deletions
@@ -59,6 +59,10 @@ classes = (
operator.ImportCostScheduleCsv,
operator.LoadCostItemQuantities,
operator.LoadCostItemTypes,
operator.AssignCostValue,
operator.LoadScheduleOfRates,
operator.ExpandCostItemRate,
operator.ContractCostItemRate,
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
@@ -66,10 +70,12 @@ classes = (
ui.BIM_PT_cost_schedules,
ui.BIM_PT_cost_item_quantities,
ui.BIM_PT_cost_item_types,
ui.BIM_PT_cost_item_rates,
ui.BIM_UL_cost_items,
ui.BIM_UL_cost_columns,
ui.BIM_UL_cost_item_quantities,
ui.BIM_UL_cost_item_types,
ui.BIM_UL_cost_item_rates,
)
@@ -62,6 +62,7 @@ class EditCostSchedule(bpy.types.Operator):
**{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes},
)
Data.load(IfcStore.get_file())
purge()
bpy.ops.bim.disable_editing_cost_schedule()
return {"FINISHED"}
@@ -124,8 +125,7 @@ class EnableEditingCostItems(bpy.types.Operator):
self.props = context.scene.BIMCostProperties
self.props.is_cost_update_enabled = False
self.props.active_cost_schedule_id = self.cost_schedule
while len(self.props.cost_items) > 0:
self.props.cost_items.remove(0)
self.props.cost_items.clear()
self.contracted_cost_items = json.loads(self.props.contracted_cost_items)
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
@@ -164,8 +164,6 @@ class EnableEditingCostItems(bpy.types.Operator):
for related_object_id in cost_item["IsNestedBy"]:
self.create_new_cost_item_li(related_object_id, level_index + 1)
return {"FINISHED"}
class DisableEditingCostSchedule(bpy.types.Operator):
bl_idname = "bim.disable_editing_cost_schedule"
@@ -808,6 +806,9 @@ class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper):
is_schedule_of_rates: bpy.props.BoolProperty(name="Is Schedule Of Rates", default=False)
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
from ifc5d.csv2ifc import Csv2Ifc
self.file = IfcStore.get_file()
@@ -819,6 +820,7 @@ class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper):
csv2ifc.execute()
Data.load(IfcStore.get_file())
UnitData.load(IfcStore.get_file())
purge()
print("Import finished in {:.2f} seconds".format(time.time() - start))
return {"FINISHED"}
@@ -912,3 +914,78 @@ class LoadCostItemTypes(bpy.types.Operator):
new.ifc_definition_id = control_id
new.name = related_object.Name or "Unnamed"
return {"FINISHED"}
class AssignCostValue(bpy.types.Operator):
bl_idname = "bim.assign_cost_value"
bl_label = "Assign Cost Value"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
return {"FINISHED"}
class LoadScheduleOfRates(bpy.types.Operator):
bl_idname = "bim.load_schedule_of_rates"
bl_label = "Load Schedule of Rates"
bl_options = {"REGISTER", "UNDO"}
cost_schedule: bpy.props.IntProperty()
def execute(self, context):
self.props = context.scene.BIMCostProperties
self.props.is_cost_update_enabled = False
self.props.cost_item_rates.clear()
self.contracted_cost_item_rates = json.loads(self.props.contracted_cost_item_rates)
for related_object_id in Data.cost_schedules[self.cost_schedule]["Controls"]:
self.create_new_cost_item_li(related_object_id, 0)
self.props.is_cost_update_enabled = True
return {"FINISHED"}
def create_new_cost_item_li(self, related_object_id, level_index):
cost_item = Data.cost_items[related_object_id]
new = self.props.cost_item_rates.add()
new.ifc_definition_id = related_object_id
new.name = cost_item["Name"] or "Unnamed"
new.identification = cost_item["Identification"] or "XXX"
new.is_expanded = related_object_id not in self.contracted_cost_item_rates
new.level_index = level_index
if cost_item["IsNestedBy"]:
new.has_children = True
if new.is_expanded:
for related_object_id in cost_item["IsNestedBy"]:
self.create_new_cost_item_li(related_object_id, level_index + 1)
class ExpandCostItemRate(bpy.types.Operator):
bl_idname = "bim.expand_cost_item_rate"
bl_label = "Expand Cost Item Rate"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.remove(self.cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(props.schedule_of_rates))
return {"FINISHED"}
class ContractCostItemRate(bpy.types.Operator):
bl_idname = "bim.contract_cost_item_rate"
bl_label = "Contract Cost Item Rate"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMCostProperties
self.file = IfcStore.get_file()
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.append(self.cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(props.schedule_of_rates))
return {"FINISHED"}
@@ -1,4 +1,3 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
#
@@ -43,6 +42,7 @@ processquantitynames_enum = []
processquantitynames_id = 0
resourcequantitynames_enum = []
resourcequantitynames_id = 0
scheduleofrates_enum = []
def purge():
@@ -53,6 +53,7 @@ def purge():
global processquantitynames_id
global resourcequantitynames_enum
global resourcequantitynames_id
global scheduleofrates_enum
quantitytypes_enum = []
productquantitynames_enum = []
productquantitynames_count = []
@@ -60,12 +61,27 @@ def purge():
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
def update_schedule_of_rates(self, context):
bpy.ops.bim.load_schedule_of_rates(cost_schedule=int(self.schedule_of_rates))
def getQuantityTypes(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum = []
quantitytypes_enum.extend(
[
(t.name(), t.name(), "")
@@ -238,4 +254,10 @@ class BIMCostProperties(PropertyGroup):
cost_item_resources: CollectionProperty(name="Cost Item Resources", type=CostItemQuantity)
active_cost_item_resource_index: IntProperty(name="Active Cost Item Resource Index")
cost_item_type_products: CollectionProperty(name="Cost Item Type Products", type=CostItemType)
active_cost_item_type_product_index: IntProperty(name="Active Cost Item TYpe Product Index")
active_cost_item_type_product_index: IntProperty(name="Active Cost Item Type Product Index")
schedule_of_rates: EnumProperty(
items=get_schedule_of_rates, name="Schedule Of Rates", update=update_schedule_of_rates
)
cost_item_rates: CollectionProperty(name="Cost Item Rates", type=CostItem)
active_cost_item_rate_index: IntProperty(name="Active Cost Rate Index")
contracted_cost_item_rates: StringProperty(name="Contracted Cost Item Rates", default="[]")
+66 -22
View File
@@ -441,26 +441,51 @@ class BIM_PT_cost_item_quantities(Panel):
op.prop_name = self.props.resource_quantity_names
class BIM_UL_cost_items(UIList):
class BIM_PT_cost_item_rates(Panel):
bl_label = "IFC Cost Item Rates"
bl_idname = "BIM_PT_cost_item_rates"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
bl_parent_id = "BIM_PT_cost_schedules"
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
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":
return False
if total_cost_items > 0 and props.active_cost_item_index < total_cost_items:
return True
return False
def draw(self, context):
self.props = context.scene.BIMCostProperties
cost_item = self.props.cost_items[self.props.active_cost_item_index]
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
row.operator("bim.assign_cost_value", text="", icon="COPYDOWN")
self.layout.template_list(
"BIM_UL_cost_item_rates",
"",
self.props,
"cost_item_rates",
self.props,
"active_cost_item_rate_index",
)
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]
row = layout.row(align=True)
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
row.operator(
"bim.contract_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
).cost_item = item.ifc_definition_id
else:
row.operator(
"bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
).cost_item = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
self.draw_hierarchy(row, item)
split1 = row.split(factor=0.1)
split1.prop(item, "identification", emboss=False, text="")
@@ -479,7 +504,19 @@ class BIM_UL_cost_items(UIList):
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
split2.label(text="{0:.2f}".format(cost_item["TotalCostValue"]))
self.draw_buttons(split2, item, cost_item)
# TODO: reimplement "bim.copy_cost_item_values" somewhere with better UX
def draw_hierarchy(self, row, item):
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
if item.has_children:
if item.is_expanded:
op = row.operator(self.contract_operator, text="", emboss=False, icon="DISCLOSURE_TRI_DOWN")
else:
op = row.operator(self.expand_operator, text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT")
op.cost_item = item.ifc_definition_id
else:
row.label(text="", icon="DOT")
def draw_uom_column(self, layout, cost_item):
text = "-"
@@ -492,13 +529,20 @@ class BIM_UL_cost_items(UIList):
def draw_quantity_column(self, layout, cost_item):
layout.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})")
def draw_buttons(self, row, item, cost_item):
pass # TODO: reimplement somewhere with better UX
# elif self.props.active_cost_item_id:
# if self.props.cost_item_editing_type == "VALUES":
# op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN")
# op.source = self.props.active_cost_item_id
# op.destination = item.ifc_definition_id
class BIM_UL_cost_items(BIM_UL_cost_items_trait, UIList):
def __init__(self):
self.contract_operator = "bim.contract_cost_item"
self.expand_operator = "bim.expand_cost_item"
class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
# A schedule of rates UIList is identical to a regular cost items UIList but
# we want a separate UIList instance so that you can browse both lists
# independently in Blender. So we use a trait.
def __init__(self):
self.contract_operator = "bim.contract_cost_item_rate"
self.expand_operator = "bim.expand_cost_item_rate"
class BIM_UL_cost_columns(UIList):