- BBIM 4D: features to review and edit Resource productivity data on the fly

- IOS: new resource utilitiy functions
This commit is contained in:
Sigma Dimensions
2023-04-30 15:31:36 +01:00
parent 380eca4382
commit 83344e4f43
7 changed files with 168 additions and 23 deletions
@@ -49,9 +49,12 @@ classes = (
operator.DisableEditingResourceCostValue, operator.DisableEditingResourceCostValue,
operator.CalculateResourceWork, operator.CalculateResourceWork,
operator.ImportResources, operator.ImportResources,
operator.EditProductivityData,
prop.Resource, prop.Resource,
prop.BIMResourceProperties, prop.BIMResourceProperties,
prop.BIMResourceTreeProperties, prop.BIMResourceTreeProperties,
prop.ISODuration,
prop.BIMResourceProductivity,
ui.BIM_PT_resources, ui.BIM_PT_resources,
ui.BIM_UL_resources, ui.BIM_UL_resources,
) )
@@ -60,8 +63,10 @@ classes = (
def register(): def register():
bpy.types.Scene.BIMResourceProperties = bpy.props.PointerProperty(type=prop.BIMResourceProperties) bpy.types.Scene.BIMResourceProperties = bpy.props.PointerProperty(type=prop.BIMResourceProperties)
bpy.types.Scene.BIMResourceTreeProperties = bpy.props.PointerProperty(type=prop.BIMResourceTreeProperties) bpy.types.Scene.BIMResourceTreeProperties = bpy.props.PointerProperty(type=prop.BIMResourceTreeProperties)
bpy.types.Scene.BIMResourceProductivity = bpy.props.PointerProperty(type=prop.BIMResourceProductivity)
def unregister(): def unregister():
del bpy.types.Scene.BIMResourceProperties del bpy.types.Scene.BIMResourceProperties
del bpy.types.Scene.BIMResourceTreeProperties del bpy.types.Scene.BIMResourceTreeProperties
del bpy.types.Scene.BIMResourceProductivity
@@ -52,9 +52,25 @@ class ResourceData:
if resource.BaseQuantity: if resource.BaseQuantity:
base_quantity = resource.BaseQuantity.get_info() base_quantity = resource.BaseQuantity.get_info()
del base_quantity["Unit"] 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 return results
@classmethod
def get_productivity(cls, resource):
return ifcopenshell.util.resource.get_productivity(resource, should_inherit=False)
@classmethod @classmethod
def cost_values(cls): def cost_values(cls):
results = [] results = []
@@ -346,3 +346,13 @@ class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
def _execute(self, context): def _execute(self, context):
core.import_resources(tool.Resource, file_path=self.filepath) 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)
@@ -18,7 +18,9 @@
import bpy import bpy
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.resource
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
import blenderbim.tool as tool
import blenderbim.bim.module.pset.data import blenderbim.bim.module.pset.data
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -71,6 +73,18 @@ def get_quantity_types(self, context):
def update_active_resource_index(self, context): def update_active_resource_index(self, context):
blenderbim.bim.module.pset.data.refresh() 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): class Resource(PropertyGroup):
@@ -113,3 +127,11 @@ class BIMResourceProperties(PropertyGroup):
quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types") quantity_types: EnumProperty(items=get_quantity_types, name="Quantity Types")
is_editing_quantity: BoolProperty(name="Is Editing Quantity") is_editing_quantity: BoolProperty(name="Is Editing Quantity")
quantity_attributes: CollectionProperty(name="Quantity Attributes", type=Attribute) 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")
@@ -65,7 +65,11 @@ class BIM_PT_resources(Panel):
self.props, self.props,
"active_resource_index", "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": if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES":
self.draw_editable_resource_attributes_ui() self.draw_editable_resource_attributes_ui()
elif self.props.active_resource_id and self.props.editing_resource_type == "QUANTITY": 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": elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
self.draw_editable_resource_time_attributes_ui() 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): def draw_resource_operators(self):
row = self.layout.row(align=True) row = self.layout.row(align=True)
op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT") 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 not self.props.active_resource_id:
if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]: if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]:
op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") if resource["Productivity"]:
op.resource = ifc_definition_id 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 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 = row.operator("bim.enable_editing_resource_base_quantity", text="", icon="PROPERTIES")
op.resource = ifc_definition_id op.resource = ifc_definition_id
@@ -204,10 +248,20 @@ class BIM_PT_resources(Panel):
op.parent = parent_id op.parent = parent_id
op.cost_value = cost_value_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): class BIM_UL_resources(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
resource = ResourceData.data["resources"][item.ifc_definition_id]
icon_map = { icon_map = {
"IfcSubContractResource": "TEXT", "IfcSubContractResource": "TEXT",
"IfcCrewResource": "COMMUNITY", "IfcCrewResource": "COMMUNITY",
@@ -217,6 +271,7 @@ class BIM_UL_resources(UIList):
"IfcConstructionProductResource": "PACKAGE", "IfcConstructionProductResource": "PACKAGE",
} }
if item: if item:
resource = ResourceData.data["resources"][item.ifc_definition_id]
props = context.scene.BIMResourceProperties props = context.scene.BIMResourceProperties
row = layout.row(align=True) row = layout.row(align=True)
for i in range(0, item.level_index): for i in range(0, item.level_index):
@@ -67,9 +67,11 @@ def parse_duration_as_blender_props(dt, simplify=True):
def simplify_duration(durations_attributes, duration_type, prop_name): def simplify_duration(durations_attributes, duration_type, prop_name):
for item in durations_attributes: duration_props = None
if item.name == prop_name: for collection in durations_attributes:
duration_props = item if collection.name == prop_name:
duration_props = collection
break
if duration_props and not duration_type or duration_type == "ELAPSEDTIME": if duration_props and not duration_type or duration_type == "ELAPSEDTIME":
duration_string = "P{}Y{}M{}DT{}H{}M{}S".format( duration_string = "P{}Y{}M{}DT{}H{}M{}S".format(
duration_props.years if duration_props.years else 0, 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) hours, seconds = divmod(seconds_left, 3600)
minutes, seconds = divmod(seconds, 60) minutes, seconds = divmod(seconds, 60)
return "P{}Y{}M{}DT{}H{}M{}S".format( duration_string = "P"
int(years), if years > 0:
int(months), duration_string += "{}Y".format(int(years))
int(days), if months > 0:
int(hours), duration_string += "{}M".format(int(months))
int(minutes), if total_days > 0:
int(seconds), 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
@@ -241,6 +241,22 @@ Scenario: Remove Resource Quantity
When I press "bim.remove_resource_quantity(resource={labor_resource})" When I press "bim.remove_resource_quantity(resource={labor_resource})"
Then nothing happens 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 Scenario: Calculate Resource Work
Given an empty IFC project Given an empty IFC project
And I press "bim.add_work_schedule" 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.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(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 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 set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
And I press "bim.edit_task" And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={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 I press "bim.assign_class"
And the object "IfcWall/Cube" is selected And the object "IfcWall/Cube" is selected
And I set "active_object.PsetProperties.qto_name" to "Qto_WallBaseQuantities" And I set "active_object.PsetProperties.qto_name" to "Qto_WallBaseQuantities"
When I press "bim.add_pset(obj='IfcWall/Cube', obj_type='Object')" When I press "bim.add_qto(obj='IfcWall/Cube', obj_type='Object')"
And the variable "qto" is "{ifc}.by_type('IfcElementQuantity')[-1].id()" And I set "active_object.PsetProperties.properties[5].metadata.float_value" to "50.00"
And I press "bim.enable_pset_editing(obj='IfcWall/Cube', obj_type='Object', pset_id={qto})" And I press "bim.edit_pset(obj='IfcWall/Cube', obj_type='Object')"
#And I set "active_object.PsetProperties.properties[2].metadata.float_value" to "3.00" When I press "bim.load_resources"
# TO DO: complete When I press "bim.add_resource(ifc_class="IfcCrewResource")"
Then nothing happens 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 Scenario: Assign Resource