mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
BBIM Feature - expand and contract all tasks in a work schedule
This commit is contained in:
@@ -20,6 +20,8 @@ import bpy
|
|||||||
from . import ui, prop, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
|
operator.ExpandAllTasks,
|
||||||
|
operator.ContractAllTasks,
|
||||||
operator.AddSummaryTask,
|
operator.AddSummaryTask,
|
||||||
operator.AddTask,
|
operator.AddTask,
|
||||||
operator.AddTaskColumn,
|
operator.AddTaskColumn,
|
||||||
|
|||||||
@@ -1507,3 +1507,23 @@ class CalculateTaskDuration(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.calculate_task_duration(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
core.calculate_task_duration(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
|
||||||
|
class ExpandAllTasks(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.expand_all_tasks"
|
||||||
|
bl_label = "Expands all tasks"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Finds the related Task"
|
||||||
|
product_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.expand_all_tasks(tool.Sequence)
|
||||||
|
|
||||||
|
|
||||||
|
class ContractAllTasks(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
|
bl_idname = "bim.contract_all_tasks"
|
||||||
|
bl_label = "Expands all tasks"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Finds the related Task"
|
||||||
|
product_type: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.contract_all_tasks(tool.Sequence)
|
||||||
|
|||||||
@@ -252,6 +252,9 @@ class BIM_PT_work_schedules(Panel):
|
|||||||
self.props,
|
self.props,
|
||||||
"active_task_index",
|
"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")
|
||||||
if self.props.active_task_id and self.props.editing_task_type == "ATTRIBUTES":
|
if self.props.active_task_id and self.props.editing_task_type == "ATTRIBUTES":
|
||||||
self.draw_editable_task_attributes_ui()
|
self.draw_editable_task_attributes_ui()
|
||||||
elif self.props.active_task_id and self.props.editing_task_type == "CALENDAR":
|
elif self.props.active_task_id and self.props.editing_task_type == "CALENDAR":
|
||||||
|
|||||||
@@ -90,6 +90,13 @@ def expand_task(sequence, task=None):
|
|||||||
sequence.load_task_properties()
|
sequence.load_task_properties()
|
||||||
|
|
||||||
|
|
||||||
|
def expand_all_tasks(sequence):
|
||||||
|
sequence.expand_all_tasks()
|
||||||
|
work_schedule = sequence.get_active_work_schedule()
|
||||||
|
sequence.create_task_tree(work_schedule)
|
||||||
|
sequence.load_task_properties()
|
||||||
|
|
||||||
|
|
||||||
def contract_task(sequence, task=None):
|
def contract_task(sequence, task=None):
|
||||||
sequence.contract_task(task)
|
sequence.contract_task(task)
|
||||||
work_schedule = sequence.get_active_work_schedule()
|
work_schedule = sequence.get_active_work_schedule()
|
||||||
@@ -97,6 +104,12 @@ def contract_task(sequence, task=None):
|
|||||||
sequence.load_task_properties()
|
sequence.load_task_properties()
|
||||||
|
|
||||||
|
|
||||||
|
def contract_all_tasks(sequence):
|
||||||
|
sequence.contract_all_tasks()
|
||||||
|
work_schedule = sequence.get_active_work_schedule()
|
||||||
|
sequence.create_task_tree(work_schedule)
|
||||||
|
sequence.load_task_properties()
|
||||||
|
|
||||||
def remove_task(ifc, sequence, task=None):
|
def remove_task(ifc, sequence, task=None):
|
||||||
ifc.run("sequence.remove_task", task=task)
|
ifc.run("sequence.remove_task", task=task)
|
||||||
work_schedule = sequence.get_active_work_schedule()
|
work_schedule = sequence.get_active_work_schedule()
|
||||||
|
|||||||
@@ -489,8 +489,7 @@ class Sequence:
|
|||||||
def remove_task_column(cls, name): pass
|
def remove_task_column(cls, name): pass
|
||||||
def set_task_sort_column(cls, column): pass
|
def set_task_sort_column(cls, column): pass
|
||||||
def find_related_output_tasks(cls, column): pass
|
def find_related_output_tasks(cls, column): pass
|
||||||
def get_root_task(cls, task): pass
|
def get_work_schedule(cls, task): pass
|
||||||
def get_task_work_schedule(cls, task): pass
|
|
||||||
def is_work_schedule_active(cls, work_schedule): pass
|
def is_work_schedule_active(cls, work_schedule): pass
|
||||||
def highlight_task(cls, task): pass
|
def highlight_task(cls, task): pass
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_task_tree(cls, work_schedule):
|
def create_task_tree(cls, work_schedule):
|
||||||
def get_work_schedule_root_tasks(work_schedule):
|
def get_root_tasks_ids(work_schedule):
|
||||||
related_objects_ids = []
|
related_objects_ids = []
|
||||||
if work_schedule.Controls:
|
if work_schedule.Controls:
|
||||||
for rel in work_schedule.Controls:
|
for rel in work_schedule.Controls:
|
||||||
@@ -136,7 +136,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||||
cls.contracted_tasks = json.loads(props.contracted_tasks)
|
cls.contracted_tasks = json.loads(props.contracted_tasks)
|
||||||
|
|
||||||
related_objects_ids = get_work_schedule_root_tasks(work_schedule)
|
related_objects_ids = get_root_tasks_ids(work_schedule)
|
||||||
if not related_objects_ids:
|
if not related_objects_ids:
|
||||||
return
|
return
|
||||||
cls.sort_keys = {i: cls.get_sort_key(tool.Ifc.get().by_id(i)) for i in related_objects_ids}
|
cls.sort_keys = {i: cls.get_sort_key(tool.Ifc.get().by_id(i)) for i in related_objects_ids}
|
||||||
@@ -268,6 +268,19 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
contracted_tasks.remove(task.id())
|
contracted_tasks.remove(task.id())
|
||||||
props.contracted_tasks = json.dumps(contracted_tasks)
|
props.contracted_tasks = json.dumps(contracted_tasks)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def expand_all_tasks(cls):
|
||||||
|
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps([])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def contract_all_tasks(cls):
|
||||||
|
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||||
|
contracted_tasks = json.loads(props.contracted_tasks)
|
||||||
|
for task_item in bpy.context.scene.BIMTaskTreeProperties.tasks:
|
||||||
|
if task_item.is_expanded:
|
||||||
|
contracted_tasks.append(task_item.ifc_definition_id)
|
||||||
|
props.contracted_tasks = json.dumps(contracted_tasks)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def contract_task(cls, task):
|
def contract_task(cls, task):
|
||||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
props = bpy.context.scene.BIMWorkScheduleProperties
|
||||||
@@ -310,7 +323,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_task_time(cls, task):
|
def get_task_time(cls, task):
|
||||||
return task.TaskTime if task.TaskTime else None
|
return task.TaskTime if task.TaskTime else None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_task_attributes(cls, task):
|
def load_task_attributes(cls, task):
|
||||||
@@ -342,17 +355,19 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
if prop.data_type == "string":
|
if prop.data_type == "string":
|
||||||
if name == "ScheduleDuration" and data[name] and isinstance(data[name], str):
|
if name == "ScheduleDuration" and data[name] and isinstance(data[name], str):
|
||||||
time_object = ifcopenshell.util.date.ifc2datetime(data[name])
|
time_object = ifcopenshell.util.date.ifc2datetime(data[name])
|
||||||
minutes = int(round(time_object.seconds/60 % 60))
|
minutes = int(round(time_object.seconds / 60 % 60))
|
||||||
hours = (time_object.seconds - minutes*60)/60/60
|
hours = (time_object.seconds - minutes * 60) / 60 / 60
|
||||||
bpy.context.scene.BIMDuration.duration_days = int(time_object.days)
|
bpy.context.scene.BIMDuration.duration_days = int(time_object.days)
|
||||||
bpy.context.scene.BIMDuration.duration_hours = round(hours)
|
bpy.context.scene.BIMDuration.duration_hours = round(hours)
|
||||||
bpy.context.scene.BIMDuration.duration_minutes = minutes # should consider years, months and seconds.
|
bpy.context.scene.BIMDuration.duration_minutes = (
|
||||||
|
minutes # should consider years, months and seconds.
|
||||||
|
)
|
||||||
if isinstance(data[name], datetime):
|
if isinstance(data[name], datetime):
|
||||||
prop.string_value = "" if prop.is_null else data[name].isoformat()
|
prop.string_value = "" if prop.is_null else data[name].isoformat()
|
||||||
if name == "ScheduleDuration":
|
if name == "ScheduleDuration":
|
||||||
bpy.context.scene.BIMDuration.duration_days = data[name].days
|
bpy.context.scene.BIMDuration.duration_days = data[name].days
|
||||||
bpy.context.scene.BIMDuration.duration_hours = round(data[name].seconds / 60 / 60)
|
bpy.context.scene.BIMDuration.duration_hours = round(data[name].seconds / 60 / 60)
|
||||||
bpy.context.scene.BIMDuration.duration_minutes = data[name].seconds/60 % 60
|
bpy.context.scene.BIMDuration.duration_minutes = data[name].seconds / 60 % 60
|
||||||
return True
|
return True
|
||||||
elif isinstance(data[name], isodate.Duration):
|
elif isinstance(data[name], isodate.Duration):
|
||||||
prop.string_value = (
|
prop.string_value = (
|
||||||
@@ -390,7 +405,7 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
return True
|
return True
|
||||||
# TODO make this parse PT32 as P4D
|
# TODO make this parse PT32 as P4D
|
||||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||||
dprops= bpy.context.scene.BIMDuration
|
dprops = bpy.context.scene.BIMDuration
|
||||||
duration_days = dprops.duration_days if dprops.duration_days else 0
|
duration_days = dprops.duration_days if dprops.duration_days else 0
|
||||||
duration_hours = dprops.duration_hours if dprops.duration_hours else 0
|
duration_hours = dprops.duration_hours if dprops.duration_hours else 0
|
||||||
duration_minutes = dprops.duration_minutes if dprops.duration_minutes else 0
|
duration_minutes = dprops.duration_minutes if dprops.duration_minutes else 0
|
||||||
@@ -638,7 +653,6 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
props.active_sequence_id = rel_sequence.id()
|
props.active_sequence_id = rel_sequence.id()
|
||||||
props.editing_sequence_type = "ATTRIBUTES"
|
props.editing_sequence_type = "ATTRIBUTES"
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_lag_time_attributes(cls, lag_time):
|
def load_lag_time_attributes(cls, lag_time):
|
||||||
def callback(name, prop, data):
|
def callback(name, prop, data):
|
||||||
@@ -716,19 +730,12 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
return related_tasks
|
return related_tasks
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_root_task(cls, task):
|
def get_work_schedule(cls, task):
|
||||||
return (
|
for rel in task.HasAssignments or []:
|
||||||
task
|
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"):
|
||||||
if not task.Nests or not task.Nests[0].RelatingObject.is_a("IfcTask")
|
return rel.RelatingControl
|
||||||
else cls.get_root_task(task.Nests[0].RelatingObject)
|
for rel in task.Nests or []:
|
||||||
)
|
return cls.get_work_schedule(rel.RelatingObject)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_task_work_schedule(cls, task):
|
|
||||||
if task.HasAssignments:
|
|
||||||
for rel in task.HasAssignments:
|
|
||||||
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule"):
|
|
||||||
return rel.RelatingControl
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_work_schedule_active(cls, work_schedule):
|
def is_work_schedule_active(cls, work_schedule):
|
||||||
@@ -736,11 +743,42 @@ class Sequence(blenderbim.core.tool.Sequence):
|
|||||||
True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False
|
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 expand_ancestors(cls, task):
|
||||||
|
for rel in task.Nests or []:
|
||||||
|
parent_task = rel.RelatingObject if rel.RelatingObject.is_a("IfcTask") else None
|
||||||
|
print(parent_task)
|
||||||
|
contracted_tasks = json.loads(bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks)
|
||||||
|
if parent_task and parent_task.id() in contracted_tasks:
|
||||||
|
contracted_tasks.remove(parent_task.id())
|
||||||
|
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps(contracted_tasks)
|
||||||
|
cls.expand_ancestors(parent_task)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def highlight_task(cls, task):
|
def highlight_task(cls, task):
|
||||||
props = bpy.context.scene.BIMWorkScheduleProperties
|
def expand_ancestors(task):
|
||||||
|
for rel in task.Nests or []:
|
||||||
|
parent_task = rel.RelatingObject if rel.RelatingObject.is_a("IfcTask") else None
|
||||||
|
contracted_tasks = json.loads(bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks)
|
||||||
|
if parent_task and parent_task.id() in contracted_tasks:
|
||||||
|
contracted_tasks.remove(parent_task.id())
|
||||||
|
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps(contracted_tasks)
|
||||||
|
expand_ancestors(parent_task)
|
||||||
|
work_schedule = cls.get_active_work_schedule()
|
||||||
|
cls.create_task_tree(work_schedule)
|
||||||
|
cls.load_task_properties()
|
||||||
|
|
||||||
task_props = bpy.context.scene.BIMTaskTreeProperties
|
task_props = bpy.context.scene.BIMTaskTreeProperties
|
||||||
task_id = task.id()
|
displayed_tasks = [item.ifc_definition_id for item in task_props.tasks]
|
||||||
task_index = [task.ifc_definition_id for task in task_props.tasks].index(task_id)
|
if not task.id() in displayed_tasks:
|
||||||
if task_index:
|
expand_ancestors(task)
|
||||||
props.active_task_index = task_index
|
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
|
||||||
|
|||||||
@@ -661,4 +661,56 @@ Scenario: Assign task calendar with no working time
|
|||||||
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleStart').string_value" to "2021-01-02"
|
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleStart').string_value" to "2021-01-02"
|
||||||
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleFinish').string_value" to "2021-01-06"
|
And I set "scene.BIMWorkScheduleProperties.task_time_attributes.get('ScheduleFinish').string_value" to "2021-01-06"
|
||||||
And I press "bim.edit_task_time"
|
And I press "bim.edit_task_time"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Contract Task
|
||||||
|
Given an empty IFC project
|
||||||
|
When I press "bim.add_work_schedule"
|
||||||
|
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||||
|
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||||
|
And the variable "summary_task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And I press "bim.contract_task(task={summary_task})"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Expand Task
|
||||||
|
Given an empty IFC project
|
||||||
|
When I press "bim.add_work_schedule"
|
||||||
|
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||||
|
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||||
|
And the variable "summary_task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And I press "bim.contract_task(task={summary_task})"
|
||||||
|
And I press "bim.expand_task(task={summary_task})"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Contract All Tasks
|
||||||
|
Given an empty IFC project
|
||||||
|
When I press "bim.add_work_schedule"
|
||||||
|
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||||
|
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||||
|
And the variable "summary_task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
|
||||||
|
When I press "bim.contract_all_tasks()"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Expand All Tasks
|
||||||
|
Given an empty IFC project
|
||||||
|
When I press "bim.add_work_schedule"
|
||||||
|
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
|
||||||
|
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||||
|
And the variable "summary_task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
|
||||||
|
When I press "bim.add_task(task={summary_task})"
|
||||||
|
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
|
||||||
|
And I press "bim.contract_all_tasks()"
|
||||||
|
When I press "bim.expand_all_tasks()"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
Reference in New Issue
Block a user