mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
You can now browse a schedule of rates alongside a cost schedule
This commit is contained in:
@@ -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="[]")
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user