Resource Features :

- Adding/removing and edit productivity data is now easier (+ cleaner UI)
- Pressing calculate schedule work on a parent resource will default to calculating its nested resources schedule work.
- Show derived Schedule Work for a parent resource
- Improve apperance of Resource Tree Structure
This commit is contained in:
Sigma Dimensions (Yass)
2023-08-24 13:43:25 +01:00
parent 8f6932de64
commit fc7459c07f
12 changed files with 137 additions and 66 deletions
@@ -22,6 +22,7 @@ from . import ui, prop, operator
classes = (
operator.AddResource,
operator.AddResourceQuantity,
operator.AddProductivityData,
operator.AssignResource,
operator.CalculateResourceWork,
operator.ConstrainResourceWork,
@@ -63,6 +63,7 @@ class ResourceData:
productivity = cls.get_productivity(resource)
if productivity:
results[resource.id()]["Productivity"] = {
"id": productivity.get("id"),
"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),
@@ -85,8 +86,21 @@ class ResourceData:
results[resource.id()]["ScheduleUsage"] = (
resource.Usage.ScheduleUsage if resource.Usage.ScheduleUsage else None
)
if resource.IsNestedBy:
results[resource.id()]["DerivedScheduleWork"] = cls.sum_person_hours(resource)
return results
@classmethod
def sum_person_hours(cls, resource):
sum = 0
nested_resources = ifcopenshell.util.resource.get_nested_resources(resource)
for nested_resource in nested_resources or []:
if not nested_resource.Usage:
continue
duration = ifcopenshell.util.date.ifc2datetime(nested_resource.Usage.ScheduleWork)
sum += duration.total_seconds() / 3600
return round(float(sum), 2) if sum else 0
@classmethod
def get_resource_benchmarks(cls, resource):
constraints = []
@@ -18,6 +18,7 @@
import bpy
from bpy_extras.io_utils import ImportHelper
from blenderbim.bim.module.resource.ui import draw_productivity_ui
import blenderbim.core.resource as core
import blenderbim.tool as tool
@@ -355,6 +356,16 @@ class ImportResources(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
core.import_resources(tool.Resource, file_path=self.filepath)
class AddProductivityData(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_productivity_data"
bl_description = "Apply"
bl_label = "Add Productivity"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
tool.Ifc.run("pset.add_pset", product=tool.Resource.get_highlighted_resource(), name="EPset_Productivity")
class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_productivity_data"
bl_description = "Apply"
@@ -364,6 +375,12 @@ class EditProductivityData(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
core.edit_productivity_pset(tool.Ifc, tool.Resource)
def draw(self, context):
draw_productivity_ui(self, context)
def invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self, width=600)
class ConstrainResourceWork(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_usage_constraint"
@@ -65,21 +65,22 @@ class BIM_PT_resources(Panel):
self.props,
"active_resource_index",
)
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":
self.draw_editable_resource_quantity_ui()
elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS":
self.draw_editable_resource_costs_ui()
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
self.draw_editable_resource_time_attributes_ui()
if self.props.active_resource_id:
if 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":
self.draw_editable_resource_quantity_ui()
elif self.props.active_resource_id and self.props.editing_resource_type == "COSTS":
self.draw_editable_resource_costs_ui()
elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE":
self.draw_editable_resource_time_attributes_ui()
self.draw_productivity_ui(context)
def draw_productivity_ui(self, context):
row = self.layout.row(align=True)
row.alignment = "RIGHT"
row.prop(self.props, "should_show_resource_tools", text="Resource Tools",icon="RECOVER_LAST")
row.prop(self.props, "should_show_resource_tools", text="Resource Tools", icon="RECOVER_LAST")
if self.props.should_show_resource_tools:
total_resources = len(self.tprops.resources)
if not total_resources or self.props.active_resource_index >= total_resources:
@@ -120,7 +121,12 @@ class BIM_PT_resources(Panel):
row1_col1 = col1.row()
row1_col1.label(text="Schedule Work")
row1col2 = col2.row()
row1col2.label(text=resource.get("ScheduleWork", "-"), icon="TIME")
schedule_work = resource.get("ScheduleWork", None)
derived_schedule_work = resource.get("DerivedScheduleWork", None)
row1col2.label(
text="{}".format(schedule_work) if schedule_work else "*{}".format(derived_schedule_work),
icon="TIME",
)
row1col3 = col3.row()
row1col3.operator("bim.calculate_resource_work", text="", icon="TEMP").resource = ifc_definition_id
@@ -156,6 +162,12 @@ class BIM_PT_resources(Panel):
)
row.alignment = "LEFT"
row.label(text=produtivitiy_rate_message, icon="ARMATURE_DATA")
row.operator("bim.edit_productivity_data", text="", icon="GREASEPENCIL")
op = row.operator("bim.remove_pset", text="", icon="X")
op.pset_id = productivity["id"]
op.obj_type = "Resource"
op.obj = ""
elif parent_productivity:
produtivitiy_rate_message = "Inherited Productivity Rate: {} {} / {}*".format(
parent_productivity["QuantityProduced"],
@@ -163,30 +175,14 @@ class BIM_PT_resources(Panel):
parent_productivity["TimeConsumed"],
)
row.alignment = "LEFT"
row.label(text="Productivity: {}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
row.label(text="{}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
row.operator("bim.add_productivity_data", text="", icon="ADD")
else:
row = self.layout.row(align=True)
row.alignment = "LEFT"
produtivitiy_rate_message = "No productivity data found"
row.label(text="Productivity: {}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
productivity_props = context.scene.BIMResourceProductivity
grid = self.layout.grid_flow(columns=2, even_columns=False, even_rows=False, align=False)
col1 = grid.column(align=False)
col2 = grid.column(align=False)
col1.ui_units_x = 1
col2.ui_units_x = 3
row1_col1 = col1.row()
row1_col1.label(text="Quantity")
row1_col2 = col2.row()
row1_col2.prop(productivity_props, "quantity_produced", text="")
row1_col2.prop(productivity_props, "quantity_produced_name", text="")
row2_col1 = col1.row()
row2_col1.label(text="Time")
row2_col2 = col2.row()
self.draw_duration_property(productivity_props.quantity_consumed, row2_col2)
row3_col2 = col2.row()
row3_col2.alignment = "RIGHT"
row3_col2.operator("bim.edit_productivity_data", text="", icon="CHECKMARK")
row.label(text="{}".format(produtivitiy_rate_message), icon="ARMATURE_DATA")
row.operator("bim.add_productivity_data", text="", icon="ADD")
def draw_resource_operators(self):
row = self.layout.row(align=True)
@@ -231,6 +227,12 @@ class BIM_PT_resources(Panel):
op.resource = ifc_definition_id
row.operator("bim.enable_editing_resource", text="", icon="GREASEPENCIL").resource = ifc_definition_id
row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id
else:
if self.props.editing_resource_type == "ATTRIBUTES":
row.operator("bim.edit_resource", text="", icon="CHECKMARK")
elif self.props.editing_resource_type == "USAGE":
row.operator("bim.edit_resource_time", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_resource", text="", icon="CANCEL")
def draw_editable_resource_attributes_ui(self):
blenderbim.bim.helper.draw_attributes(self.props.resource_attributes, self.layout)
@@ -354,7 +356,7 @@ class BIM_UL_resources(UIList):
else:
row.label(text="", icon="DOT")
row.prop(item, "name", emboss=False, text="", icon=icon_map[resource["type"]])
row.prop(item, "schedule_usage", text="", emboss=False)
row.prop(item, "schedule_usage", text="", emboss=False) if item.schedule_usage else None
if context.active_object and not props.active_resource_id:
row = layout.row(align=True)
if item.ifc_definition_id in ResourceData.data["active_resource_ids"]:
@@ -370,3 +372,29 @@ class BIM_UL_resources(UIList):
elif props.editing_resource_type == "USAGE":
row.operator("bim.edit_resource_time", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_resource", text="", icon="CANCEL")
def draw_productivity_ui(self, context):
def draw_duration_property(duration_props, layout):
for duration_prop in duration_props:
if duration_prop.name == "BaseQuantityConsumed":
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")
productivity_props = context.scene.BIMResourceProductivity
grid = self.layout.grid_flow(columns=2, even_columns=False, even_rows=False, align=False)
col1 = grid.column(align=False)
col2 = grid.column(align=False)
row1_col1 = col1.row()
row1_col1.label(text="Quantity")
row1_col2 = col2.row()
row1_col2.prop(productivity_props, "quantity_produced", text="")
row1_col2.prop(productivity_props, "quantity_produced_name", text="")
row2_col1 = col1.row()
row2_col1.label(text="Time")
row2_col2 = col2.row()
draw_duration_property(productivity_props.quantity_consumed, row2_col2)
+13 -15
View File
@@ -26,8 +26,7 @@ def load_resources(resource):
def add_resource(tool_ifc, resource_tool, ifc_class, parent_resource=None):
tool_ifc.run("resource.add_resource", ifc_class=ifc_class, parent_resource=parent_resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
def load_resource_properties(resource_tool, resource=None):
@@ -56,8 +55,7 @@ def edit_resource(ifc, resource_tool, resource):
def remove_resource(ifc, resource_tool, resource=None):
ifc.run("resource.remove_resource", resource=resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
def enable_editing_resource_time(ifc_tool, resource_tool, resource):
@@ -79,9 +77,13 @@ def disable_editing_resource_time(resource_tool):
def calculate_resource_work(ifc, resource_tool, resource):
ifc.run("resource.calculate_resource_work", resource=resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
if resource_tool.get_task_assignments(resource):
ifc.run("resource.calculate_resource_work", resource=resource)
else:
nested_resources = resource_tool.get_nested_resources(resource)
for nested_resource in nested_resources or []:
ifc.run("resource.calculate_resource_work", resource=nested_resource)
load_resources(resource_tool)
def enable_editing_resource_costs(resource_tool, resource):
@@ -142,20 +144,17 @@ def edit_resource_quantity(resource_tool, ifc, physical_quantity=None):
def import_resources(resource_tool, file_path):
resource_tool.import_resources(file_path)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
def expand_resource(resource_tool, resource):
resource_tool.expand_resource(resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
def contract_resource(resource_tool, resource):
resource_tool.contract_resource(resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
def assign_resource(ifc, spatial, resource=None, products=None):
@@ -220,5 +219,4 @@ def go_to_resource(resource_tool, resource):
def calculate_resource_usage(ifc, resource_tool, resource):
ifc.run("resource.calculate_resource_usage", resource=resource)
resource_tool.load_resources()
resource_tool.load_resource_properties()
load_resources(resource_tool)
+11 -8
View File
@@ -55,12 +55,13 @@ class Resource(blenderbim.core.tool.Resource):
tprops = bpy.context.scene.BIMResourceTreeProperties
tprops.resources.clear()
contracted_resources = json.loads(props.contracted_resources)
props.is_resource_update_enabled = False
for resource in tool.Ifc.get().by_type("IfcResource"):
if not resource.HasContext:
continue
create_new_resource_li(resource, 0)
cls.load_productivity_data()
props.is_resource_update_enabled = True
props.is_editing = True
@classmethod
@@ -71,7 +72,7 @@ class Resource(blenderbim.core.tool.Resource):
for item in tprops.resources:
resource = tool.Ifc.get().by_id(item.ifc_definition_id)
item.name = resource.Name if resource.Name else "Unnamed"
item.schedule_usage = resource.Usage.ScheduleUsage or 1 if resource.Usage else 0
item.schedule_usage = resource.Usage.ScheduleUsage if (resource.Usage and resource.Usage.ScheduleUsage) else 0
props.is_resource_update_enabled = True
@classmethod
@@ -374,13 +375,11 @@ class Resource(blenderbim.core.tool.Resource):
@classmethod
def edit_productivity_pset(cls, resource, attributes):
productivity = cls.get_productivity(resource)
if productivity:
pset = tool.Ifc.get().by_id(productivity["id"])
else:
pset = tool.Ifc.run("pset.add_pset", product=resource, name="EPset_Productivity")
tool.Ifc.run(
if not productivity:
return
return tool.Ifc.run(
"pset.edit_pset",
pset=pset,
pset= tool.Ifc.get().by_id(productivity["id"]),
properties=attributes,
)
@@ -444,3 +443,7 @@ class Resource(blenderbim.core.tool.Resource):
@classmethod
def get_task_assignments(cls, resource):
return ifcopenshell.util.resource.get_task_assignments(resource)
@classmethod
def get_nested_resources(cls, resource):
return ifcopenshell.util.resource.get_nested_resources(resource)
+1 -1
View File
@@ -416,7 +416,7 @@ class Sequence(blenderbim.core.tool.Sequence):
new = props.task_resources.add()
new.ifc_definition_id = resource.id()
new.name = resource.Name or "Unnamed"
new.schedule_usage = resource.Usage.ScheduleUsage or 1 if resource.Usage else 0
new.schedule_usage = resource.Usage.ScheduleUsage or 0 if resource.Usage else 0
@classmethod
def load_resources(cls):
@@ -249,6 +249,7 @@ Scenario: Add Productivity data
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 press "bim.add_productivity_data"
And I set "scene.BIMResourceProperties.should_show_resource_tools" to "True"
And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00"
And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea"
@@ -286,6 +287,7 @@ Scenario: Calculate Resource Work
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 press "bim.add_productivity_data"
And I set "scene.BIMResourceProperties.should_show_resource_tools" to "True"
And I set "scene.BIMResourceProductivity.quantity_produced" to "5.00"
And I set "scene.BIMResourceProductivity.quantity_produced_name" to "GrossSideArea"
@@ -60,14 +60,7 @@ class Usecase:
self.settings = {"resource": resource}
def execute(self):
metrics = ifcopenshell.util.constraint.has_metric_constraints(
self.settings["resource"], "Usage.ScheduleWork"
)
if (
metrics
and metrics[0].ConstraintGrade == "HARD"
and metrics[0].Benchmark == "EQUALTO"
):
if self.has_hard_constraint():
return
amount_worked = ifcopenshell.util.resource.get_resource_required_work(
self.settings["resource"]
@@ -81,3 +74,15 @@ class Usecase:
resource=self.settings["resource"],
)
self.settings["resource"].Usage.ScheduleWork = amount_worked
def has_hard_constraint(self):
metrics = ifcopenshell.util.constraint.has_metric_constraints(
self.settings["resource"], "Usage.ScheduleWork"
)
if (
metrics
and metrics[0].ConstraintGrade == "HARD"
and metrics[0].Benchmark == "EQUALTO"
):
return True
return False
@@ -98,7 +98,7 @@ def readable_ifc_duration(string):
final_string += f"{months} m " if months else ""
final_string += f"{weeks} w " if weeks else ""
final_string += f"{days} d " if days else ""
final_string += f"{hours} h " if hours else ""
final_string += f"{round(float(hours),2)} h " if hours else ""
final_string += f"{minutes} m " if minutes else ""
final_string += f"{seconds} s " if seconds else ""
@@ -112,3 +112,6 @@ def get_resource_required_work(resource):
required_work = total_quantity_to_produce * productivity_ratio
iso_string = f"P{required_work}D"
return iso_string
def get_nested_resources(resource):
return [object for rel in resource.IsNestedBy or [] for object in rel.RelatedObjects]
@@ -248,7 +248,7 @@ def get_task_work_schedule(task):
def get_nested_tasks(task):
return [object for rel in task.IsNestedBy for object in rel.RelatedObjects]
return [object for rel in task.IsNestedBy or [] for object in rel.RelatedObjects]
def get_parent_task(task):