diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 662fa5b2d8..dd24137729 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -77,6 +77,7 @@ classes = ( operator.ExportMSP, operator.ExportP6, operator.GenerateGanttChart, + operator.GuessDateRange, operator.ImportMSP, operator.ImportP6, operator.ImportP6XER, @@ -97,6 +98,7 @@ classes = ( operator.SelectTaskRelatedProducts, operator.SelectTaskRelatedInputs, operator.SetTaskSortColumn, + operator.SetupDefaultTaskColumns, operator.UnassignLagTime, operator.UnassignPredecessor, operator.UnassignProcess, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/helper.py b/src/blenderbim/blenderbim/bim/module/sequence/helper.py index c14fda3e6f..8dbca96b8d 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/helper.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/helper.py @@ -71,7 +71,6 @@ def get_nested_tasks(task): def get_parent_task(task): return task.Nests[0].RelatingObject if task.Nests and task.Nests[0].RelatingObject.is_a("IfcTask") else None - def get_task_work_schedule(task): parent_task = get_parent_task(task) if parent_task: @@ -95,11 +94,44 @@ def get_work_schedule_tasks(work_schedule): for root_task in get_root_tasks(work_schedule): nested_tasks = get_all_nested_tasks(root_task) tasks.extend(nested_tasks) - return tasks def get_root_tasks(work_schedule): return [obj for rel in work_schedule.Controls for obj in rel.RelatedObjects if obj.is_a("IfcTask")] def get_root_tasks_ids(work_schedule): - return [obj.id() for rel in work_schedule.Controls for obj in rel.RelatedObjects if obj.is_a("IfcTask")] \ No newline at end of file + return [obj.id() for rel in work_schedule.Controls for obj in rel.RelatedObjects if obj.is_a("IfcTask")] + +def guess_date_range(work_schedule): + earliest = None + latest = None + root_tasks = get_root_tasks(work_schedule) + tasks_with_assignements = [] + for task in root_tasks: + if has_task_outputs(task): + tasks_with_assignements.append(task) + for sub_task in get_all_nested_tasks(task): + if has_task_outputs(sub_task): + tasks_with_assignements.append(sub_task) + + for task in tasks_with_assignements: + derived_start = derive_date(task, "ScheduleStart", is_earliest=True) + derived_finish = derive_date(task, "ScheduleFinish", is_latest=True) + if derived_start and (not earliest or derived_start < earliest): + earliest = derived_start + if derived_finish and (not latest or derived_finish > latest): + latest = derived_finish + return earliest, latest + +def get_direct_task_outputs(task): + return [rel.RelatingProduct for rel in task.HasAssignments if rel.is_a("IfcRelAssignsToProduct")] + +def get_task_outputs(task, is_deep=False): + if not is_deep: + return get_direct_task_outputs(task) + else: + nested_tasks = get_all_nested_tasks(task) + return [output for nested_task in nested_tasks for output in get_direct_task_outputs(nested_task)] + +def has_task_outputs(task): + return len(get_task_outputs(task)) > 0 \ No newline at end of file diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index e3e515a732..6b70716853 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -1086,6 +1086,15 @@ class VisualiseWorkScheduleDate(bpy.types.Operator): self.completed.update(products) +class GuessDateRange(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.guess_date_range" + bl_label = "Guess Work Schedule Date Range" + bl_options = {"REGISTER", "UNDO"} + work_schedule: bpy.props.IntProperty() + + def _execute(self, context): + core.guess_date_range(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule)) + class VisualiseWorkScheduleDateRange(bpy.types.Operator): bl_idname = "bim.visualise_work_schedule_date_range" bl_label = "Visualise Work Schedule Date Range" @@ -1452,6 +1461,14 @@ class AddTaskColumn(bpy.types.Operator): core.add_task_column(tool.Sequence, self.column_type, self.name, self.data_type) return {"FINISHED"} +class SetupDefaultTaskColumns(bpy.types.Operator): + bl_idname = "bim.setup_default_task_columns" + bl_label = "Setip Default Task Columns" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + core.setup_default_task_columns(tool.Sequence) + return {"FINISHED"} class RemoveTaskColumn(bpy.types.Operator): bl_idname = "bim.remove_task_column" diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 68bde7770a..5624c55215 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -193,16 +193,18 @@ class BIM_PT_work_schedules(Panel): row.operator("bim.remove_task", text="", icon="X").task = ifc_definition_id def draw_column_ui(self): + row = self.layout.row(align=True) + row.operator("bim.setup_default_task_columns", text="Show Default Columns", icon="ANCHOR_BOTTOM") row = self.layout.row(align=True) row.prop(self.props, "column_types", text="") column_type = self.props.column_types - if self.props.column_types == "IfcTask": + if column_type == "IfcTask": row.prop(self.props, "task_columns", text="") name, data_type = self.props.task_columns.split("/") - elif self.props.column_types == "IfcTaskTime": + elif column_type == "IfcTaskTime": row.prop(self.props, "task_time_columns", text="") name, data_type = self.props.task_time_columns.split("/") - elif self.props.column_types == "Special": + elif column_type == "Special": row.prop(self.props, "other_columns", text="") column_type, name = self.props.other_columns.split(".") data_type = "string" @@ -223,6 +225,8 @@ class BIM_PT_work_schedules(Panel): op.target_prop = "BIMWorkScheduleProperties.visualisation_start" op = row.operator("bim.datepicker", text=self.props.visualisation_finish or "Finish Date", icon="FF") op.target_prop = "BIMWorkScheduleProperties.visualisation_finish" + op = row.operator("bim.guess_date_range", text="Guess", icon="FILE_REFRESH") + op.work_schedule = self.props.active_work_schedule_id op = row.operator("bim.visualise_work_schedule_date", text="", icon="RESTRICT_RENDER_OFF") op.work_schedule = self.props.active_work_schedule_id op = row.operator("bim.visualise_work_schedule_date_range", text="", icon="OUTLINER_OB_CAMERA") @@ -253,8 +257,8 @@ class BIM_PT_work_schedules(Panel): "active_task_index", ) row = self.layout.row() - row.operator("bim.expand_all_tasks", text="Expand All Tasks") - row.operator("bim.contract_all_tasks", text="Contract All Tasks") + row.operator("bim.expand_all_tasks", text="Expand All") + row.operator("bim.contract_all_tasks", text="Contract All") if self.props.active_task_id and self.props.editing_task_type == "ATTRIBUTES": self.draw_editable_task_attributes_ui() elif self.props.active_task_id and self.props.editing_task_type == "CALENDAR": diff --git a/src/blenderbim/blenderbim/core/sequence.py b/src/blenderbim/blenderbim/core/sequence.py index 38b0eb343e..e43b4cab12 100644 --- a/src/blenderbim/blenderbim/core/sequence.py +++ b/src/blenderbim/blenderbim/core/sequence.py @@ -469,3 +469,10 @@ def highlight_product_related_task(sequence, product_type=None): is_work_schedule_active = sequence.is_work_schedule_active(work_schedule) if is_work_schedule_active: sequence.highlight_task(task) + +def guess_date_range(sequence, work_schedule=None): + start, finish = sequence.guess_date_range(work_schedule) + sequence.update_visualisation_date(start,finish) + +def setup_default_task_columns(sequence): + sequence.setup_default_task_columns() \ No newline at end of file diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index f3913fad0b..1917af33fa 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -474,7 +474,6 @@ class Sequence: def get_work_plan_attributes(cls): pass def load_work_plan_attributes(cls, work_plan): pass def get_direct_nested_tasks(cls, task):pass - def get_all_nested_tasks(cls, task): pass def enable_editing_work_plan(cls, work_plan): pass def disable_editing_work_plan(cls): pass def enable_editing_work_plan_schedules(cls, work_plan): pass @@ -544,7 +543,9 @@ class Sequence: def get_work_schedule(cls, task): pass def is_work_schedule_active(cls, work_schedule): pass def highlight_task(cls, task): pass - + def setup_default_task_columns(cls): pass + def guess_date_range(cls, task): pass + def update_visualisation_date(cls, date): pass @interface diff --git a/src/blenderbim/blenderbim/tool/sequence.py b/src/blenderbim/blenderbim/tool/sequence.py index 339cf8080c..11706d7e1d 100644 --- a/src/blenderbim/blenderbim/tool/sequence.py +++ b/src/blenderbim/blenderbim/tool/sequence.py @@ -127,20 +127,11 @@ class Sequence(blenderbim.core.tool.Sequence): @classmethod def create_task_tree(cls, work_schedule): - def get_root_tasks_ids(work_schedule): - related_objects_ids = [] - if work_schedule.Controls: - for rel in work_schedule.Controls: - for obj in rel.RelatedObjects: - if obj.is_a("IfcTask"): - related_objects_ids.append(obj.id()) - return related_objects_ids - bpy.context.scene.BIMTaskTreeProperties.tasks.clear() props = bpy.context.scene.BIMWorkScheduleProperties cls.contracted_tasks = json.loads(props.contracted_tasks) - related_objects_ids = get_root_tasks_ids(work_schedule) + related_objects_ids = helper.get_root_tasks_ids(work_schedule) if not related_objects_ids: return cls.sort_keys = {i: cls.get_sort_key(tool.Ifc.get().by_id(i)) for i in related_objects_ids} @@ -466,25 +457,15 @@ class Sequence(blenderbim.core.tool.Sequence): @classmethod def get_direct_nested_tasks(cls, task): return helper.get_nested_tasks(task) - - @classmethod - def get_all_nested_tasks(cls, task): - for nested_task in helper.get_nested_tasks(task): - yield nested_task - yield from cls.get_all_nested_tasks(nested_task) @classmethod def get_direct_task_outputs(cls, task): - return[rel.RelatingProduct for rel in task.HasAssignments if rel.is_a("IfcRelAssignsToProduct")] + return helper.get_direct_task_outputs(task) @classmethod def get_task_outputs(cls, task): - if bpy.context.scene.BIMWorkScheduleProperties.is_nested_task_outputs: - nested_tasks = cls.get_all_nested_tasks(task) - outputs = [output for nested_task in nested_tasks for output in cls.get_direct_task_outputs(nested_task)] - else: - outputs = cls.get_direct_task_outputs(task) - return outputs + is_deep = bpy.context.scene.BIMWorkScheduleProperties.is_nested_task_outputs + return helper.get_task_outputs(task, is_deep) @classmethod def get_task_resources(cls, task): @@ -719,6 +700,30 @@ class Sequence(blenderbim.core.tool.Sequence): new.name = f"{column_type}.{name}" new.data_type = data_type + @classmethod + def setup_default_task_columns(cls): + items = [ + { + "column_type": "IfcTaskTime", + "name": "ScheduleStart", + }, + { + "column_type": "IfcTaskTime", + "name": "ScheduleFinish", + }, + { + "column_type": "IfcTaskTime", + "name": "ScheduleDuration", + }, + ] + + props = bpy.context.scene.BIMWorkScheduleProperties + props.columns.clear() + for item in items: + new = props.columns.add() + new.name = f"{item['column_type']}.{item['name']}" + new.data_type = "string" + @classmethod def remove_task_column(cls, name): props = bpy.context.scene.BIMWorkScheduleProperties @@ -759,14 +764,6 @@ class Sequence(blenderbim.core.tool.Sequence): True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False ) - @classmethod - def get_work_schedule(cls, task): - for rel in task.HasAssignments or []: - if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"): - return rel.RelatingControl - for rel in task.Nests or []: - return cls.get_work_schedule(rel.RelatingObject) - @classmethod def highlight_task(cls, task): def expand_ancestors(task): @@ -787,3 +784,20 @@ class Sequence(blenderbim.core.tool.Sequence): expand_ancestors(task) task_index = [item.ifc_definition_id for item in task_props.tasks].index(task.id()) or 0 bpy.context.scene.BIMWorkScheduleProperties.active_task_index = task_index + + @classmethod + def guess_date_range(cls, work_schedule): + return helper.guess_date_range(work_schedule) + + @classmethod + def update_visualisation_date(cls, start_date, finish_date): + def canonicalise_time(time): + if not time: + return "-" + return time.strftime("%d/%m/%y") + + print(start_date, finish_date) + + props = bpy.context.scene.BIMWorkScheduleProperties + props.visualisation_start = canonicalise_time(start_date) + props.visualisation_finish = canonicalise_time(finish_date) \ No newline at end of file