Linked cost item products now auto update cost quantities upon assignment and unassignment, with user friendly quantity type selection dropdown

This commit is contained in:
Dion Moult
2021-05-13 14:25:05 +10:00
parent 5334d2b754
commit 3b33977acc
8 changed files with 118 additions and 37 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ def import_attributes(ifc_class, props, data, callback=None):
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else False
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
@@ -24,8 +24,8 @@ classes = (
operator.ExpandCostItem,
operator.ContractCostItem,
operator.RemoveCostItem,
operator.AssignControl,
operator.UnassignControl,
operator.AssignCostItemProduct,
operator.UnassignCostItemProduct,
operator.AddCostItemQuantity,
operator.RemoveCostItemQuantity,
operator.AddCostValue,
@@ -3,6 +3,7 @@ import bpy
import json
import ifcopenshell.api
import blenderbim.bim.helper
from blenderbim.bim.module.cost.prop import purge
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
@@ -259,8 +260,8 @@ class EditCostItem(bpy.types.Operator):
return {"FINISHED"}
class AssignControl(bpy.types.Operator):
bl_idname = "bim.assign_control"
class AssignCostItemProduct(bpy.types.Operator):
bl_idname = "bim.assign_cost_item_product"
bl_label = "Assign Control"
cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty()
@@ -269,20 +270,23 @@ class AssignControl(bpy.types.Operator):
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
for related_object in related_objects:
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"control.assign_control",
self.file,
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
relating_control=self.file.by_id(self.cost_item),
)
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.assign_cost_item_product",
self.file,
cost_item=self.file.by_id(self.cost_item),
products=[
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
for o in related_objects
if o.BIMObjectProperties.ifc_definition_id
],
)
Data.load(self.file)
return {"FINISHED"}
class UnassignControl(bpy.types.Operator):
bl_idname = "bim.unassign_control"
class UnassignCostItemProduct(bpy.types.Operator):
bl_idname = "bim.unassign_cost_item_product"
bl_label = "Unassign Control"
cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty()
@@ -291,14 +295,17 @@ class UnassignControl(bpy.types.Operator):
related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects
)
for related_object in related_objects:
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"control.unassign_control",
self.file,
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
relating_control=self.file.by_id(self.cost_item),
)
self.file = IfcStore.get_file()
ifcopenshell.api.run(
"cost.unassign_cost_item_product",
self.file,
cost_item=self.file.by_id(self.cost_item),
products=[
self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
for o in related_objects
if o.BIMObjectProperties.ifc_definition_id
],
)
Data.load(self.file)
return {"FINISHED"}
@@ -312,6 +319,7 @@ class EnableEditingCostItemQuantities(bpy.types.Operator):
props = context.scene.BIMCostProperties
props.active_cost_item_id = self.cost_item
props.cost_item_editing_type = "QUANTITIES"
purge()
return {"FINISHED"}
@@ -348,8 +356,7 @@ class AddCostItemQuantity(bpy.types.Operator):
"cost.assign_cost_item_product_quantities",
self.file,
cost_item=self.file.by_id(self.cost_item),
qto_name=self.props.qto_name,
prop_name=self.props.prop_name
prop_name=self.props.quantity_names,
)
def add_manual_quantity(self):
@@ -439,9 +446,7 @@ class AddCostValue(bpy.types.Operator):
elif self.cost_type == "CATEGORY":
category = self.cost_category
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
ifcopenshell.api.run(
"cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category}
)
ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category})
Data.load(self.file)
return {"FINISHED"}
@@ -2,6 +2,7 @@ import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.pset.data import Data as PsetData
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
@@ -17,17 +18,19 @@ from bpy.props import (
quantitytypes_enum = []
quantitynames_enum = []
def purge():
global quantitytypes_enum
global quantitynames_enum
quantitytypes_enum = []
quantitynames_enum = []
def getQuantityTypes(self, context):
global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.clear()
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
quantitytypes_enum.extend(
[
@@ -38,6 +41,21 @@ def getQuantityTypes(self, context):
return quantitytypes_enum
def getQuantityNames(self, context):
global quantitynames_enum
ifc_file = IfcStore.get_file()
if len(quantitynames_enum) == 0 and ifc_file:
names = set()
for element_id in Data.cost_items[self.active_cost_item_id]["Controls"]:
if element_id not in PsetData.products:
PsetData.load(IfcStore.get_file(), element_id)
for qto_id in PsetData.products[element_id]["qtos"]:
qto = PsetData.qtos[qto_id]
[names.add(PsetData.properties[p]["Name"]) for p in qto["Properties"]]
quantitynames_enum.extend([(n, n, "") for n in names])
return quantitynames_enum
def updateCostItemName(self, context):
if self.name == "Unnamed":
return
@@ -73,8 +91,7 @@ class BIMCostProperties(PropertyGroup):
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
qto_name: StringProperty(name="Qto Name")
prop_name: StringProperty(name="Prop Name")
quantity_names: EnumProperty(items=getQuantityNames, name="Quantity Names")
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
cost_types: EnumProperty(
@@ -97,8 +97,7 @@ class BIM_PT_cost_schedules(Panel):
row = self.layout.row(align=True)
row.prop(self.props, "quantity_types", text="")
if self.props.quantity_types == "QTO":
row.prop(self.props, "qto_name", text="")
row.prop(self.props, "prop_name", text="")
row.prop(self.props, "quantity_names", text="")
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
op.cost_item = self.props.active_cost_item_id
op.ifc_class = self.props.quantity_types
@@ -254,10 +253,10 @@ class BIM_UL_cost_items(UIList):
oprops = context.active_object.BIMObjectProperties
row = layout.row(align=True)
if oprops.ifc_definition_id in cost_item["Controls"]:
op = row.operator("bim.unassign_control", text="", icon="KEYFRAME_HLT", emboss=False)
op = row.operator("bim.unassign_cost_item_product", text="", icon="KEYFRAME_HLT", emboss=False)
op.cost_item = item.ifc_definition_id
else:
op = row.operator("bim.assign_control", text="", icon="KEYFRAME", emboss=False)
op = row.operator("bim.assign_cost_item_product", text="", icon="KEYFRAME", emboss=False)
op.cost_item = item.ifc_definition_id
if props.active_cost_item_id == item.ifc_definition_id:
@@ -0,0 +1,31 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_item": None, "products": []}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
quantity_names = set()
for quantity in self.settings["cost_item"].CostQuantities or []:
if quantity.Name:
quantity_names.add(quantity.Name)
for product in self.settings["products"]:
ifcopenshell.api.run(
"control.assign_control",
self.file,
related_object=product,
relating_control=self.settings["cost_item"],
)
for name in quantity_names:
ifcopenshell.api.run(
"cost.assign_cost_item_product_quantities",
self.file,
cost_item=self.settings["cost_item"],
prop_name=name
)
@@ -4,7 +4,7 @@ import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_item": None, "qto_name": "", "prop_name": ""}
self.settings = {"cost_item": None, "prop_name": ""}
for key, value in settings.items():
self.settings[key] = value
@@ -25,7 +25,7 @@ class Usecase:
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
def add_quantity_from_qto(self, qto):
if not qto.is_a("IfcElementQuantity") or qto.Name.lower() != self.settings["qto_name"].lower():
if not qto.is_a("IfcElementQuantity"):
return
for prop in qto.Quantities:
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower():
@@ -0,0 +1,29 @@
import ifcopenshell.api
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"cost_item": None, "products": []}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.quantities = set(self.settings["cost_item"].CostQuantities or [])
for quantity in self.settings["cost_item"].CostQuantities or []:
for inverse in self.file.get_inverse(quantity):
if not inverse.is_a("IfcElementQuantity"):
continue
for rel in inverse.DefinesOccurrence or []:
for related_object in rel.RelatedObjects:
if related_object in self.settings["products"]:
self.quantities.remove(quantity)
self.settings["cost_item"].CostQuantities = list(self.quantities)
for product in self.settings["products"]:
ifcopenshell.api.run(
"control.unassign_control",
self.file,
related_object=product,
relating_control=self.settings["cost_item"],
)