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_null = data[attribute.name()] is None
new.is_optional = attribute.optional() new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else "" 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: if is_handled_by_callback:
pass # Our job is done pass # Our job is done
elif is_handled_by_callback is False: elif is_handled_by_callback is False:
@@ -24,8 +24,8 @@ classes = (
operator.ExpandCostItem, operator.ExpandCostItem,
operator.ContractCostItem, operator.ContractCostItem,
operator.RemoveCostItem, operator.RemoveCostItem,
operator.AssignControl, operator.AssignCostItemProduct,
operator.UnassignControl, operator.UnassignCostItemProduct,
operator.AddCostItemQuantity, operator.AddCostItemQuantity,
operator.RemoveCostItemQuantity, operator.RemoveCostItemQuantity,
operator.AddCostValue, operator.AddCostValue,
@@ -3,6 +3,7 @@ import bpy
import json import json
import ifcopenshell.api import ifcopenshell.api
import blenderbim.bim.helper import blenderbim.bim.helper
from blenderbim.bim.module.cost.prop import purge
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data from ifcopenshell.api.cost.data import Data
@@ -259,8 +260,8 @@ class EditCostItem(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
class AssignControl(bpy.types.Operator): class AssignCostItemProduct(bpy.types.Operator):
bl_idname = "bim.assign_control" bl_idname = "bim.assign_cost_item_product"
bl_label = "Assign Control" bl_label = "Assign Control"
cost_item: bpy.props.IntProperty() cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty() related_object: bpy.props.StringProperty()
@@ -269,20 +270,23 @@ class AssignControl(bpy.types.Operator):
related_objects = ( related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_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()
self.file = IfcStore.get_file() ifcopenshell.api.run(
ifcopenshell.api.run( "cost.assign_cost_item_product",
"control.assign_control", self.file,
self.file, cost_item=self.file.by_id(self.cost_item),
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), products=[
relating_control=self.file.by_id(self.cost_item), self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
) for o in related_objects
if o.BIMObjectProperties.ifc_definition_id
],
)
Data.load(self.file) Data.load(self.file)
return {"FINISHED"} return {"FINISHED"}
class UnassignControl(bpy.types.Operator): class UnassignCostItemProduct(bpy.types.Operator):
bl_idname = "bim.unassign_control" bl_idname = "bim.unassign_cost_item_product"
bl_label = "Unassign Control" bl_label = "Unassign Control"
cost_item: bpy.props.IntProperty() cost_item: bpy.props.IntProperty()
related_object: bpy.props.StringProperty() related_object: bpy.props.StringProperty()
@@ -291,14 +295,17 @@ class UnassignControl(bpy.types.Operator):
related_objects = ( related_objects = (
[bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_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()
self.file = IfcStore.get_file() ifcopenshell.api.run(
ifcopenshell.api.run( "cost.unassign_cost_item_product",
"control.unassign_control", self.file,
self.file, cost_item=self.file.by_id(self.cost_item),
related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), products=[
relating_control=self.file.by_id(self.cost_item), self.file.by_id(o.BIMObjectProperties.ifc_definition_id)
) for o in related_objects
if o.BIMObjectProperties.ifc_definition_id
],
)
Data.load(self.file) Data.load(self.file)
return {"FINISHED"} return {"FINISHED"}
@@ -312,6 +319,7 @@ class EnableEditingCostItemQuantities(bpy.types.Operator):
props = context.scene.BIMCostProperties props = context.scene.BIMCostProperties
props.active_cost_item_id = self.cost_item props.active_cost_item_id = self.cost_item
props.cost_item_editing_type = "QUANTITIES" props.cost_item_editing_type = "QUANTITIES"
purge()
return {"FINISHED"} return {"FINISHED"}
@@ -348,8 +356,7 @@ class AddCostItemQuantity(bpy.types.Operator):
"cost.assign_cost_item_product_quantities", "cost.assign_cost_item_product_quantities",
self.file, self.file,
cost_item=self.file.by_id(self.cost_item), cost_item=self.file.by_id(self.cost_item),
qto_name=self.props.qto_name, prop_name=self.props.quantity_names,
prop_name=self.props.prop_name
) )
def add_manual_quantity(self): def add_manual_quantity(self):
@@ -439,9 +446,7 @@ class AddCostValue(bpy.types.Operator):
elif self.cost_type == "CATEGORY": elif self.cost_type == "CATEGORY":
category = self.cost_category category = self.cost_category
value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent)) value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=self.file.by_id(self.parent))
ifcopenshell.api.run( ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category})
"cost.edit_cost_value", self.file, cost_value=value, attributes={"Category": category}
)
Data.load(self.file) Data.load(self.file)
return {"FINISHED"} return {"FINISHED"}
@@ -2,6 +2,7 @@ import bpy
import ifcopenshell.api import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.cost.data import Data from ifcopenshell.api.cost.data import Data
from ifcopenshell.api.pset.data import Data as PsetData
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
@@ -17,17 +18,19 @@ from bpy.props import (
quantitytypes_enum = [] quantitytypes_enum = []
quantitynames_enum = []
def purge(): def purge():
global quantitytypes_enum global quantitytypes_enum
global quantitynames_enum
quantitytypes_enum = [] quantitytypes_enum = []
quantitynames_enum = []
def getQuantityTypes(self, context): def getQuantityTypes(self, context):
global quantitytypes_enum global quantitytypes_enum
if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
quantitytypes_enum.clear()
quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")] quantitytypes_enum = [("QTO", "Qto", "Derive quantities from IFC quantity sets")]
quantitytypes_enum.extend( quantitytypes_enum.extend(
[ [
@@ -38,6 +41,21 @@ def getQuantityTypes(self, context):
return quantitytypes_enum 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): def updateCostItemName(self, context):
if self.name == "Unnamed": if self.name == "Unnamed":
return return
@@ -73,8 +91,7 @@ class BIMCostProperties(PropertyGroup):
cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute) cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]") contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]")
quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types") quantity_types: EnumProperty(items=getQuantityTypes, name="Quantity Types")
qto_name: StringProperty(name="Qto Name") quantity_names: EnumProperty(items=getQuantityNames, name="Quantity Names")
prop_name: StringProperty(name="Prop Name")
active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id") active_cost_item_quantity_id: IntProperty(name="Active Cost Item Quantity Id")
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute)
cost_types: EnumProperty( cost_types: EnumProperty(
@@ -97,8 +97,7 @@ class BIM_PT_cost_schedules(Panel):
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.props, "quantity_types", text="") row.prop(self.props, "quantity_types", text="")
if self.props.quantity_types == "QTO": if self.props.quantity_types == "QTO":
row.prop(self.props, "qto_name", text="") row.prop(self.props, "quantity_names", text="")
row.prop(self.props, "prop_name", text="")
op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD") op = row.operator("bim.add_cost_item_quantity", text="", icon="ADD")
op.cost_item = self.props.active_cost_item_id op.cost_item = self.props.active_cost_item_id
op.ifc_class = self.props.quantity_types op.ifc_class = self.props.quantity_types
@@ -254,10 +253,10 @@ class BIM_UL_cost_items(UIList):
oprops = context.active_object.BIMObjectProperties oprops = context.active_object.BIMObjectProperties
row = layout.row(align=True) row = layout.row(align=True)
if oprops.ifc_definition_id in cost_item["Controls"]: 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 op.cost_item = item.ifc_definition_id
else: 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 op.cost_item = item.ifc_definition_id
if props.active_cost_item_id == 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: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file 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(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
@@ -25,7 +25,7 @@ class Usecase:
self.add_quantity_from_qto(relationship.RelatingPropertyDefinition) self.add_quantity_from_qto(relationship.RelatingPropertyDefinition)
def add_quantity_from_qto(self, qto): 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 return
for prop in qto.Quantities: for prop in qto.Quantities:
if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): 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"],
)