From 29b33661d787e9d94229288d15efaa3d89472dbb Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 15 Apr 2021 10:38:45 +1000 Subject: [PATCH] You can now assign summary tasks to a work schedule. Thanks myoualid! --- .../bim/module/sequence/__init__.py | 7 +-- .../bim/module/sequence/operator.py | 25 ++++++-- .../blenderbim/bim/module/sequence/prop.py | 8 +-- .../blenderbim/bim/module/sequence/ui.py | 57 ++++++------------- .../api/control/assign_control.py | 52 +++++++++++++++++ .../ifcopenshell/api/sequence/add_task.py | 11 +++- .../ifcopenshell/api/sequence/data.py | 5 ++ 7 files changed, 107 insertions(+), 58 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 16af753c26..27102da316 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -25,32 +25,29 @@ classes = ( operator.RemoveWorkCalendar, operator.EnableEditingWorkCalendar, operator.DisableEditingWorkCalendar, + operator.AddTask, prop.WorkPlan, prop.BIMWorkPlanProperties, + prop.Task, prop.WorkSchedule, prop.BIMWorkScheduleProperties, prop.WorkCalendar, prop.BIMWorkCalendarProperties, - prop.Task, - prop.BIMTaskProperties, ui.BIM_PT_work_plans, ui.BIM_UL_work_plans, ui.BIM_PT_work_schedules, ui.BIM_UL_work_schedules, ui.BIM_PT_work_calendars, ui.BIM_UL_work_calendars, - ui.BIM_PT_tasks, ui.BIM_UL_tasks, ) def register(): - bpy.types.Scene.BIMTaskProperties = bpy.props.PointerProperty(type=prop.BIMTaskProperties) bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties) bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties) bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties) def unregister(): - del bpy.types.Scene.BIMTaskProperties del bpy.types.Scene.BIMWorkPlanProperties del bpy.types.Scene.BIMWorkScheduleProperties del bpy.types.Scene.BIMWorkCalendarProperties diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index f78fbf5513..1b0ecad74a 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -231,6 +231,7 @@ class EnableEditingWorkSchedule(bpy.types.Operator): if data[attribute.name()]: new.enum_value = data[attribute.name()] props.active_work_schedule_id = self.work_schedule + bpy.ops.bim.load_tasks(work_schedule=self.work_schedule) return {"FINISHED"} @@ -364,17 +365,18 @@ class DisableEditingWorkCalendar(bpy.types.Operator): class LoadTasks(bpy.types.Operator): bl_idname = "bim.load_tasks" bl_label = "Load Tasks" + work_schedule: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMTaskProperties + props = context.scene.BIMWorkScheduleProperties while len(props.tasks) > 0: props.tasks.remove(0) - for ifc_definition_id, task in Data.tasks.items(): + for ifc_definition_id in Data.work_schedules[self.work_schedule]["RelatedObjects"]: + task = Data.tasks[ifc_definition_id] new = props.tasks.add() new.ifc_definition_id = ifc_definition_id - new.name = task["Name"] + new.name = task["Name"] or "Unnamed" new.identification = task["Identification"] - props.is_editing = True return {"FINISHED"} @@ -385,3 +387,18 @@ class DisableTaskEditingUI(bpy.types.Operator): def execute(self, context): context.scene.BIMTaskProperties.is_editing = False return {"FINISHED"} + + +class AddTask(bpy.types.Operator): + bl_idname = "bim.add_task" + bl_label = "Add Task" + work_schedule: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + task = ifcopenshell.api.run("sequence.add_task", self.file) + control = self.file.by_id(self.work_schedule) + ifcopenshell.api.run("control.assign_control", self.file, related_object=task, relating_control=control) + Data.load(self.file) + bpy.ops.bim.enable_editing_work_schedule(work_schedule = self.work_schedule) + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 29a1af6b14..a21ec53a48 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -19,12 +19,6 @@ class Task(PropertyGroup): ifc_definition_id: IntProperty(name="IFC Definition ID") -class BIMTaskProperties(PropertyGroup): - is_editing: BoolProperty(name="Is Editing", default=False) - tasks: CollectionProperty(name="Tasks", type=Task) - active_task_index: IntProperty(name="Active Task Index") - - class WorkPlan(PropertyGroup): name: StringProperty(name="Name") ifc_definition_id: IntProperty(name="IFC Definition ID") @@ -49,6 +43,8 @@ class BIMWorkScheduleProperties(PropertyGroup): work_schedules: CollectionProperty(name="Work Schedules", type=WorkSchedule) active_work_schedule_index: IntProperty(name="Active Work Schedules Index") active_work_schedule_id: IntProperty(name="Active Work Schedules Id") + tasks: CollectionProperty(name="Tasks", type=Task) + active_task_index: IntProperty(name="Active Task Index") class WorkCalendar(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 9ed96cef3c..d0eba710f5 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -114,6 +114,22 @@ class BIM_PT_work_schedules(Panel): if attribute.is_optional: row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") + self.draw_task_ui(context) + + def draw_task_ui(self, context): + row = self.layout.row(align=True) + row.label(text="{} Tasks Found".format(len(Data.tasks)), icon="ACTION") + row.operator("bim.add_task", text="", icon="ADD").work_schedule = self.props.active_work_schedule_id + + self.layout.template_list( + "BIM_UL_tasks", + "", + self.props, + "tasks", + self.props, + "active_task_index", + ) + class BIM_UL_work_schedules(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): @@ -195,47 +211,6 @@ class BIM_UL_work_calendars(UIList): row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id -class BIM_PT_tasks(Panel): - bl_label = "IFC Tasks" - bl_idname = "BIM_PT_tasks" - bl_options = {"DEFAULT_CLOSED"} - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "scene" - - @classmethod - def poll(cls, context): - return IfcStore.get_file() - - def draw(self, context): - if not Data.is_loaded: - Data.load(IfcStore.get_file()) - self.props = context.scene.BIMTaskProperties - - row = self.layout.row(align=True) - row.label(text="{} Tasks Found".format(len(Data.tasks)), icon="ACTION") - if self.props.is_editing: - row.operator("bim.disable_task_editing_ui", text="", icon="CHECKMARK") - else: - row.operator("bim.load_tasks", text="", icon="GREASEPENCIL") - - if self.props.is_editing: - self.layout.template_list( - "BIM_UL_tasks", - "", - self.props, - "tasks", - self.props, - "active_task_index", - ) - - if self.props.active_task_index: - self.draw_editable_ui(context) - - def draw_editable_ui(self, context): - pass - - class BIM_UL_tasks(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py new file mode 100644 index 0000000000..d684f63f44 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -0,0 +1,52 @@ +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "related_object": None, + "relating_control": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + has_assignments = None + if self.settings["related_object"].HasAssignments: + for assignement in self.settings["related_object"].HasAssignments: + if assignement.is_a("IfclRelAssignsToControl"): + has_assignments = assignement + + controls = None + for rel in self.settings["relating_control"].Controls: + if rel.is_a("IfcRelAssignsToControl"): + controls = rel + break + if has_assignments and has_assignments == controls: + return + if has_assignments: + related_objects = list(has_assignments.RelatedObjects) + related_objects.remove(self.settings["related_object"]) + if related_objects: + has_assignments.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_assignments}) + else: + self.file.remove(has_assignments) + if controls: + related_objects = list(controls.RelatedObjects) + related_objects.append(self.settings["related_object"]) + controls.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": controls}) + else: + controls = self.file.create_entity( + "IfcRelAssignsToControl", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), + "RelatedObjects": [self.settings["related_object"]], + "RelatingControl": self.settings["relating_control"], + } + ) + return controls diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py index c4b7a8061c..e27acee45a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task.py @@ -4,7 +4,14 @@ import ifcopenshell.api class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {"name": None, "predefined_type": "NOTDEFINED", "is_milestone":False, "identification": "none", "predecessor_to":None, "successor_from":None} + self.settings = { + "name": None, + "predefined_type": "NOTDEFINED", + "is_milestone": False, + "identification": "none", + "predecessor_to": None, + "successor_from": None, + } for key, value in settings.items(): self.settings[key] = value @@ -17,4 +24,4 @@ class Usecase: name=self.settings["name"], ) task.IsMilestone = self.settings["is_milestone"] - return task \ No newline at end of file + return task diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py index 501af373c4..edfa8cbe45 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py @@ -52,6 +52,11 @@ class Data: data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]) if data["FinishTime"]: data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]) + data["RelatedObjects"] = [] + for rel in work_schedule.Controls: + for obj in rel.RelatedObjects: + if obj.is_a("IfcTask"): + data["RelatedObjects"].append(obj.id()) cls.work_schedules[work_schedule.id()] = data @classmethod