Implement cost identification numbers and big cleanup to cost schedule UI

This commit is contained in:
Dion Moult
2021-08-13 18:01:23 +10:00
parent 26f800331e
commit 2f3c79a0e6
5 changed files with 87 additions and 47 deletions
@@ -103,6 +103,7 @@ class EnableEditingCostItems(bpy.types.Operator):
if context.preferences.addons["blenderbim"].preferences.should_play_chaching_sound:
self.play_chaching_sound() # lol
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)
@@ -111,6 +112,7 @@ class EnableEditingCostItems(bpy.types.Operator):
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_editing = "COST_ITEMS"
self.props.is_cost_update_enabled = True
return {"FINISHED"}
def play_chaching_sound(self):
@@ -134,6 +136,7 @@ class EnableEditingCostItems(bpy.types.Operator):
new = self.props.cost_items.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_items
new.level_index = level_index
if cost_item["IsNestedBy"]:
@@ -128,11 +128,27 @@ def update_cost_item_index(self, context):
bpy.ops.bim.load_cost_item_quantities()
def updateCostItemName(self, context):
if self.name == "Unnamed":
def updateCostItemIdentification(self, context):
props = context.scene.BIMCostProperties
if not props.is_cost_update_enabled or self.identification == "XXX":
return
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_item",
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
def updateCostItemName(self, context):
props = context.scene.BIMCostProperties
if not props.is_cost_update_enabled or self.name == "Unnamed":
return
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.edit_cost_item",
self.file,
@@ -146,6 +162,7 @@ def updateCostItemName(self, context):
class CostItem(PropertyGroup):
name: StringProperty(name="Name", update=updateCostItemName)
identification: StringProperty(name="Identification", update=updateCostItemIdentification)
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_children: BoolProperty(name="Has Children")
is_expanded: BoolProperty(name="Is Expanded")
@@ -159,6 +176,7 @@ class CostItemQuantity(PropertyGroup):
class BIMCostProperties(PropertyGroup):
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
is_editing: StringProperty(name="Is Editing")
active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id")
+46 -30
View File
@@ -76,6 +76,32 @@ class BIM_PT_cost_schedules(Panel):
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
def draw_editable_cost_item_ui(self, cost_schedule_id):
row = self.layout.row(align=True)
row.alignment = "RIGHT"
ifc_definition_id = None
if self.props.cost_items and self.props.active_cost_item_index < len(self.props.cost_items):
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":
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
op.cost_item = ifc_definition_id
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
op.cost_item = ifc_definition_id
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = ifc_definition_id
if self.props.active_cost_item_id == ifc_definition_id:
if self.props.cost_item_editing_type == "ATTRIBUTES":
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
else:
op = row.operator("bim.enable_editing_cost_item", text="", icon="GREASEPENCIL")
op.cost_item = ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = ifc_definition_id
self.layout.template_list(
"BIM_UL_cost_items",
"",
@@ -348,7 +374,7 @@ class BIM_PT_cost_item_quantities(Panel):
class BIM_UL_cost_items(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
props = context.scene.BIMCostProperties
self.props = context.scene.BIMCostProperties
cost_item = Data.cost_items[item.ifc_definition_id]
row = layout.row(align=True)
@@ -366,40 +392,30 @@ class BIM_UL_cost_items(UIList):
else:
row.label(text="", icon="DOT")
split1 = row.split(factor=0.7)
split1.prop(item, "name", emboss=False, text="")
split1 = row.split(factor=0.1)
split1.prop(item, "identification", emboss=False, text="")
split2 = split1.split(factor=0.5)
split2.alignment = "RIGHT"
split2.prop(item, "name", emboss=False, text="")
if Data.cost_schedules[props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
op = row.operator("bim.enable_editing_cost_item_quantities", text="", icon="PROPERTIES")
op.cost_item = item.ifc_definition_id
row.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol']})")
if Data.cost_schedules[self.props.active_cost_schedule_id]["PredefinedType"] != "SCHEDULEOFRATES":
split2.label(text="{0:.2f}".format(cost_item["TotalCostQuantity"]) + f" ({cost_item['UnitSymbol'] or '?'})")
op = row.operator("bim.enable_editing_cost_item_values", text="", icon="DISC")
op.cost_item = item.ifc_definition_id
row.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
split2.label(text="{0:.2f}".format(cost_item["TotalAppliedValue"]))
for column in props.columns:
row.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
for column in self.props.columns:
split2.label(text=str(cost_item["CategoryValues"].get(column.name, "-")))
row.label(text="{0:.2f}".format(cost_item["TotalCostValue"]), icon="CON_TRANSLIKE")
split2.label(text="{0:.2f}".format(cost_item["TotalCostValue"]))
self.draw_buttons(split2, item, cost_item)
if props.active_cost_item_id == item.ifc_definition_id:
if props.cost_item_editing_type == "ATTRIBUTES":
row.operator("bim.edit_cost_item", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL")
elif props.active_cost_item_id:
if props.cost_item_editing_type == "VALUES":
op = row.operator("bim.copy_cost_item_values", text="", icon="COPYDOWN")
op.source = props.active_cost_item_id
op.destination = item.ifc_definition_id
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
else:
row.operator(
"bim.enable_editing_cost_item", text="", icon="GREASEPENCIL"
).cost_item = item.ifc_definition_id
row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id
row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id
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_columns(UIList):
+4 -1
View File
@@ -60,18 +60,20 @@ class Csv2Ifc:
def get_row_cost_data(self, row):
name = row[self.headers["Name"]]
identification = row[self.headers["Identification"]] if "Identification" in self.headers else None
cost_quantities = row[self.headers["Quantity"]]
cost_quantities_unit = row[self.headers["Unit"]]
if self.has_categories:
cost_values = {
k: float(row[v])
for k, v in self.headers.items()
if k not in ["Hierarchy", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
if k not in ["Hierarchy", "Identification", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
}
else:
cost_values = row[self.headers["Value"]]
cost_values = float(cost_values) if cost_values else None
return {
"Identification": str(identification) if identification else None,
"Name": str(name) if name else None,
"CostQuantities": float(cost_quantities) if cost_quantities else None,
"CostQuantitiesUnit": str(cost_quantities_unit) if cost_quantities_unit else None,
@@ -99,6 +101,7 @@ class Csv2Ifc:
cost_item["ifc"] = ifcopenshell.api.run("cost.add_cost_item", self.file, cost_item=parent)
cost_item["ifc"].Name = cost_item["Name"]
cost_item["ifc"].Identification = cost_item["Identification"]
if not cost_item["CostValues"]:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
+14 -14
View File
@@ -1,14 +1,14 @@
"Hierarchy","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal"
1,"Demolition",,,,,,301
,,,,,,,
2,"Building A",,,,,,96
,,,,,,,
3,"Soft strip",1,"m",5,4,3,12
3,"Hard Strip",2,"m2",6,5,4,30
3,"Hazmat",3,"m3",7,6,5,54
,,,,,,,
2,"Building B",,,,,,205
,,,,,,,
3,"Soft strip",4,"hr",8,7,,60
3,"Hard Strip",5,,9,,8,85
3,"Hazmat",6,"kg",10,,,60
"Hierarchy","Identification","Name","Quantity","Unit","Foo","Bar","Baz","Subtotal"
1,1,"Demolition",,,,,,301
,,,,,,,,
2,1.1,"Building A",,,,,,96
,,,,,,,,
3,"1.1.1","Soft strip",1,"m",5,4,3,12
3,"1.1.2","Hard Strip",2,"m2",6,5,4,30
3,"1.1.3","Hazmat",3,"m3",7,6,5,54
,,,,,,,,
2,1.2,"Building B",,,,,,205
,,,,,,,,
3,"1.2.1","Soft strip",4,"hr",8,7,,60
3,"1.2.2","Hard Strip",5,,9,,8,85
3,"1.2.3","Hazmat",6,"kg",10,,,60
1 Hierarchy Identification Name Quantity Unit Foo Bar Baz Subtotal
2 1 1 Demolition 301
3
4 2 1.1 Building A 96
5
6 3 1.1.1 Soft strip 1 m 5 4 3 12
7 3 1.1.2 Hard Strip 2 m2 6 5 4 30
8 3 1.1.3 Hazmat 3 m3 7 6 5 54
9
10 2 1.2 Building B 205
11
12 3 1.2.1 Soft strip 4 hr 8 7 60
13 3 1.2.2 Hard Strip 5 9 8 85
14 3 1.2.3 Hazmat 6 kg 10 60