From 4a8f5af7ba306b7443a74b2b70f76ead4a8433b4 Mon Sep 17 00:00:00 2001 From: Sigma Dimensions <79010126+myoualid@users.noreply.github.com> Date: Sun, 30 Apr 2023 15:31:36 +0100 Subject: [PATCH] - BBIM 4D: features to review and edit Resource productivity data on the fly - IOS: new resource utilitiy functions --- .../bim/module/resource/__init__.py | 5 ++ .../blenderbim/bim/module/resource/data.py | 18 +++++- .../bim/module/resource/operator.py | 10 +++ .../blenderbim/bim/module/resource/prop.py | 22 +++++++ .../blenderbim/bim/module/resource/ui.py | 63 +++++++++++++++++-- .../blenderbim/bim/module/sequence/helper.py | 32 ++++++---- .../test/bim/feature/resource.feature | 41 +++++++++--- 7 files changed, 168 insertions(+), 23 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 03cf738203..c91d7b7591 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -49,9 +49,12 @@ classes = ( operator.DisableEditingResourceCostValue, operator.CalculateResourceWork, operator.ImportResources, + operator.EditProductivityData, prop.Resource, prop.BIMResourceProperties, prop.BIMResourceTreeProperties, + prop.ISODuration, + prop.BIMResourceProductivity, ui.BIM_PT_resources, ui.BIM_UL_resources, ) @@ -60,8 +63,10 @@ classes = ( def register(): bpy.types.Scene.BIMResourceProperties = bpy.props.PointerProperty(type=prop.BIMResourceProperties) bpy.types.Scene.BIMResourceTreeProperties = bpy.props.PointerProperty(type=prop.BIMResourceTreeProperties) + bpy.types.Scene.BIMResourceProductivity = bpy.props.PointerProperty(type=prop.BIMResourceProductivity) def unregister(): del bpy.types.Scene.BIMResourceProperties del bpy.types.Scene.BIMResourceTreeProperties + del bpy.types.Scene.BIMResourceProductivity diff --git a/src/blenderbim/blenderbim/bim/module/resource/data.py b/src/blenderbim/blenderbim/bim/module/resource/data.py index 95f1c6886c..f9abe1729f 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/data.py +++ b/src/blenderbim/blenderbim/bim/module/resource/data.py @@ -52,9 +52,25 @@ class ResourceData: if resource.BaseQuantity: base_quantity = resource.BaseQuantity.get_info() del base_quantity["Unit"] - results[resource.id()] = {"type": resource.is_a(), "BaseQuantity": base_quantity} + results[resource.id()] = { + "type": resource.is_a(), + "BaseQuantity": base_quantity, + } + if resource.is_a() in ["IfcLaborResource", "IfcConstructionEquipmentResource"]: + results[resource.id()]["Productivity"] = {} + productivity = cls.get_productivity(resource) + if productivity: + results[resource.id()]["Productivity"] = { + "QuantityProduced": ifcopenshell.util.resource.get_quantity_produced(productivity), + "TimeConsumed": ifcopenshell.util.resource.get_unit_consumed(productivity), + "QuantityProducedName": ifcopenshell.util.resource.get_quantity_produced_name(productivity), + } return results + @classmethod + def get_productivity(cls, resource): + return ifcopenshell.util.resource.get_productivity(resource, should_inherit=False) + @classmethod def cost_values(cls): results = [] diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 1df5400c48..b41bb69c2c 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -346,3 +346,13 @@ class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper): def _execute(self, context): core.import_resources(tool.Resource, file_path=self.filepath) + + +class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.edit_productivity_data" + bl_description = "Apply" + bl_label = "Edit Productivity" + bl_options = {"REGISTER", "UNDO"} + + def _execute(self, context): + core.edit_productivity_pset(tool.Ifc, tool.Resource) diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py index 8b5d9df21c..0d4aca536a 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/prop.py +++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py @@ -18,7 +18,9 @@ import bpy import ifcopenshell.api +import ifcopenshell.util.resource from blenderbim.bim.ifc import IfcStore +import blenderbim.tool as tool import blenderbim.bim.module.pset.data from blenderbim.bim.prop import StrProperty, Attribute from bpy.types import PropertyGroup @@ -71,6 +73,18 @@ def get_quantity_types(self, context): def update_active_resource_index(self, context): blenderbim.bim.module.pset.data.refresh() + if self.should_show_productivity: + tool.Resource.load_productivity_data() + + +class ISODuration(PropertyGroup): + name: StringProperty(name="Name") + years: IntProperty(name="Years", default=0) + months: IntProperty(name="Months", default=0) + days: IntProperty(name="Days", default=0) + hours: IntProperty(name="Hours", default=0) + minutes: IntProperty(name="Minutes", default=0) + seconds: IntProperty(name="Seconds", default=0) class Resource(PropertyGroup): @@ -113,3 +127,11 @@ class BIMResourceProperties(PropertyGroup): quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") is_editing_quantity: BoolProperty(name="Is Editing Quantity") quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) + should_show_productivity: BoolProperty(name="Edit Productivity", update=update_active_resource_index) + + +class BIMResourceProductivity(PropertyGroup): + ifc_definition_id: IntProperty(name="IFC Definition ID") + quantity_consumed: CollectionProperty(name="Duration", type=ISODuration) + quantity_produced: FloatProperty(name="Quantity Produced") + quantity_produced_name: StringProperty(name="Quantity Produced Name") \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index e2c1300afc..433c0e73e1 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -65,7 +65,11 @@ class BIM_PT_resources(Panel): self.props, "active_resource_index", ) - + row = self.layout.row(align=True) + row.alignment = "RIGHT" + row.prop(self.props, "should_show_productivity", icon="RECOVER_LAST") + if self.props.should_show_productivity: + self.draw_productivity_ui(context) if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES": self.draw_editable_resource_attributes_ui() elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY": @@ -75,6 +79,45 @@ class BIM_PT_resources(Panel): elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": self.draw_editable_resource_time_attributes_ui() + def draw_productivity_ui(self, context): + total_resources = len(self.tprops.resources) + if not total_resources or self.props.active_resource_index >= total_resources: + return + + ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id + resource = ResourceData.data["resources"][ifc_definition_id] + + if not resource["type"] in ["IfcConstructionEquipmentResource", "IfcLaborResource"]: + row = self.layout.row(align=True) + row.label(text="Resource type cannot have productivity data", icon="ERROR") + return + + self.productivity_props = context.scene.BIMResourceProductivity + + if resource["Productivity"]: + produtivitiy_rate_message = "Current Rate: {}/{}".format( + resource["Productivity"]["QuantityProduced"], resource["Productivity"]["TimeConsumed"] + ) + row = self.layout.row() + row.alignment = "LEFT" + row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA") + else: + row = self.layout.row(align=True) + row.alignment = "LEFT" + produtivitiy_rate_message = "No productivity data found" + row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA") + + row = self.layout.row(align=True) + row.alignment = "RIGHT" + row.prop(self.productivity_props, "quantity_produced", text="Quantity Produced") + row.prop(self.productivity_props, "quantity_produced_name", text="Quantity Name") + row = self.layout.row() + row.alignment = "RIGHT" + self.draw_duration_property(self.productivity_props.quantity_consumed, row) + row = self.layout.row() + row.alignment = "RIGHT" + row.operator("bim.edit_productivity_data", text="Apply", icon="CHECKMARK") + def draw_resource_operators(self): row = self.layout.row(align=True) op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT") @@ -111,8 +154,9 @@ class BIM_PT_resources(Panel): if not self.props.active_resource_id: if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]: - op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") - op.resource = ifc_definition_id + if resource["Productivity"]: + op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") + op.resource = ifc_definition_id row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = ifc_definition_id op = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES") op.resource = ifc_definition_id @@ -204,10 +248,20 @@ class BIM_PT_resources(Panel): op.parent = parent_id op.cost_value = cost_value_id + def draw_duration_property(self, duration_props, layout): + for duration_prop in duration_props: + if duration_prop.name == "BaseQuantityConsumed": + layout.label(text=duration_prop.name) + layout.prop(duration_prop, "years", text="Y") + layout.prop(duration_prop, "months", text="M") + layout.prop(duration_prop, "days", text="D") + layout.prop(duration_prop, "hours", text="H") + layout.prop(duration_prop, "minutes", text="Min") + layout.prop(duration_prop, "seconds", text="S") + class BIM_UL_resources(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - resource = ResourceData.data["resources"][item.ifc_definition_id] icon_map = { "IfcSubContractResource": "TEXT", "IfcCrewResource": "COMMUNITY", @@ -217,6 +271,7 @@ class BIM_UL_resources(UIList): "IfcConstructionProductResource": "PACKAGE", } if item: + resource = ResourceData.data["resources"][item.ifc_definition_id] props = context.scene.BIMResourceProperties row = layout.row(align=True) for i in range(0, item.level_index): diff --git a/src/blenderbim/blenderbim/bim/module/sequence/helper.py b/src/blenderbim/blenderbim/bim/module/sequence/helper.py index 56d5a95340..dd8e63e9b4 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/helper.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/helper.py @@ -67,9 +67,11 @@ def parse_duration_as_blender_props(dt, simplify=True): def simplify_duration(durations_attributes, duration_type, prop_name): - for item in durations_attributes: - if item.name == prop_name: - duration_props = item + duration_props = None + for collection in durations_attributes: + if collection.name == prop_name: + duration_props = collection + break if duration_props and not duration_type or duration_type == "ELAPSEDTIME": duration_string = "P{}Y{}M{}DT{}H{}M{}S".format( duration_props.years if duration_props.years else 0, @@ -114,11 +116,19 @@ def simplify_duration(durations_attributes, duration_type, prop_name): hours, seconds = divmod(seconds_left, 3600) minutes, seconds = divmod(seconds, 60) - return "P{}Y{}M{}DT{}H{}M{}S".format( - int(years), - int(months), - int(days), - int(hours), - int(minutes), - int(seconds), - ) + duration_string = "P" + if years > 0: + duration_string += "{}Y".format(int(years)) + if months > 0: + duration_string += "{}M".format(int(months)) + if total_days > 0: + duration_string += "{}D".format(int(total_days)) + if hours > 0 or minutes > 0 or seconds > 0: + duration_string += "T" + if hours > 0: + duration_string += "{}H".format(int(hours)) + if minutes > 0: + duration_string += "{}M".format(int(minutes)) + if seconds > 0: + duration_string += "{}S".format(int(seconds)) + return duration_string diff --git a/src/blenderbim/test/bim/feature/resource.feature b/src/blenderbim/test/bim/feature/resource.feature index bc1f2ca203..836639f16f 100644 --- a/src/blenderbim/test/bim/feature/resource.feature +++ b/src/blenderbim/test/bim/feature/resource.feature @@ -241,6 +241,22 @@ Scenario: Remove Resource Quantity When I press "bim.remove_resource_quantity(resource={labor_resource})" Then nothing happens +Scenario: Add Productivity data + Given an empty IFC project + When I press "bim.load_resources" + When I press "bim.add_resource(ifc_class="IfcCrewResource")" + And the variable "crew_resource" is "IfcStore.get_file().by_type('IfcCrewResource')[0].id()" + When I press "bim.add_resource(ifc_class="IfcLaborResource", parent_resource={crew_resource})" + And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()" + And I set "scene.BIMResourceProperties.active_resource_index" to "1" + And I set "scene.BIMResourceProperties.should_show_productivity" to "True" + And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00" + And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea" + And I set "scene.BIMResourceProductivity.quantity_consumed[0].hours" to "5" + When I press "bim.edit_productivity_data()" + And the variable "productivity_data" is "IfcStore.get_file().by_type('IfcPropertySet')[-1].id()" + + Scenario: Calculate Resource Work Given an empty IFC project And I press "bim.add_work_schedule" @@ -248,7 +264,7 @@ Scenario: Calculate Resource Work And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})" And I press "bim.add_summary_task(work_schedule={work_schedule})" And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()" - And I press "bim.enable_editing_task(task={task})" + And I press "bim.enable_editing_task_attributes(task={task})" And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION" And I press "bim.edit_task" And I press "bim.enable_editing_task_time(task={task})" @@ -261,12 +277,23 @@ Scenario: Calculate Resource Work And I press "bim.assign_class" And the object "IfcWall/Cube" is selected And I set "active_object.PsetProperties.qto_name" to "Qto_WallBaseQuantities" - When I press "bim.add_pset(obj='IfcWall/Cube', obj_type='Object')" - And the variable "qto" is "{ifc}.by_type('IfcElementQuantity')[-1].id()" - And I press "bim.enable_pset_editing(obj='IfcWall/Cube', obj_type='Object', pset_id={qto})" - #And I set "active_object.PsetProperties.properties[2].metadata.float_value" to "3.00" - # TO DO: complete - Then nothing happens + When I press "bim.add_qto(obj='IfcWall/Cube', obj_type='Object')" + And I set "active_object.PsetProperties.properties[5].metadata.float_value" to "50.00" + And I press "bim.edit_pset(obj='IfcWall/Cube', obj_type='Object')" + When I press "bim.load_resources" + When I press "bim.add_resource(ifc_class="IfcCrewResource")" + And the variable "crew_resource" is "IfcStore.get_file().by_type('IfcCrewResource')[0].id()" + When I press "bim.add_resource(ifc_class="IfcLaborResource", parent_resource={crew_resource})" + And the variable "labor_resource" is "IfcStore.get_file().by_type('IfcLaborResource')[0].id()" + And I set "scene.BIMResourceProperties.active_resource_index" to "1" + And I set "scene.BIMResourceProperties.should_show_productivity" to "True" + And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00" + And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea" + And I set "scene.BIMResourceProductivity.quantity_consumed[0].hours" to "5" + When I press "bim.edit_productivity_data()" + And the variable "productivity_data" is "IfcStore.get_file().by_type('IfcPropertySet')[-1].id()" + When I press "bim.calculate_resource_work(resource={labor_resource})" + Then nothing happens Scenario: Assign Resource