mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user