From 6899f9fa8fae3c54f9084c81136765b6159a6a19 Mon Sep 17 00:00:00 2001 From: bosonprojets Date: Mon, 9 Aug 2021 03:13:26 +0100 Subject: [PATCH] Feature to enable assigning Construction Resources to Tasks --- .../bim/module/sequence/__init__.py | 3 + .../bim/module/sequence/operator.py | 79 ++++++++++++++++++- .../blenderbim/bim/module/sequence/prop.py | 9 +++ .../blenderbim/bim/module/sequence/ui.py | 21 +++++ .../ifcopenshell/api/resource/add_resource.py | 2 +- .../ifcopenshell/api/resource/data.py | 15 +++- 6 files changed, 125 insertions(+), 4 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 553e9f4d77..1fae8dd723 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -76,6 +76,9 @@ classes = ( operator.AddTaskColumn, operator.RemoveTaskColumn, operator.SetTaskSortColumn, + operator.EnableAssigningResources, + operator.AssignResource, + operator.UnassignResource, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 30ac8f770e..c5edca95b1 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -18,6 +18,7 @@ from dateutil import parser, relativedelta from blenderbim.bim.ifc import IfcStore from bpy_extras.io_utils import ImportHelper from ifcopenshell.api.sequence.data import Data +from ifcopenshell.api.resource.data import Data as ResourceData class AddWorkPlan(bpy.types.Operator): @@ -534,7 +535,7 @@ class EnableEditingTaskTime(bpy.types.Operator): def add_task_time(self): task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task)) - Data.load(IfcStore.get_file()) + Data.load(self.file) return task_time @@ -1958,3 +1959,79 @@ class SetTaskSortColumn(bpy.types.Operator): self.props.sort_column = self.column bpy.ops.bim.enable_editing_tasks(work_schedule=self.props.active_work_schedule_id) return {"FINISHED"} + + +class EnableAssigningResources(bpy.types.Operator): + bl_idname = "bim.enable_assigning_resources" + bl_label = "Enable Assigning Resources To Tasks" + bl_options = {"REGISTER", "UNDO"} + task: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMWorkScheduleProperties + self.props.active_task_id = self.task + self.props.editing_task_type = "RESOURCES" + return {"FINISHED"} + + +class AssignResource(bpy.types.Operator): + bl_idname = "bim.assign_resource" + bl_label = "Assign Resource" + bl_options = {"REGISTER", "UNDO"} + task: bpy.props.IntProperty() + parent_resource: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + resource = ifcopenshell.api.run( + "resource.add_resource", + self.file, + **{ + "parent_resource": self.file.by_id(self.parent_resource), + "ifc_class": self.file.by_id(self.parent_resource).is_a(), + "name": self.file.by_id(self.parent_resource).Name + ": " + self.file.by_id(self.task).Name, + }, + ) + ifcopenshell.api.run( + "sequence.assign_process", + self.file, + **{ + "related_object": resource, + "relating_process": self.file.by_id(self.task), + }, + ) + Data.load(self.file) + ResourceData.load(self.file) + bpy.ops.bim.load_resources() + return {"FINISHED"} + +class UnassignResource(bpy.types.Operator): + bl_idname = "bim.unassign_resource" + bl_label = "Unassign Resource" + bl_options = {"REGISTER", "UNDO"} + task: bpy.props.IntProperty() + resource: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.unassign_process", + self.file, + related_object=self.file.by_id(self.resource), + relating_process=self.file.by_id(self.task), + ) + ifcopenshell.api.run( + "resource.remove_resource", + self.file, + resource=self.file.by_id(self.resource), + ) + Data.load(self.file) + ResourceData.load(self.file) + bpy.ops.bim.load_resources() + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 02e6e6cbde..348306e66c 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -3,6 +3,7 @@ import ifcopenshell.api import ifcopenshell.util.attribute from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.sequence.data import Data +from ifcopenshell.api.resource.data import Data as ResourceData from blenderbim.bim.prop import StrProperty, Attribute from dateutil import parser from bpy.types import PropertyGroup @@ -208,6 +209,13 @@ def updateVisualisationStartFinish(self, context, startfinish): setattr(self, startfinish, canonical_value) +def getResources(self, context): + self.file = IfcStore.get_file() + return [(str(k), v["Name"], "") for k, v in ResourceData.resources.items() + if self.file.by_id(k).Nests and self.file.by_id(k).Nests[0].RelatingObject.HasContext + ] + + class Task(PropertyGroup): name: StringProperty(name="Name", update=updateTaskName) identification: StringProperty(name="Identification", update=updateTaskIdentification) @@ -297,6 +305,7 @@ class BIMWorkScheduleProperties(PropertyGroup): name="Speed Type", default="FRAME_SPEED", ) + resources: EnumProperty(items=getResources, name="Resources") class BIMTaskTreeProperties(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 7c6148731c..d7dd580dd7 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -3,6 +3,7 @@ import blenderbim.bim.helper from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.sequence.data import Data +from ifcopenshell.api.resource.data import Data as ResourceData import blenderbim.bim.module.sequence.helper as helper from datetime import datetime @@ -214,6 +215,8 @@ class BIM_PT_work_schedules(Panel): self.draw_editable_task_sequence_ui() elif self.props.active_task_time_id and self.props.editing_task_type == "TASKTIME": self.draw_editable_task_time_attributes_ui() + elif self.props.active_task_id and self.props.editing_task_type == "RESOURCES": + self.draw_editable_task_resource_ui() def draw_editable_task_sequence_ui(self): task = Data.tasks[self.props.active_task_id] @@ -287,6 +290,23 @@ class BIM_PT_work_schedules(Panel): def draw_editable_task_time_attributes_ui(self): blenderbim.bim.helper.draw_attributes(self.props.task_time_attributes, self.layout) + def draw_editable_task_resource_ui(self): + row = self.layout.row(align=True) + row.prop(self.props, "resources", text="") + op = row.operator("bim.assign_resource", text="", icon="ADD") + op.parent_resource = int(self.props.resources) + op.task = self.props.active_task_id + task = Data.tasks[self.props.active_task_id] + ResourceData.load(IfcStore.get_file()) + + for related_obect_id in task["OperatesOn"]: + resource = ResourceData.resources[related_obect_id] + row = self.layout.row(align=True) + row.label(text=resource["Name"], icon="COMMUNITY") + op = row.operator("bim.unassign_resource", text="", icon="X") + op.task = self.props.active_task_id + op.resource = related_obect_id + class BIM_UL_task_columns(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): @@ -416,6 +436,7 @@ class BIM_UL_tasks(UIList): row.operator( "bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO" ).task = item.ifc_definition_id + row.operator("bim.enable_assigning_resources", text="", icon="COMMUNITY").task = item.ifc_definition_id row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py index 3ef741ae6c..63c9be21c7 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource.py @@ -19,7 +19,7 @@ class Usecase: self.file, ifc_class=self.settings["ifc_class"], predefined_type=self.settings["predefined_type"], - name=self.settings["name"], + name=self.settings["name"] or "Unammed", ) # TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ? # https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550 diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py index eec9223e6f..af068a88a4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py @@ -4,20 +4,31 @@ class Data: @classmethod def purge(cls): + cls.is_loaded = False cls.resources = {} @classmethod def load(cls, file): + cls._file = file + if not cls._file: + return + cls.load_resources() + cls.is_loaded=True + + @classmethod + def load_resources(cls): cls.resources = {} - for resource in file.by_type("IfcResource"): + for resource in cls._file.by_type("IfcResource"): data = resource.get_info() del data["OwnerHistory"] data["IsNestedBy"] = [] for rel in resource.IsNestedBy: [data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects] + data["Nests"] = [] + for rel in resource.Nests: + [data["Nests"].append(rel.RelatingObject.id())] data["ResourceOf"] = [] for rel in resource.ResourceOf: [data["ResourceOf"].append(o.id()) for o in rel.RelatedObjects] data["HasContext"] = resource.HasContext[0].RelatingContext.id() if resource.HasContext else None cls.resources[resource.id()] = data - cls.is_loaded=True