diff --git a/src/blenderbim/blenderbim/bim/ifc.py b/src/blenderbim/blenderbim/bim/ifc.py index e779befc27..c5d9f65d78 100644 --- a/src/blenderbim/blenderbim/bim/ifc.py +++ b/src/blenderbim/blenderbim/bim/ifc.py @@ -183,7 +183,7 @@ class IfcStore: def commit_link_element(data): obj = bpy.data.objects.get(data["obj"]) IfcStore.id_map[data["id"]] = obj - if data["guid"]: + if "guid" in data: IfcStore.guid_map[data["guid"]] = obj blenderbim.bim.handler.subscribe_to(obj, "mode", blenderbim.bim.handler.mode_callback) blenderbim.bim.handler.subscribe_to(obj, "name", blenderbim.bim.handler.name_callback) diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 96bd5cd595..a23f8caa23 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -637,10 +637,10 @@ class EnableEditingCostItemValue(bpy.types.Operator): data = Data.cost_values[self.cost_value] blenderbim.bim.helper.import_attributes( - data["type"], - self.props.cost_value_attributes, - data, - lambda name, prop, data: self.import_attributes(name, prop, data, context) + data["type"], + self.props.cost_value_attributes, + data, + lambda name, prop, data: self.import_attributes(name, prop, data, context), ) return {"FINISHED"} @@ -715,8 +715,8 @@ class EditCostValue(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMCostProperties attributes = blenderbim.bim.helper.export_attributes( - props.cost_value_attributes, - lambda attributes, prop: self.export_attributes(attributes, prop, context)) + props.cost_value_attributes, lambda attributes, prop: self.export_attributes(attributes, prop, context) + ) self.file = IfcStore.get_file() ifcopenshell.api.run( "cost.edit_cost_value", diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index 94d05993b2..6530781763 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -79,7 +79,7 @@ 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): +def get_quantity_types(self, context): global quantitytypes_enum if len(quantitytypes_enum) == 0 and IfcStore.get_schema(): quantitytypes_enum.extend( @@ -91,7 +91,7 @@ def getQuantityTypes(self, context): return quantitytypes_enum -def getProductQuantityNames(self, context): +def get_product_quantity_names(self, context): global productquantitynames_enum global productquantitynames_count ifc_file = IfcStore.get_file() @@ -115,7 +115,7 @@ def getProductQuantityNames(self, context): return productquantitynames_enum -def getProcessQuantityNames(self, context): +def get_process_quantity_names(self, context): global processquantitynames_enum global processquantitynames_id ifc_file = IfcStore.get_file() @@ -137,7 +137,7 @@ def getProcessQuantityNames(self, context): return processquantitynames_enum -def getResourceQuantityNames(self, context): +def get_resource_quantity_names(self, context): global resourcequantitynames_enum global resourcequantitynames_id ifc_file = IfcStore.get_file() @@ -166,7 +166,7 @@ def update_active_cost_item_index(self, context): bpy.ops.bim.load_cost_item_quantities() -def updateCostItemIdentification(self, context): +def update_cost_item_identification(self, context): props = context.scene.BIMCostProperties if not props.is_cost_update_enabled or self.identification == "XXX": return @@ -182,7 +182,7 @@ def updateCostItemIdentification(self, context): attribute.string_value = self.identification -def updateCostItemName(self, context): +def update_cost_item_name(self, context): props = context.scene.BIMCostProperties if not props.is_cost_update_enabled or self.name == "Unnamed": return @@ -199,8 +199,8 @@ def updateCostItemName(self, context): class CostItem(PropertyGroup): - name: StringProperty(name="Name", update=updateCostItemName) - identification: StringProperty(name="Identification", update=updateCostItemIdentification) + name: StringProperty(name="Name", update=update_cost_item_name) + identification: StringProperty(name="Identification", update=update_cost_item_identification) ifc_definition_id: IntProperty(name="IFC Definition ID") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") @@ -229,10 +229,10 @@ class BIMCostProperties(PropertyGroup): active_cost_item_index: IntProperty(name="Active Cost Item Index", update=update_active_cost_item_index) 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") - product_quantity_names: EnumProperty(items=getProductQuantityNames, name="Product Quantity Names") - process_quantity_names: EnumProperty(items=getProcessQuantityNames, name="Process Quantity Names") - resource_quantity_names: EnumProperty(items=getResourceQuantityNames, name="Resource Quantity Names") + quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") + product_quantity_names: EnumProperty(items=get_product_quantity_names, name="Product Quantity Names") + process_quantity_names: EnumProperty(items=get_process_quantity_names, name="Process Quantity Names") + resource_quantity_names: EnumProperty(items=get_resource_quantity_names, name="Resource 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( diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 12d5deaecc..18ea7cd3f6 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -373,11 +373,29 @@ class BIM_PT_cost_item_quantities(Panel): # Column1 col = grid.column() + has_quantity_names = CostProp.get_product_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Elements") op = row2.operator("bim.select_cost_item_products", icon="RESTRICT_SELECT_OFF", text="") op.cost_item = cost_item.ifc_definition_id + if context.selected_objects: + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "PRODUCT" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.product_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "PRODUCT" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + + op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE") + op.cost_item = cost_item.ifc_definition_id + op.related_object = 0 + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -388,23 +406,32 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_product_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "product_quantity_names", text="") - op = row2.operator("bim.unassign_cost_item_quantity", text="", icon="REMOVE") - op.cost_item = cost_item.ifc_definition_id - op.related_object = 0 - if CostProp.productquantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "PRODUCT" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.product_quantity_names + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "product_quantity_names", text="") # Column2 col = grid.column() + has_quantity_names = CostProp.get_process_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Tasks") + tprops = context.scene.BIMTaskTreeProperties + wprops = context.scene.BIMWorkScheduleProperties + if tprops.tasks and wprops.active_task_index < len(tprops.tasks): + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "PROCESS" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.process_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "PROCESS" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -415,20 +442,32 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_process_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "process_quantity_names", text="") - if CostProp.processquantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "PROCESS" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.process_quantity_names + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "process_quantity_names", text="") # Column3 col = grid.column() + has_quantity_names = CostProp.get_resource_quantity_names(self, context) + row2 = col.row(align=True) row2.label(text="Resources") + rtprops = context.scene.BIMResourceTreeProperties + rprops = context.scene.BIMResourceProperties + if rtprops.resources and rprops.active_resource_index < len(rtprops.resources): + if has_quantity_names: + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") + op.related_object_type = "RESOURCE" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = self.props.resource_quantity_names + + op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") + op.related_object_type = "RESOURCE" + op.cost_item = cost_item.ifc_definition_id + op.prop_name = "" + row2 = col.row() row2.template_list( "BIM_UL_cost_item_quantities", @@ -439,14 +478,9 @@ class BIM_PT_cost_item_quantities(Panel): "active_cost_item_resource_index", ) - row2 = col.row(align=True) - row2.prop(self.props, "resource_quantity_names", text="") - if CostProp.resourcequantitynames_enum: - op = row2.operator("bim.assign_cost_item_quantity", text="", icon="ADD") - op.related_object_type = "RESOURCE" - op.cost_item = cost_item.ifc_definition_id - op.prop_name = self.props.resource_quantity_names - + if has_quantity_names: + row2 = col.row() + row2.prop(self.props, "resource_quantity_names", text="") class BIM_PT_cost_item_rates(Panel): bl_label = "IFC Cost Item Rates" diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py index dff2d825a0..744409687d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/add_cost_item_quantity.py @@ -14,8 +14,10 @@ class Usecase: # This is a bold assumption # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 if self.settings["ifc_class"] == "IfcQuantityCount" and self.settings["cost_item"].Controls: + count = 0 for rel in self.settings["cost_item"].Controls: - quantity[3] += len(rel.RelatedObjects) + count += len(rel.RelatedObjects) + quantity[3] = count quantities = list(self.settings["cost_item"].CostQuantities or []) quantities.append(quantity) self.settings["cost_item"].CostQuantities = quantities diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index 21f4447383..41518aa011 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -9,7 +9,8 @@ class Usecase: self.settings[key] = value def execute(self): - self.quantities = set(self.settings["cost_item"].CostQuantities or []) + if self.settings["prop_name"]: + self.quantities = set(self.settings["cost_item"].CostQuantities or []) for product in self.settings["products"]: ifcopenshell.api.run( "control.assign_control", @@ -17,8 +18,12 @@ class Usecase: related_object=product, relating_control=self.settings["cost_item"], ) - self.add_quantity_from_related_object(product) - self.settings["cost_item"].CostQuantities = list(self.quantities) + if self.settings["prop_name"]: + self.add_quantity_from_related_object(product) + if self.settings["prop_name"]: + self.settings["cost_item"].CostQuantities = list(self.quantities) + else: + self.update_cost_item_count() def add_quantity_from_related_object(self, element): if not element.is_a("IfcObject"): @@ -33,3 +38,21 @@ class Usecase: for prop in qto.Quantities: if prop.is_a("IfcPhysicalSimpleQuantity") and prop.Name.lower() == self.settings["prop_name"].lower(): self.quantities.add(prop) + + def update_cost_item_count(self): + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if not self.settings["cost_item"].CostQuantities: + return ifcopenshell.api.run( + "cost.add_cost_item_quantity", + self.file, + cost_item=self.settings["cost_item"], + ifc_class="IfcQuantityCount", + ) + if len(self.settings["cost_item"].CostQuantities) == 1: + quantity = self.settings["cost_item"].CostQuantities[0] + if quantity.is_a("IfcQuantityCount"): + count = 0 + for rel in self.settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py index ec652a0e10..ed7b555d77 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/unassign_cost_item_quantity.py @@ -19,7 +19,6 @@ class Usecase: 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", @@ -27,3 +26,15 @@ class Usecase: related_object=product, relating_control=self.settings["cost_item"], ) + self.update_cost_item_count() + + def update_cost_item_count(self): + # This is a bold assumption + # https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564 + if len(self.settings["cost_item"].CostQuantities) == 1: + quantity = self.settings["cost_item"].CostQuantities[0] + if quantity.is_a("IfcQuantityCount"): + count = 0 + for rel in self.settings["cost_item"].Controls: + count += len(rel.RelatedObjects) + quantity[3] = count