From f4b93ef24390b1b4bc5e4b68d20f2a2885f9777b Mon Sep 17 00:00:00 2001 From: Sigma Dimensions <79010126+myoualid@users.noreply.github.com> Date: Sun, 12 Feb 2023 00:37:37 +0100 Subject: [PATCH] You can now duplicate tasks --- .../bim/module/sequence/__init__.py | 1 + .../bim/module/sequence/operator.py | 9 +- .../blenderbim/bim/module/sequence/ui.py | 1 + .../api/sequence/duplicate_task.py | 142 ++++++++++++++++++ 4 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 72ee2568c6..7a84e51b1d 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -45,6 +45,7 @@ classes = ( operator.CalculateTaskDuration, operator.ContractTask, operator.CopyTaskAttribute, + operator.CopyTask, operator.DisableEditingSequence, operator.DisableEditingTask, operator.DisableEditingTaskTime, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index d15b7a6972..e55554e3fb 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -1268,12 +1268,11 @@ class DisableEditingTaskAnimationColors(bpy.types.Operator): core.disable_editing_task_animation_colors(tool.Sequence) return {"FINISHED"} -class CopyTask(bpy.types.Operator): - bl_idname = "bim.copy_task" +class CopyTask(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.duplicate_task" bl_label = "Copy Task" bl_options = {"REGISTER", "UNDO"} task: bpy.props.IntProperty() - def execute(self, context): - core.copy_task(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task)) - return {"FINISHED"} + def _execute(self, context): + core.duplicate_task(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task)) \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index f84fd9ce92..6f6bc44526 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -184,6 +184,7 @@ class BIM_PT_work_schedules(Panel): row.operator("bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO").task = ifc_definition_id row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = ifc_definition_id row.operator("bim.add_task", text="", icon="ADD").task = ifc_definition_id + row.operator("bim.duplicate_task", text="", icon="DUPLICATE").task = ifc_definition_id row.operator("bim.remove_task", text="", icon="X").task = ifc_definition_id def draw_column_ui(self): diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py new file mode 100644 index 0000000000..186f888da3 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/duplicate_task.py @@ -0,0 +1,142 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021-2022 Dion Moult , Yassine Oualid +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# IfcOpenShell is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell +import ifcopenshell.util.system +import ifcopenshell.util.element + + +class Usecase: + def __init__(self, file, task=None): + """Duplicates a task in the project + + The following relationships are also duplicated: + + * The copy will have the same attributes and property sets as the original task + * The copy will be assigned to the parent task or work schedule + * The copy will have duplicated nested tasks + + :param task: The task to be duplicated + :type task: ifcopenshell.entity_instance.entity_instance + :return: The duplicated task + :rtype: ifcopenshell.entity_instance.entity_instance + + Example: + .. code:: python + + # We have a task + original_task = Task(name="Design new feature", deadline="2023-03-01") + + # And now we have two + duplicated_task = project.duplicate_task(original_task) + """ + self.file = file + self.settings = {"task": task} + + def execute(self): + result = ifcopenshell.util.element.copy(self.file, self.settings["task"]) + self.copy_indirect_attributes(self.settings["task"], result) + return result + + def copy_sequence_relationship(self, original_tasks, duplicated_tasks): + for i, original_task in enumerate(original_tasks): + for inverse in self.file.get_inverse(original_task): + if inverse.is_a("IfcRelSequence") and ( + inverse.RelatingProcess == original_task + or inverse.RelatedProcess == original_task + ): + original_task_index = original_tasks.index(original_task) + duplicated_task = duplicated_tasks[original_task_index] + relating_process, related_process = None, None + if inverse.RelatingProcess == original_task: + relating_process = duplicated_task + else: + related_process = duplicated_task + if inverse.RelatedProcess in original_tasks: + related_process_index = original_tasks.index( + inverse.RelatedProcess + ) + related_process = duplicated_tasks[related_process_index] + if inverse.RelatingProcess in original_tasks: + relating_process_index = original_tasks.index( + inverse.RelatingProcess + ) + relating_process = duplicated_tasks[relating_process_index] + if relating_process and related_process: + rel = ifcopenshell.api.run( + "sequence.assign_sequence", + self.file, + relating_process=relating_process, + related_process=related_process, + ) + if inverse.TimeLag: + ifcopenshell.api.run( + "sequence.assign_lag_time", + self.file, + sequence=rel, + lag_time=inverse.TimeLag.LagValue, + duration_type=inverse.TimeLag.DurationType, + ) + + def copy_indirect_attributes(self, from_element, to_element): + for inverse in self.file.get_inverse(from_element): + if inverse.is_a("IfcRelDefinesByProperties"): + inverse = ifcopenshell.util.element.copy(self.file, inverse) + inverse.RelatedObjects = [to_element] + pset = ifcopenshell.util.element.copy_deep( + self.file, inverse.RelatingPropertyDefinition + ) + inverse.RelatingPropertyDefinition = pset + elif inverse.is_a("IfcRelNests") and inverse.RelatingObject == from_element: + nested_tasks = [e for e in inverse.RelatedObjects if e.is_a("IfcTask")] + if nested_tasks: + new_tasks = [ + ifcopenshell.api.run( + "sequence.duplicate_task", self.file, task=t + ) + for t in nested_tasks + ] + inverse = ifcopenshell.util.element.copy(self.file, inverse) + inverse.RelatingObject = to_element + inverse.RelatedObjects = new_tasks + for task in new_tasks: + ifcopenshell.api.run( + "nest.unassign_object", self.file, related_object=task + ) + rel = ifcopenshell.api.run( + "nest.assign_object", + self.file, + related_object=task, + relating_object=to_element, + ) + self.copy_sequence_relationship(nested_tasks, new_tasks) + + elif inverse.is_a("IfcRelSequence") and ( + inverse.RelatingProcess == from_element + or inverse.RelatedProcess == from_element + ): + continue + else: + for i, value in enumerate(inverse): + if value == from_element: + new_inverse = ifcopenshell.util.element.copy(self.file, inverse) + new_inverse[i] = to_element + elif isinstance(value, (tuple, list)) and from_element in value: + new_value = list(value) + new_value.append(to_element) + inverse[i] = new_value