From 37dec500fd66b64e4cc0f041a4dacf81e0467c92 Mon Sep 17 00:00:00 2001
From: Sigma Dimensions <79010126+myoualid@users.noreply.github.com>
Date: Mon, 29 Aug 2022 21:55:37 +0100
Subject: [PATCH] massive refactor of the sequence module
---
src/blenderbim/blenderbim/bim/helper.py | 10 +-
.../blenderbim/bim/module/pset/operator.py | 3 +-
.../bim/module/resource/operator.py | 3 +-
.../blenderbim/bim/module/resource/prop.py | 6 +-
.../bim/module/sequence/__init__.py | 6 +-
.../blenderbim/bim/module/sequence/data.py | 188 ++-
.../blenderbim/bim/module/sequence/helper.py | 62 +-
.../bim/module/sequence/operator.py | 1181 +++--------------
.../blenderbim/bim/module/sequence/prop.py | 29 +-
.../blenderbim/bim/module/sequence/ui.py | 176 ++-
src/blenderbim/blenderbim/core/sequence.py | 424 +++++-
src/blenderbim/blenderbim/core/tool.py | 78 +-
src/blenderbim/blenderbim/tool/__init__.py | 2 +-
src/blenderbim/blenderbim/tool/sequence.py | 744 ++++++++++-
src/blenderbim/test/bim/feature/pset.feature | 4 +-
.../test/bim/feature/sequence.feature | 447 ++++++-
.../api/sequence/cascade_schedule.py | 2 +-
.../ifcopenshell/api/sequence/data.py | 20 -
.../api/sequence/remove_work_calendar.py | 7 +
19 files changed, 2208 insertions(+), 1184 deletions(-)
diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py
index 06508b50fa..851a54713f 100644
--- a/src/blenderbim/blenderbim/bim/helper.py
+++ b/src/blenderbim/blenderbim/bim/helper.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+# from datetime import date
import bpy
import json
import math
@@ -46,6 +47,11 @@ def draw_attribute(attribute, layout, copy_operator=None):
value_name,
text=attribute.name,
)
+ if "ScheduleDuration" in attribute.name:
+ layout.prop(bpy.context.scene.BIMDuration, "duration_days",text="D")
+ layout.prop(bpy.context.scene.BIMDuration, "duration_hours",text="H")
+ layout.prop(bpy.context.scene.BIMDuration, "duration_minutes",text="M")
+
if attribute.is_optional:
layout.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
if copy_operator:
@@ -78,6 +84,7 @@ def import_attribute(attribute, props, data, callback=None):
new.is_optional = attribute.optional()
new.data_type = data_type if isinstance(data_type, str) else ""
is_handled_by_callback = callback(attribute.name(), new, data) if callback else None
+
if is_handled_by_callback:
pass # Our job is done
elif is_handled_by_callback is False:
@@ -96,8 +103,7 @@ def import_attribute(attribute, props, data, callback=None):
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
-
-
+
def export_attributes(props, callback=None):
attributes = {}
for prop in props:
diff --git a/src/blenderbim/blenderbim/bim/module/pset/operator.py b/src/blenderbim/blenderbim/bim/module/pset/operator.py
index 0b1a523efe..9a5e714e20 100644
--- a/src/blenderbim/blenderbim/bim/module/pset/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/pset/operator.py
@@ -102,7 +102,8 @@ class EnablePsetEditing(bpy.types.Operator):
def execute(self, context):
self.props = get_pset_props(context, self.obj, self.obj_type)
self.props.properties.clear()
-
+ ifc_definition_id = get_pset_obj_ifc_definition_id(context, self.obj, self.obj_type)
+ Data.load(IfcStore.get_file(), ifc_definition_id)
data = Data.psets if self.pset_id in Data.psets else Data.qtos
pset_data = data[self.pset_id]
self.props.active_pset_name = pset_data["Name"]
diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py
index 412101ef71..016b8aa11d 100644
--- a/src/blenderbim/blenderbim/bim/module/resource/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py
@@ -40,6 +40,7 @@ class LoadResources(bpy.types.Operator):
self.tprops.resources.clear()
self.contracted_resources = json.loads(self.props.contracted_resources)
+ Data.load(IfcStore.get_file())
for resource_id, data in Data.resources.items():
if not data["HasContext"]:
continue
@@ -116,7 +117,7 @@ class LoadResourceProperties(bpy.types.Operator):
class DisableEditingResource(bpy.types.Operator):
bl_idname = "bim.disable_editing_resource"
- bl_label = "Disable Editing Workplan"
+ bl_label = "Disable Editing Resources"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py
index 178171cd37..0a81516cd1 100644
--- a/src/blenderbim/blenderbim/bim/module/resource/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py
@@ -20,6 +20,7 @@ import bpy
import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore
from ifcopenshell.api.resource.data import Data
+import blenderbim.bim.module.pset.data
from blenderbim.bim.prop import StrProperty, Attribute
from bpy.types import PropertyGroup
from bpy.props import (
@@ -70,6 +71,9 @@ def get_quantity_types(self, context):
return quantitytypes_enum
+def update_active_resource_index(self, context):
+ blenderbim.bim.module.pset.data.refresh()
+
class Resource(PropertyGroup):
name: StringProperty(name="Name", update=updateResourceName)
ifc_definition_id: IntProperty(name="IFC Definition ID")
@@ -85,7 +89,7 @@ class BIMResourceTreeProperties(PropertyGroup):
class BIMResourceProperties(PropertyGroup):
resource_attributes: CollectionProperty(name="Resource Attributes", type=Attribute)
is_editing: BoolProperty(name="Is Editing")
- active_resource_index: IntProperty(name="Active Resource Index")
+ active_resource_index: IntProperty(name="Active Resource Index", update=update_active_resource_index)
active_resource_id: IntProperty(name="Active Resource Id")
contracted_resources: StringProperty(name="Contracted Resources", default="[]")
is_resource_update_enabled: BoolProperty(name="Is Resource Update Enabled", default=True)
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py
index 5c36c39b27..8598228e0d 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py
@@ -63,7 +63,7 @@ classes = (
operator.EnableEditingTaskCalendar,
operator.EnableEditingTaskSequence,
operator.EnableEditingTaskTime,
- operator.EnableEditingTasks,
+ operator.EnableEditingWorkScheduleTasks,
operator.EnableEditingWorkCalendar,
operator.EnableEditingWorkCalendarTimes,
operator.EnableEditingWorkPlan,
@@ -92,6 +92,7 @@ classes = (
operator.RemoveWorkSchedule,
operator.RemoveWorkTime,
operator.SelectTaskRelatedProducts,
+ operator.SelectTaskRelatedInputs,
operator.SetTaskSortColumn,
operator.UnassignLagTime,
operator.UnassignPredecessor,
@@ -114,6 +115,7 @@ classes = (
prop.BIMWorkCalendarProperties,
prop.DatePickerProperties,
prop.BIMDateTextProperties,
+ prop.BIMDuration,
ui.BIM_PT_work_plans,
ui.BIM_PT_work_schedules,
ui.BIM_PT_work_calendars,
@@ -144,6 +146,7 @@ def register():
bpy.types.Scene.BIMTaskTreeProperties = bpy.props.PointerProperty(type=prop.BIMTaskTreeProperties)
bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties)
bpy.types.Scene.DatePickerProperties = bpy.props.PointerProperty(type=prop.DatePickerProperties)
+ bpy.types.Scene.BIMDuration = bpy.props.PointerProperty(type=prop.BIMDuration)
bpy.types.TextCurve.BIMDateTextProperties = bpy.props.PointerProperty(type=prop.BIMDateTextProperties)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
@@ -155,6 +158,7 @@ def unregister():
del bpy.types.Scene.BIMTaskTreeProperties
del bpy.types.Scene.BIMWorkCalendarProperties
del bpy.types.Scene.DatePickerProperties
+ del bpy.types.Scene.BIMDuration
del bpy.types.TextCurve.BIMDateTextProperties
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/data.py b/src/blenderbim/blenderbim/bim/module/sequence/data.py
index cc948bbe49..75d19b5e6f 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/data.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/data.py
@@ -18,6 +18,7 @@
import bpy
import blenderbim.tool as tool
+import ifcopenshell
def refresh():
@@ -25,20 +26,193 @@ def refresh():
class SequenceData:
+ data = {}
is_loaded = False
- number_of_work_plans_loaded = 0
- number_of_work_schedules_loaded = 0
- number_of_tasks_loaded = 0
- tasks = {}
@classmethod
def load(cls):
+ cls.data = {
+ "has_work_plans": cls.has_work_plans(),
+ "has_work_schedules": cls.has_work_schedules(),
+ "has_work_calendars": cls.has_work_calendars(),
+ }
cls.load_work_plans()
+ cls.load_work_schedules()
+ cls.load_work_calendars()
+ cls.load_work_times()
+ cls.load_recurrence_patterns()
+ cls.load_time_periods()
+ cls.load_sequences()
+ cls.load_lag_times()
+ cls.load_task_times()
+ cls.load_tasks()
cls.is_loaded = True
+ @classmethod
+ def has_work_plans(cls):
+ return bool(tool.Ifc.get().by_type("IfcWorkPlan"))
+
+ @classmethod
+ def has_work_calendars(cls):
+ return bool(tool.Ifc.get().by_type("IfcWorkCalendar"))
+
+ @classmethod
+ def number_of_work_plans_loaded(cls):
+ return len(tool.Ifc.get().by_type("IfcWorkPlan"))
+
+ @classmethod
+ def number_of_work_schedules_loaded(cls):
+ return len(tool.Ifc.get().by_type("IfcWorkSchedule"))
+
+ @classmethod
+ def has_work_schedules(cls):
+ return bool(tool.Ifc.get().by_type("IfcWorkSchedule"))
+
@classmethod
def load_work_plans(cls):
- cls.work_plans = {}
- cls.number_of_work_plans_loaded = len(tool.Ifc.get().by_type("IfcWorkPlan"))
+ cls.data["work_plans"] = {}
for work_plan in tool.Ifc.get().by_type("IfcWorkPlan"):
- cls.work_plans[work_plan.id()] = {"Name": work_plan.Name}
+ data = {"Name": work_plan.Name}
+ data["IsDecomposedBy"] = []
+ for rel in work_plan.IsDecomposedBy:
+ data["IsDecomposedBy"].extend([o.id() for o in rel.RelatedObjects])
+ cls.data["work_plans"][work_plan.id()] = data
+ cls.data["number_of_work_plans_loaded"] = cls.number_of_work_plans_loaded()
+
+ @classmethod
+ def load_work_schedules(cls):
+ cls.data["work_schedules"] = {}
+ for work_schedule in tool.Ifc.get().by_type("IfcWorkSchedule"):
+ data = work_schedule.get_info()
+ del data["OwnerHistory"]
+ if data["Creators"]:
+ data["Creators"] = [p.id() for p in data["Creators"]]
+ data["CreationDate"] = (
+ ifcopenshell.util.date.ifc2datetime(data["CreationDate"]) if data["CreationDate"] else ""
+ )
+ data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]) if data["StartTime"] else ""
+ data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]) if data["FinishTime"] else ""
+ data["RelatedObjects"] = []
+ for rel in work_schedule.Controls:
+ for obj in rel.RelatedObjects:
+ if obj.is_a("IfcTask"):
+ data["RelatedObjects"].append(obj.id())
+ cls.data["work_schedules"][work_schedule.id()] = data
+
+ cls.data["number_of_work_schedules_loaded"] = cls.number_of_work_schedules_loaded()
+
+ @classmethod
+ def load_work_calendars(cls):
+ cls.data["work_calendars"] = {}
+ for work_calendar in tool.Ifc.get().by_type("IfcWorkCalendar"):
+ data = work_calendar.get_info()
+ del data["OwnerHistory"]
+ data["WorkingTimes"] = [t.id() for t in work_calendar.WorkingTimes or []]
+ data["ExceptionTimes"] = [t.id() for t in work_calendar.ExceptionTimes or []]
+ cls.data["work_calendars"][work_calendar.id()] = data
+
+ cls.data["number_of_work_calendars_loaded"] = len(cls.data["work_calendars"].keys())
+
+ @classmethod
+ def load_work_times(cls):
+ cls.data["work_times"] = {}
+ for work_time in tool.Ifc.get().by_type("IfcWorkTime"):
+ data = work_time.get_info()
+ data["Start"] = ifcopenshell.util.date.ifc2datetime(data["Start"]) if data["Start"] else None
+ data["Finish"] = ifcopenshell.util.date.ifc2datetime(data["Finish"]) if data["Finish"] else None
+ data["RecurrencePattern"] = work_time.RecurrencePattern.id() if work_time.RecurrencePattern else None
+ cls.data["work_times"][work_time.id()] = data
+
+ @classmethod
+ def load_recurrence_patterns(cls):
+ cls.data["recurrence_patterns"] = {}
+ for recurrence_pattern in tool.Ifc.get().by_type("IfcRecurrencePattern"):
+ data = recurrence_pattern.get_info()
+ data["TimePeriods"] = [t.id() for t in recurrence_pattern.TimePeriods or []]
+ cls.data["recurrence_patterns"][recurrence_pattern.id()] = data
+
+ @classmethod
+ def load_sequences(cls):
+ cls.data["sequences"] = {}
+ for sequence in tool.Ifc.get().by_type("IfcRelSequence"):
+ data = sequence.get_info()
+ data["RelatingProcess"] = sequence.RelatingProcess.id()
+ data["RelatedProcess"] = sequence.RelatedProcess.id()
+ data["TimeLag"] = sequence.TimeLag.id() if sequence.TimeLag else None
+ cls.data["sequences"][sequence.id()] = data
+
+ @classmethod
+ def load_time_periods(cls):
+ cls.data["time_periods"] = {}
+ for time_period in tool.Ifc.get().by_type("IfcTimePeriod"):
+ cls.data["time_periods"][time_period.id()] = {
+ "StartTime": ifcopenshell.util.date.ifc2datetime(time_period.StartTime),
+ "EndTime": ifcopenshell.util.date.ifc2datetime(time_period.EndTime),
+ }
+
+ @classmethod
+ def load_task_times(cls):
+ cls.data["task_times"] = {}
+ for task_time in tool.Ifc.get().by_type("IfcTaskTime"):
+ data = task_time.get_info()
+ for key, value in data.items():
+ if not value:
+ continue
+ if "Start" in key or "Finish" in key or key == "StatusTime":
+ data[key] = ifcopenshell.util.date.ifc2datetime(value)
+ elif key == "ScheduleDuration":
+ data[key] = ifcopenshell.util.date.ifc2datetime(value)
+ cls.data["task_times"][task_time.id()] = data
+
+ @classmethod
+ def load_lag_times(cls):
+ cls.data["lag_times"] = {}
+ for lag_time in tool.Ifc.get().by_type("IfcLagTime"):
+ data = lag_time.get_info()
+ if data["LagValue"]:
+ if data["LagValue"].is_a("IfcDuration"):
+ data["LagValue"] = ifcopenshell.util.date.ifc2datetime(data["LagValue"].wrappedValue)
+ else:
+ data["LagValue"] = float(data["LagValue"].wrappedValue)
+ cls.data["lag_times"][lag_time.id()] = data
+
+ @classmethod
+ def load_tasks(cls):
+ cls.data["tasks"] = {}
+ for task in tool.Ifc.get().by_type("IfcTask"):
+ data = task.get_info()
+ del data["OwnerHistory"]
+ data["HasAssignmentsWorkCalendar"] = []
+ data["RelatedObjects"] = []
+ data["Inputs"] = []
+ data["Controls"] = []
+ data["Outputs"] = []
+ data["Resources"] = []
+ data["IsPredecessorTo"] = []
+ data["IsSuccessorFrom"] = []
+ if task.TaskTime:
+ data["TaskTime"] = data["TaskTime"].id()
+ for rel in task.IsNestedBy:
+ [data["RelatedObjects"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcTask")]
+ data["Nests"] = [r.RelatingObject.id() for r in task.Nests or []]
+ [
+ data["Outputs"].append(r.RelatingProduct.id())
+ for r in task.HasAssignments
+ if r.is_a("IfcRelAssignsToProduct")
+ ]
+ [
+ data["Resources"].extend([o.id() for o in r.RelatedObjects if o.is_a("IfcResource")])
+ for r in task.OperatesOn
+ ]
+ [
+ data["Controls"].extend([o.id() for o in r.RelatedObjects if o.is_a("IfcControl")])
+ for r in task.OperatesOn
+ ]
+ [data["Inputs"].extend([o.id() for o in r.RelatedObjects if o.is_a("IfcProduct")]) for r in task.OperatesOn]
+ [data["IsPredecessorTo"].append(rel.id()) for rel in task.IsPredecessorTo or []]
+ [data["IsSuccessorFrom"].append(rel.id()) for rel in task.IsSuccessorFrom or []]
+ for rel in task.HasAssignments:
+ if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl:
+ if rel.RelatingControl.is_a("IfcWorkCalendar"):
+ data["HasAssignmentsWorkCalendar"].append(rel.RelatingControl.id())
+ cls.data["tasks"][task.id()] = data
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/helper.py b/src/blenderbim/blenderbim/bim/module/sequence/helper.py
index 385724818a..a1b03ce7ee 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/helper.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/helper.py
@@ -16,38 +16,21 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
-import bpy
-import math
import isodate
-import datetime
from dateutil import parser
-from ifcopenshell.api.sequence.data import Data
+import ifcopenshell.util.date as ifcdateutils
-def derive_date(ifc_definition_id, attribute_name, date=None, is_earliest=False, is_latest=False):
- task = Data.tasks[ifc_definition_id]
- if task["TaskTime"]:
- current_date = Data.task_times[task["TaskTime"]][attribute_name]
+def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False):
+ if task.TaskTime:
+ current_date = (
+ ifcdateutils.ifc2datetime(getattr(task.TaskTime, attribute_name))
+ if getattr(task.TaskTime, attribute_name)
+ else ""
+ )
if current_date:
return current_date
- for subtask in task["RelatedObjects"]:
- current_date = derive_date(subtask, attribute_name, date=date, is_earliest=is_earliest, is_latest=is_latest)
- if is_earliest:
- if current_date and (date is None or current_date < date):
- date = current_date
- if is_latest:
- if current_date and (date is None or current_date > date):
- date = current_date
- return date
-
-
-def derive_duration(ifc_definition_id, attribute_name):
- task = Data.tasks[ifc_definition_id]
- if task["TaskTime"]:
- current_date = Data.task_times[task["TaskTime"]][attribute_name]
- if current_date:
- return current_date
- for subtask in task["RelatedObjects"]:
+ for subtask in get_nested_tasks(task):
current_date = derive_date(subtask, attribute_name, date=date, is_earliest=is_earliest, is_latest=is_latest)
if is_earliest:
if current_date and (date is None or current_date < date):
@@ -79,3 +62,30 @@ def canonicalise_time(time):
if not time:
return "-"
return time.strftime("%d/%m/%y")
+
+
+def get_nested_tasks(task):
+ tasks = []
+ for rel in task.IsNestedBy:
+ for object in rel.RelatedObjects:
+ if object.is_a("IfcTask"):
+ tasks.append(object)
+ return tasks
+
+
+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:
+ get_task_work_schedule(parent_task)
+ else:
+ schedules = [
+ rel.RelatingControl
+ for rel in task.HasAssignments
+ if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkSchedule")
+ ]
+ print(f"Returning {schedules}")
+ return schedules
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
index a3e8d2e7c0..dee5dc5d35 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py
@@ -60,7 +60,7 @@ class AddWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
- core.add_work_plan(tool.Ifc, tool.Sequence)
+ core.add_work_plan(tool.Ifc)
class EditWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
@@ -69,7 +69,11 @@ class EditWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Edit Work Plan"
def _execute(self, context):
- core.edit_work_plan(tool.Ifc, tool.Sequence)
+ core.edit_work_plan(
+ tool.Ifc,
+ tool.Sequence,
+ work_plan=tool.Ifc.get().by_id(context.scene.BIMWorkPlanProperties.active_work_plan_id),
+ )
class RemoveWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
@@ -79,7 +83,7 @@ class RemoveWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
work_plan: bpy.props.IntProperty()
def _execute(self, context):
- core.remove_work_plan(tool.Ifc, tool.Sequence, work_plan=tool.Ifc.get().by_id(self.work_plan))
+ core.remove_work_plan(tool.Ifc, work_plan=tool.Ifc.get().by_id(self.work_plan))
class EnableEditingWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
@@ -108,126 +112,71 @@ class EnableEditingWorkPlanSchedules(bpy.types.Operator):
work_plan: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkPlanProperties
- props.active_work_plan_id = self.work_plan
- props.editing_type = "SCHEDULES"
+ core.enable_editing_work_plan_schedules(tool.Sequence, work_plan=tool.Ifc.get().by_id(self.work_plan))
return {"FINISHED"}
-class AssignWorkSchedule(bpy.types.Operator):
+class AssignWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_work_schedule"
bl_label = "Assign Work Schedule"
bl_options = {"REGISTER", "UNDO"}
work_plan: bpy.props.IntProperty()
work_schedule: 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(
- "aggregate.assign_object",
- self.file,
- **{
- "relating_object": self.file.by_id(self.work_plan),
- "product": self.file.by_id(self.work_schedule),
- },
+ core.assign_work_schedule(
+ tool.Ifc,
+ tool.Sequence,
+ work_plan=tool.Ifc.get().by_id(self.work_plan),
+ work_schedule=tool.Ifc.get().by_id(self.work_schedule),
)
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
-class UnassignWorkSchedule(bpy.types.Operator):
+class UnassignWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_work_schedule"
bl_label = "Unassign Work Schedule"
bl_options = {"REGISTER", "UNDO"}
work_plan: bpy.props.IntProperty()
work_schedule: 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(
- "aggregate.unassign_object",
- self.file,
- **{
- "relating_object": self.file.by_id(self.work_plan),
- "product": self.file.by_id(self.work_schedule),
- },
+ core.unassign_work_schedule(
+ tool.Ifc,
+ work_plan=tool.Ifc.get().by_id(self.work_plan),
+ work_schedule=tool.Ifc.get().by_id(self.work_schedule),
)
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
-class AddWorkSchedule(bpy.types.Operator):
+class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_work_schedule"
bl_label = "Add Work Schedule"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- ifcopenshell.api.run("sequence.add_work_schedule", IfcStore.get_file())
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
+ core.add_work_schedule(tool.Ifc)
-class EditWorkSchedule(bpy.types.Operator):
+class EditWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_work_schedule"
bl_label = "Edit Work Schedule"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- attributes = blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, self.export_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_work_schedule",
- self.file,
- **{"work_schedule": self.file.by_id(props.active_work_schedule_id), "attributes": attributes},
+ core.edit_work_schedule(
+ tool.Ifc,
+ tool.Sequence,
+ work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id),
)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.disable_editing_work_schedule()
- return {"FINISHED"}
-
- def export_attributes(self, attributes, prop):
- if "Date" in prop.name or "Time" in prop.name:
- if prop.is_null:
- attributes[prop.name] = None
- return True
- attributes[prop.name] = helper.parse_datetime(prop.string_value)
- return True
- elif prop.name == "Duration" or prop.name == "TotalFloat":
- if prop.is_null:
- attributes[prop.name] = None
- return True
- attributes[prop.name] = helper.parse_duration(prop.string_value)
- return True
-class RemoveWorkSchedule(bpy.types.Operator):
+class RemoveWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_work_schedule"
bl_label = "Remove Work Schedule"
bl_options = {"REGISTER", "UNDO"}
work_schedule: 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.remove_work_schedule", self.file, work_schedule=self.file.by_id(self.work_schedule)
- )
- Data.load(self.file)
- return {"FINISHED"}
+ core.remove_work_schedule(tool.Ifc, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
class EnableEditingWorkSchedule(bpy.types.Operator):
@@ -237,151 +186,30 @@ class EnableEditingWorkSchedule(bpy.types.Operator):
work_schedule: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.props.active_work_schedule_id = self.work_schedule
- self.props.work_schedule_attributes.clear()
- self.enable_editing_work_schedule()
- self.props.editing_type = "WORK_SCHEDULE"
+ core.enable_editing_work_schedule(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
return {"FINISHED"}
- def enable_editing_work_schedule(self):
- data = Data.work_schedules[self.work_schedule]
- blenderbim.bim.helper.import_attributes(
- "IfcWorkSchedule", self.props.work_schedule_attributes, data, self.import_attributes
- )
-
- def import_attributes(self, name, prop, data):
- if name in ["CreationDate", "StartTime", "FinishTime"]:
- prop.string_value = "" if prop.is_null else data[name].isoformat()
- return True
-
-
-class EnableEditingTasks(bpy.types.Operator):
- bl_idname = "bim.enable_editing_tasks"
+class EnableEditingWorkScheduleTasks(bpy.types.Operator):
+ bl_idname = "bim.enable_editing_work_schedule_tasks"
bl_label = "Enable Editing Tasks"
bl_options = {"REGISTER", "UNDO"}
work_schedule: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.tprops = context.scene.BIMTaskTreeProperties
- self.props.active_work_schedule_id = self.work_schedule
- self.tprops.tasks.clear()
-
- self.contracted_tasks = json.loads(self.props.contracted_tasks)
- self.sort_keys = {
- i: self.get_sort_key(Data.tasks[i]) for i in Data.work_schedules[self.work_schedule]["RelatedObjects"]
- }
-
- related_object_ids = sorted(self.sort_keys, key=self.natural_sort_key)
- if self.props.is_sort_reversed:
- related_object_ids.reverse()
-
- for related_object_id in related_object_ids:
- self.create_new_task_li(related_object_id, 0)
- bpy.ops.bim.load_task_properties()
- self.props.editing_type = "TASKS"
+ core.enable_editing_work_schedule_tasks(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
return {"FINISHED"}
- def create_new_task_li(self, related_object_id, level_index):
- task = Data.tasks[related_object_id]
- new = self.tprops.tasks.add()
- new.ifc_definition_id = related_object_id
- new.is_expanded = related_object_id not in self.contracted_tasks
- new.level_index = level_index
- if task["RelatedObjects"]:
- new.has_children = True
- if new.is_expanded:
- self.sort_keys = {i: self.get_sort_key(Data.tasks[i]) for i in task["RelatedObjects"]}
- related_object_ids = sorted(self.sort_keys, key=self.natural_sort_key)
- if self.props.is_sort_reversed:
- related_object_ids.reverse()
- for related_object_id in related_object_ids:
- self.create_new_task_li(related_object_id, level_index + 1)
-
- def get_sort_key(self, task):
- # Sorting only applies to actual tasks, not the WBS
- if task["RelatedObjects"]:
- # Sorry for the hack
- return "0000000000" + (task["Identification"] or "")
- if not self.props.sort_column:
- return task["Identification"] or ""
- column_type, name = self.props.sort_column.split(".")
- if column_type == "IfcTask":
- return task.get(name)
- elif column_type == "IfcTaskTime" and task.get("TaskTime"):
- task_time = Data.task_times[task.get("TaskTime")].get(name)
- return task["Identification"] or ""
-
- def natural_sort_key(self, i, _nsre=re.compile("([0-9]+)")):
- s = self.sort_keys[i]
- return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
-
class LoadTaskProperties(bpy.types.Operator):
bl_idname = "bim.load_task_properties"
bl_label = "Load Task Properties"
bl_options = {"REGISTER", "UNDO"}
- task: bpy.props.IntProperty()
def execute(self, context):
- self.file = IfcStore.get_file()
- self.props = context.scene.BIMWorkScheduleProperties
- self.tprops = context.scene.BIMTaskTreeProperties
- self.props.is_task_update_enabled = False
- for item in self.tprops.tasks:
- if self.task and item.ifc_definition_id != self.task:
- continue
- task = Data.tasks[item.ifc_definition_id]
- item.name = task["Name"] or "Unnamed"
- item.identification = task["Identification"] or "XXX"
- if self.props.active_task_id:
- item.is_predecessor = self.props.active_task_id in [
- Data.sequences[r]["RelatedProcess"] for r in task["IsPredecessorTo"]
- ]
- item.is_successor = self.props.active_task_id in [
- Data.sequences[r]["RelatingProcess"] for r in task["IsSuccessorFrom"]
- ]
-
- calendar = ifcopenshell.util.sequence.derive_calendar(self.file.by_id(item.ifc_definition_id))
- if calendar:
- calendar = Data.work_calendars[calendar.id()]
- if task["HasAssignmentsWorkCalendar"]:
- item.calendar = calendar["Name"] or "Unnamed"
- else:
- item.calendar = ""
- item.derived_calendar = calendar["Name"] or "Unnamed" if calendar else ""
-
- if task["TaskTime"]:
- task_time = Data.task_times[task["TaskTime"]]
- item.start = self.canonicalise_time(task_time["ScheduleStart"])
- item.finish = self.canonicalise_time(task_time["ScheduleFinish"])
- item.duration = (
- isodate.duration_isoformat(task_time["ScheduleDuration"]) if task_time["ScheduleDuration"] else "-"
- )
- else:
- derived_start = helper.derive_date(item.ifc_definition_id, "ScheduleStart", is_earliest=True)
- derived_finish = helper.derive_date(item.ifc_definition_id, "ScheduleFinish", is_latest=True)
- item.derived_start = self.canonicalise_time(derived_start) if derived_start else ""
- item.derived_finish = self.canonicalise_time(derived_finish) if derived_finish else ""
- if derived_start and derived_finish and calendar:
- derived_duration = ifcopenshell.util.sequence.count_working_days(
- derived_start, derived_finish, self.file.by_id(calendar["id"])
- )
- item.derived_duration = f"P{derived_duration}D"
- item.start = "-"
- item.finish = "-"
- item.duration = "-"
-
- self.props.is_task_update_enabled = True
+ core.load_task_properties(tool.Sequence)
return {"FINISHED"}
- def canonicalise_time(self, time):
- if not time:
- return "-"
- return time.strftime("%d/%m/%y")
-
class DisableEditingWorkSchedule(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_schedule"
@@ -389,44 +217,28 @@ class DisableEditingWorkSchedule(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
+ core.disable_editing_work_schedule(tool.Sequence)
return {"FINISHED"}
-class AddTask(bpy.types.Operator):
+class AddTask(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_task"
bl_label = "Add Task"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- ifcopenshell.api.run("sequence.add_task", self.file, parent_task=self.file.by_id(self.task))
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
- return {"FINISHED"}
+ core.add_task(tool.Ifc, tool.Sequence, parent_task=tool.Ifc.get().by_id(self.task))
-class AddSummaryTask(bpy.types.Operator):
+class AddSummaryTask(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_summary_task"
bl_label = "Add Task"
bl_options = {"REGISTER", "UNDO"}
work_schedule: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- ifcopenshell.api.run("sequence.add_task", self.file, work_schedule=self.file.by_id(self.work_schedule))
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
- return {"FINISHED"}
+ core.add_summary_task(tool.Ifc, tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
class ExpandTask(bpy.types.Operator):
@@ -436,13 +248,7 @@ class ExpandTask(bpy.types.Operator):
task: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- contracted_tasks = json.loads(props.contracted_tasks)
- contracted_tasks.remove(self.task)
- props.contracted_tasks = json.dumps(contracted_tasks)
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
+ core.expand_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
@@ -453,129 +259,41 @@ class ContractTask(bpy.types.Operator):
task: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- contracted_tasks = json.loads(props.contracted_tasks)
- contracted_tasks.append(self.task)
- props.contracted_tasks = json.dumps(contracted_tasks)
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
+ core.contract_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
-class RemoveTask(bpy.types.Operator):
+class RemoveTask(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_task"
bl_label = "Remove Task"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- edited_task_ifc = self.file.by_id(props.active_task_id) if props.active_task_id else None
-
- ifcopenshell.api.run(
- "sequence.remove_task",
- self.file,
- task=self.file.by_id(self.task),
- )
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
-
- if edited_task_ifc:
- if not any(
- task
- for task in context.scene.BIMTaskTreeProperties.tasks
- if task.ifc_definition_id == props.active_task_id
- and self.file.by_id(props.active_task_id) == edited_task_ifc
- ): # Task was deleted
- bpy.ops.bim.disable_editing_task()
-
- return {"FINISHED"}
+ core.remove_task(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class EnableEditingTaskTime(bpy.types.Operator):
+class EnableEditingTaskTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_task_time"
bl_label = "Enable Editing Task Time"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
-
- task_time_id = Data.tasks[self.task]["TaskTime"] or self.add_task_time().id()
-
- props.task_time_attributes.clear()
-
- data = Data.task_times[task_time_id]
-
- blenderbim.bim.helper.import_attributes("IfcTaskTime", props.task_time_attributes, data, self.import_attributes)
- props.active_task_time_id = task_time_id
- props.active_task_id = self.task
- props.editing_task_type = "TASKTIME"
- return {"FINISHED"}
-
- def import_attributes(self, name, prop, data):
- if prop.data_type == "string":
- if isinstance(data[name], datetime):
- prop.string_value = "" if prop.is_null else data[name].isoformat()
- return True
- elif isinstance(data[name], isodate.Duration):
- prop.string_value = (
- "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
- )
- return True
-
- 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(self.file)
- return task_time
+ core.enable_editing_task_time(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class EditTaskTime(bpy.types.Operator):
+class EditTaskTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_task_time"
bl_label = "Edit Task Time"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- attributes = blenderbim.bim.helper.export_attributes(props.task_time_attributes, self.export_attributes)
-
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_task_time",
- self.file,
- **{"task_time": self.file.by_id(props.active_task_time_id), "attributes": attributes},
+ core.edit_task_time(
+ tool.Ifc,
+ tool.Sequence,
+ task_time=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_time_id),
)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.disable_editing_task_time()
- bpy.ops.bim.load_task_properties(task=props.active_task_id)
- return {"FINISHED"}
-
- def export_attributes(self, attributes, prop):
- if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
- if prop.is_null:
- attributes[prop.name] = None
- return True
- attributes[prop.name] = helper.parse_datetime(prop.string_value)
- return True
- elif prop.name == "ScheduleDuration":
- if prop.is_null:
- attributes[prop.name] = None
- return True
- # TODO make this parse PT32 as P4D
- attributes[prop.name] = helper.parse_duration(prop.string_value)
- return True
class EnableEditingTask(bpy.types.Operator):
@@ -585,13 +303,7 @@ class EnableEditingTask(bpy.types.Operator):
task: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- props.task_attributes.clear()
- data = Data.tasks[self.task]
-
- blenderbim.bim.helper.import_attributes("IfcTask", props.task_attributes, data)
- props.active_task_id = self.task
- props.editing_task_type = "ATTRIBUTES"
+ core.enable_editing_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
@@ -601,262 +313,132 @@ class DisableEditingTask(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkScheduleProperties.active_task_id = 0
- context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+ core.disable_editing_task(tool.Sequence)
return {"FINISHED"}
-class EditTask(bpy.types.Operator):
+class EditTask(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_task"
bl_label = "Edit Task"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- attributes = blenderbim.bim.helper.export_attributes(props.task_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_task", self.file, **{"task": self.file.by_id(props.active_task_id), "attributes": attributes}
+ core.edit_task(
+ tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_id)
)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.disable_editing_task()
- bpy.ops.bim.load_task_properties(task=props.active_task_id)
- return {"FINISHED"}
-class CopyTaskAttribute(bpy.types.Operator):
+class CopyTaskAttribute(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.copy_task_attribute"
bl_label = "Copy Task Attribute"
bl_options = {"REGISTER", "UNDO"}
name: bpy.props.StringProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- value = context.scene.BIMWorkScheduleProperties.task_attributes.get(self.name).get_value()
- self.file = IfcStore.get_file()
- props = context.scene.BIMTaskTreeProperties
- for task in props.tasks:
- if task.is_selected:
- ifcopenshell.api.run(
- "sequence.edit_task",
- self.file,
- task=self.file.by_id(task.ifc_definition_id),
- attributes={self.name: value},
- )
- Data.load(IfcStore.get_file())
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.copy_task_attribute(tool.Ifc, tool.Sequence, attribute_name=self.name)
-class AssignPredecessor(bpy.types.Operator):
+class AssignPredecessor(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_predecessor"
bl_label = "Assign Predecessor"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- rel = ifcopenshell.api.run(
- "sequence.assign_sequence",
- self.file,
- relating_process=IfcStore.get_file().by_id(self.task),
- related_process=IfcStore.get_file().by_id(props.active_task_id),
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.assign_predecessor(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class AssignSuccessor(bpy.types.Operator):
+class AssignSuccessor(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_successor"
bl_label = "Assign Successor"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- rel = ifcopenshell.api.run(
- "sequence.assign_sequence",
- self.file,
- relating_process=IfcStore.get_file().by_id(props.active_task_id),
- related_process=IfcStore.get_file().by_id(self.task),
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.assign_successor(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class UnassignPredecessor(bpy.types.Operator):
+class UnassignPredecessor(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_predecessor"
bl_label = "Unassign Predecessor"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.unassign_sequence",
- self.file,
- relating_process=IfcStore.get_file().by_id(self.task),
- related_process=IfcStore.get_file().by_id(props.active_task_id),
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.unassign_predecessor(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class UnassignSuccessor(bpy.types.Operator):
+class UnassignSuccessor(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_successor"
bl_label = "Unassign Successor"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.unassign_sequence",
- self.file,
- relating_process=self.file.by_id(props.active_task_id),
- related_process=self.file.by_id(self.task),
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.unassign_successor(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class AssignProduct(bpy.types.Operator):
+class AssignProduct(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_product"
bl_label = "Assign Product"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- relating_product: bpy.props.StringProperty()
-
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
+ relating_product: bpy.props.IntProperty()
def _execute(self, context):
- self.file = IfcStore.get_file()
- relating_products = (
- [bpy.data.objects.get(self.relating_product)] if self.relating_product else context.selected_objects
- )
- for relating_product in relating_products:
- if not relating_product.BIMObjectProperties.ifc_definition_id:
- continue
- ifcopenshell.api.run(
- "sequence.assign_product",
- self.file,
- relating_product=self.file.by_id(relating_product.BIMObjectProperties.ifc_definition_id),
- related_object=self.file.by_id(self.task),
+ if self.relating_product:
+ core.assign_products(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ products=[tool.Ifc.get().by_id(self.relating_product)],
)
- Data.load(self.file)
- bpy.ops.bim.load_task_outputs()
- return {"FINISHED"}
+ else:
+ core.assign_products(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class UnassignProduct(bpy.types.Operator):
+class UnassignProduct(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_product"
bl_label = "Unassign Product"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- relating_product: bpy.props.StringProperty()
-
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
+ relating_product: bpy.props.IntProperty()
def _execute(self, context):
- self.file = IfcStore.get_file()
- relating_products = (
- [bpy.data.objects.get(self.relating_product)] if self.relating_product else context.selected_objects
- )
- for relating_product in relating_products:
- if not relating_product.BIMObjectProperties.ifc_definition_id:
- continue
- ifcopenshell.api.run(
- "sequence.unassign_product",
- self.file,
- relating_product=self.file.by_id(relating_product.BIMObjectProperties.ifc_definition_id),
- related_object=self.file.by_id(self.task),
+ if self.relating_product:
+ core.unassign_products(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ products=[tool.Ifc.get().by_id(self.relating_product)],
)
- Data.load(self.file)
- bpy.ops.bim.load_task_outputs()
- return {"FINISHED"}
+ else:
+ core.unassign_products(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
-class AssignProcess(bpy.types.Operator):
+class AssignProcess(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_process"
bl_label = "Assign Process"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
related_object_type: bpy.props.StringProperty()
- related_object: bpy.props.StringProperty()
- resource: bpy.props.IntProperty()
-
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
+ related_object: bpy.props.IntProperty()
def _execute(self, context):
- self.file = IfcStore.get_file()
if self.related_object_type == "RESOURCE":
- self.assign_resource()
+ core.assign_resource(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
elif self.related_object_type == "PRODUCT":
- self.assign_product(context)
+ if self.related_object:
+ core.assign_input_products(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ products=[tool.Ifc.get().by_id(self.related_object)],
+ )
+ else:
+ core.assign_input_products(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
elif self.related_object_type == "CONTROL":
pass # TODO
- return {"FINISHED"}
-
- def assign_resource(self):
- task = self.file.by_id(self.task)
- resource = self.file.by_id(self.resource)
- subresource = ifcopenshell.api.run(
- "resource.add_resource",
- self.file,
- **{"parent_resource": resource, "ifc_class": resource.is_a(), "name": resource.Name},
- )
- ifcopenshell.api.run(
- "sequence.assign_process", self.file, **{"related_object": subresource, "relating_process": task}
- )
- ResourceData.load(self.file)
- Data.load(self.file)
- bpy.ops.bim.load_resources()
- bpy.ops.bim.load_task_resources()
-
- def assign_product(self, context):
- task = self.file.by_id(self.task)
- related_objects = (
- [bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
- )
- for related_object in related_objects:
- if not related_object.BIMObjectProperties.ifc_definition_id:
- continue
- ifcopenshell.api.run(
- "sequence.assign_process",
- self.file,
- related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
- relating_process=task,
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_inputs()
class UnassignProcess(bpy.types.Operator):
@@ -865,49 +447,35 @@ class UnassignProcess(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
related_object_type: bpy.props.StringProperty()
- related_object: bpy.props.StringProperty()
+ related_object: 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()
if self.related_object_type == "RESOURCE":
- self.unassign_resource()
+ core.unassign_resource(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ resource=tool.Ifc.get().by_id(self.resource),
+ )
+
elif self.related_object_type == "PRODUCT":
- self.unassign_product(context)
+ if self.related_object:
+ core.unassign_input_products(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ products=[tool.Ifc.get().by_id(self.related_object)],
+ )
+ else:
+ core.unassign_input_products(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
elif self.related_object_type == "CONTROL":
pass # TODO
return {"FINISHED"}
- def unassign_resource(self):
- task = self.file.by_id(self.task)
- resource = self.file.by_id(self.resource)
- ifcopenshell.api.run("sequence.unassign_process", self.file, related_object=resource, relating_process=task)
- ifcopenshell.api.run("resource.remove_resource", self.file, resource=resource)
- ResourceData.load(self.file)
- Data.load(self.file)
- bpy.ops.bim.load_resources()
- bpy.ops.bim.load_task_resources()
-
- def unassign_product(self, context):
- task = self.file.by_id(self.task)
- related_objects = (
- [bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
- )
- for related_object in related_objects:
- if not related_object.BIMObjectProperties.ifc_definition_id:
- continue
- ifcopenshell.api.run(
- "sequence.unassign_process",
- self.file,
- related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id),
- relating_process=task,
- )
- Data.load(self.file)
- bpy.ops.bim.load_task_inputs()
-
class GenerateGanttChart(bpy.types.Operator):
bl_idname = "bim.generate_gantt_chart"
@@ -926,6 +494,7 @@ class GenerateGanttChart(bpy.types.Operator):
"USERDEFINED": "FS",
"NOTDEFINED": "FS",
}
+ Data.load(self.file)
for task_id in Data.work_schedules[self.work_schedule]["RelatedObjects"]:
self.create_new_task_json(task_id)
with open(os.path.join(context.scene.BIMProperties.data_dir, "gantt", "index.html"), "w") as f:
@@ -971,58 +540,36 @@ class GenerateGanttChart(bpy.types.Operator):
self.create_new_task_json(task_id)
-class AddWorkCalendar(bpy.types.Operator):
+class AddWorkCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_work_calendar"
bl_label = "Add Work Calendar"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file())
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
+ core.add_work_calendar(tool.Ifc)
-class EditWorkCalendar(bpy.types.Operator):
+class EditWorkCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_work_calendar"
bl_label = "Edit Work Calendar"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkCalendarProperties
- attributes = blenderbim.bim.helper.export_attributes(props.work_calendar_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_work_calendar",
- self.file,
- **{"work_calendar": self.file.by_id(props.active_work_calendar_id), "attributes": attributes},
+ core.edit_work_calendar(
+ tool.Ifc,
+ tool.Sequence,
+ work_calendar=tool.Ifc.get().by_id(context.scene.BIMWorkCalendarProperties.active_work_calendar_id),
)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.disable_editing_work_calendar()
- return {"FINISHED"}
-class RemoveWorkCalendar(bpy.types.Operator):
+class RemoveWorkCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_work_calendar"
bl_label = "Remove Work Plan"
bl_options = {"REGISTER", "UNDO"}
work_calendar: 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.remove_work_calendar", self.file, **{"work_calendar": self.file.by_id(self.work_calendar)}
- )
- Data.load(self.file)
- return {"FINISHED"}
+ core.remove_work_calendar(tool.Ifc, work_calendar=tool.Ifc.get().by_id(self.work_calendar))
class EnableEditingWorkCalendar(bpy.types.Operator):
@@ -1032,14 +579,7 @@ class EnableEditingWorkCalendar(bpy.types.Operator):
work_calendar: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkCalendarProperties
- self.props.work_calendar_attributes.clear()
-
- data = Data.work_calendars[self.work_calendar]
-
- blenderbim.bim.helper.import_attributes("IfcWorkCalendar", self.props.work_calendar_attributes, data)
- self.props.active_work_calendar_id = self.work_calendar
- self.props.editing_type = "ATTRIBUTES"
+ core.enable_editing_work_calendar(tool.Sequence, work_calendar=tool.Ifc.get().by_id(self.work_calendar))
return {"FINISHED"}
@@ -1049,7 +589,7 @@ class DisableEditingWorkCalendar(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0
+ core.disable_editing_work_calendar(tool.Sequence)
return {"FINISHED"}
@@ -1227,31 +767,19 @@ class EnableEditingWorkCalendarTimes(bpy.types.Operator):
work_calendar: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkCalendarProperties
- props.active_work_calendar_id = self.work_calendar
- props.editing_type = "WORKTIMES"
+ core.enable_editing_work_calendar_times(tool.Sequence, work_calendar=tool.Ifc.get().by_id(self.work_calendar))
return {"FINISHED"}
-class AddWorkTime(bpy.types.Operator):
+class AddWorkTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_work_time"
bl_label = "Add Work Time"
bl_options = {"REGISTER", "UNDO"}
work_calendar: bpy.props.IntProperty()
time_type: bpy.props.StringProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.add_work_time",
- self.file,
- **{"work_calendar": self.file.by_id(self.work_calendar), "time_type": self.time_type},
- )
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
+ core.add_work_time(tool.Ifc, work_calendar=tool.Ifc.get().by_id(self.work_calendar), time_type=self.time_type)
class EnableEditingWorkTime(bpy.types.Operator):
@@ -1261,64 +789,9 @@ class EnableEditingWorkTime(bpy.types.Operator):
work_time: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkCalendarProperties
- self.props.work_time_attributes.clear()
-
- data = Data.work_times[self.work_time]
-
- blenderbim.bim.helper.import_attributes(
- "IfcWorkTime", self.props.work_time_attributes, data, self.import_attributes
- )
-
- self.initialise_recurrence_components()
- self.load_recurrence_pattern_data(data)
- self.props.active_work_time_id = self.work_time
+ core.enable_editing_work_time(tool.Sequence, work_time=tool.Ifc.get().by_id(self.work_time))
return {"FINISHED"}
- def import_attributes(self, name, prop, data):
- if name in ["Start", "Finish"]:
- prop.string_value = "" if prop.is_null else data[name].isoformat()
- return True
-
- def initialise_recurrence_components(self):
- if len(self.props.day_components) == 0:
- for i in range(0, 31):
- new = self.props.day_components.add()
- new.name = str(i + 1)
- if len(self.props.weekday_components) == 0:
- for d in ["M", "T", "W", "T", "F", "S", "S"]:
- new = self.props.weekday_components.add()
- new.name = d
- if len(self.props.month_components) == 0:
- for m in ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]:
- new = self.props.month_components.add()
- new.name = m
-
- def load_recurrence_pattern_data(self, work_time):
- self.props.position = 0
- self.props.interval = 0
- self.props.occurrences = 0
- self.props.start_time = ""
- self.props.end_time = ""
- for component in self.props.day_components:
- component.is_specified = False
- for component in self.props.weekday_components:
- component.is_specified = False
- for component in self.props.month_components:
- component.is_specified = False
- if not work_time["RecurrencePattern"]:
- return
- recurrence_pattern = Data.recurrence_patterns[work_time["RecurrencePattern"]]
- for attribute in ["Position", "Interval", "Occurrences"]:
- if recurrence_pattern[attribute]:
- setattr(self.props, attribute.lower(), recurrence_pattern[attribute])
- for component in recurrence_pattern["DayComponent"] or []:
- self.props.day_components[component - 1].is_specified = True
- for component in recurrence_pattern["WeekdayComponent"] or []:
- self.props.weekday_components[component - 1].is_specified = True
- for component in recurrence_pattern["MonthComponent"] or []:
- self.props.month_components[component - 1].is_specified = True
-
class DisableEditingWorkTime(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_time"
@@ -1326,82 +799,27 @@ class DisableEditingWorkTime(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkCalendarProperties.active_work_time_id = 0
+ core.disable_editing_work_time(tool.Sequence)
return {"FINISHED"}
-class EditWorkTime(bpy.types.Operator):
+class EditWorkTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_work_time"
bl_label = "Edit Work Time"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- self.props = context.scene.BIMWorkCalendarProperties
- attributes = blenderbim.bim.helper.export_attributes(self.props.work_time_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_work_time",
- self.file,
- **{"work_time": self.file.by_id(self.props.active_work_time_id), "attributes": attributes},
- )
-
- work_time = Data.work_times[self.props.active_work_time_id]
- if work_time["RecurrencePattern"]:
- self.edit_recurrence_pattern(work_time["RecurrencePattern"])
-
- Data.load(IfcStore.get_file())
- bpy.ops.bim.disable_editing_work_time()
- return {"FINISHED"}
-
- def edit_recurrence_pattern(self, recurrence_pattern_id):
- recurrence_pattern = self.file.by_id(recurrence_pattern_id)
- attributes = {
- "Interval": self.props.interval if self.props.interval > 0 else None,
- "Occurrences": self.props.occurrences if self.props.occurrences > 0 else None,
- }
- applicable_data = {
- "DAILY": ["Interval", "Occurrences"],
- "WEEKLY": ["WeekdayComponent", "Interval", "Occurrences"],
- "MONTHLY_BY_DAY_OF_MONTH": ["DayComponent", "Interval", "Occurrences"],
- "MONTHLY_BY_POSITION": ["WeekdayComponent", "Position", "Interval", "Occurrences"],
- "BY_DAY_COUNT": ["Interval", "Occurrences"],
- "BY_WEEKDAY_COUNT": ["WeekdayComponent", "Interval", "Occurrences"],
- "YEARLY_BY_DAY_OF_MONTH": ["DayComponent", "MonthComponent", "Interval", "Occurrences"],
- "YEARLY_BY_POSITION": ["WeekdayComponent", "MonthComponent", "Position", "Interval", "Occurrences"],
- }
- if "Position" in applicable_data[recurrence_pattern.RecurrenceType]:
- attributes["Position"] = self.props.position if self.props.position != 0 else None
- if "DayComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
- attributes["DayComponent"] = [i + 1 for i, c in enumerate(self.props.day_components) if c.is_specified]
- if "WeekdayComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
- attributes["WeekdayComponent"] = [
- i + 1 for i, c in enumerate(self.props.weekday_components) if c.is_specified
- ]
- if "MonthComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
- attributes["MonthComponent"] = [i + 1 for i, c in enumerate(self.props.month_components) if c.is_specified]
- ifcopenshell.api.run(
- "sequence.edit_recurrence_pattern",
- self.file,
- **{"recurrence_pattern": recurrence_pattern, "attributes": attributes},
- )
+ core.edit_work_time(tool.Ifc, tool.Sequence)
-class RemoveWorkTime(bpy.types.Operator):
+class RemoveWorkTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_work_time"
bl_label = "Remove Work Plan"
bl_options = {"REGISTER", "UNDO"}
work_time: 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.remove_work_time", self.file, **{"work_time": self.file.by_id(self.work_time)})
- Data.load(self.file)
+ core.remove_work_time(tool.Ifc, work_time=tool.Ifc.get().by_id(self.work_time))
return {"FINISHED"}
@@ -1416,13 +834,9 @@ class AssignRecurrencePattern(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.assign_recurrence_pattern",
- self.file,
- **{"parent": self.file.by_id(self.work_time), "recurrence_type": self.recurrence_type},
+ core.assign_recurrence_pattern(
+ tool.Ifc, work_time=tool.Ifc.get().by_id(self.work_time), recurrence_type=self.recurrence_type
)
- Data.load(IfcStore.get_file())
return {"FINISHED"}
@@ -1436,66 +850,28 @@ class UnassignRecurrencePattern(bpy.types.Operator):
return IfcStore.execute_ifc_operator(self, context)
def _execute(self, context):
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.unassign_recurrence_pattern",
- self.file,
- **{"recurrence_pattern": self.file.by_id(self.recurrence_pattern)},
- )
- Data.load(self.file)
+ core.unassign_recurrence_pattern(tool.Ifc, recurrence_pattern=tool.Ifc.get().by_id(self.recurrence_pattern))
return {"FINISHED"}
-class AddTimePeriod(bpy.types.Operator):
+class AddTimePeriod(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_time_period"
bl_label = "Add Time Period"
bl_options = {"REGISTER", "UNDO"}
recurrence_pattern: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- self.props = context.scene.BIMWorkCalendarProperties
- self.file = IfcStore.get_file()
- try:
- start_time = parser.parse(self.props.start_time)
- end_time = parser.parse(self.props.end_time)
- except:
- return {"FINISHED"}
- ifcopenshell.api.run(
- "sequence.add_time_period",
- self.file,
- **{
- "recurrence_pattern": self.file.by_id(self.recurrence_pattern),
- "start_time": start_time,
- "end_time": end_time,
- },
- )
- self.props.start_time = ""
- self.props.end_time = ""
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
+ core.add_time_period(tool.Ifc, tool.Sequence, recurrence_pattern=tool.Ifc.get().by_id(self.recurrence_pattern))
-class RemoveTimePeriod(bpy.types.Operator):
+class RemoveTimePeriod(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_time_period"
bl_label = "Remove Time Period"
bl_options = {"REGISTER", "UNDO"}
time_period: 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.remove_time_period",
- self.file,
- **{"time_period": self.file.by_id(self.time_period)},
- )
- Data.load(self.file)
- return {"FINISHED"}
+ core.remove_time_period(tool.Ifc, time_period=tool.Ifc.get().by_id(self.time_period))
class EnableEditingTaskCalendar(bpy.types.Operator):
@@ -1505,64 +881,40 @@ class EnableEditingTaskCalendar(bpy.types.Operator):
task: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- props.active_task_id = self.task
- props.editing_task_type = "CALENDAR"
+ core.enable_editing_task_calendar(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
-class EditTaskCalendar(bpy.types.Operator):
+class EditTaskCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_task_calendar"
bl_label = "Edit Task Calendar"
bl_options = {"REGISTER", "UNDO"}
work_calendar: bpy.props.IntProperty()
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- self.file = IfcStore.get_file()
- task = self.file.by_id(self.task)
- ifcopenshell.api.run(
- "control.assign_control",
- self.file,
- **{
- "relating_control": self.file.by_id(self.work_calendar),
- "related_object": task,
- },
+ core.edit_task_calendar(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ work_calendar=tool.Ifc.get().by_id(self.work_calendar),
)
- ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=task)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
-class RemoveTaskCalendar(bpy.types.Operator):
+class RemoveTaskCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_task_calendar"
bl_label = "Remove Task Calendar"
bl_options = {"REGISTER", "UNDO"}
work_calendar: bpy.props.IntProperty()
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- self.file = IfcStore.get_file()
- task = self.file.by_id(self.task)
- ifcopenshell.api.run(
- "control.unassign_control",
- self.file,
- **{
- "relating_control": self.file.by_id(self.work_calendar),
- "related_object": task,
- },
+ core.remove_task_calendar(
+ tool.Ifc,
+ tool.Sequence,
+ task=tool.Ifc.get().by_id(self.task),
+ work_calendar=tool.Ifc.get().by_id(self.work_calendar),
)
- ifcopenshell.api.run("sequence.cascade_schedule", self.file, task=task)
- Data.load(IfcStore.get_file())
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
class EnableEditingTaskSequence(bpy.types.Operator):
@@ -1571,10 +923,7 @@ class EnableEditingTaskSequence(bpy.types.Operator):
task: bpy.props.IntProperty()
def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- props.active_task_id = self.task
- props.editing_task_type = "SEQUENCE"
- bpy.ops.bim.load_task_properties()
+ core.enable_editing_task_sequence(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
@@ -1584,8 +933,7 @@ class DisableEditingTaskTime(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
- bpy.ops.bim.disable_editing_task()
+ core.disable_editing_task_time(tool.Sequence)
return {"FINISHED"}
@@ -1596,141 +944,67 @@ class EnableEditingSequenceAttributes(bpy.types.Operator):
sequence: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.props.active_sequence_id = self.sequence
- self.props.editing_sequence_type = "ATTRIBUTES"
- self.props.sequence_attributes.clear()
- self.enable_editing_sequence_attributes()
+ core.enable_editing_sequence_attributes(tool.Sequence, rel_sequence=tool.Ifc.get().by_id(self.sequence))
return {"FINISHED"}
- def enable_editing_sequence_attributes(self):
- data = Data.sequences[self.sequence]
- blenderbim.bim.helper.import_attributes("IfcRelSequence", self.props.sequence_attributes, data)
-
class EnableEditingSequenceTimeLag(bpy.types.Operator):
- bl_idname = "bim.enable_editing_sequence_time_lag"
+ bl_idname = "bim.enable_editing_sequence_lag_time"
bl_label = "Enable Editing Sequence Time Lag"
bl_options = {"REGISTER", "UNDO"}
sequence: bpy.props.IntProperty()
lag_time: bpy.props.IntProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.props.active_sequence_id = self.sequence
- self.props.editing_sequence_type = "TIME_LAG"
- self.props.time_lag_attributes.clear()
- self.enable_editing_attributes()
+ core.enable_editing_sequence_lag_time(
+ tool.Sequence,
+ rel_sequence=tool.Ifc.get().by_id(self.sequence),
+ lag_time=tool.Ifc.get().by_id(self.lag_time),
+ )
return {"FINISHED"}
- def enable_editing_attributes(self):
- data = Data.lag_times[self.lag_time]
- blenderbim.bim.helper.import_attributes(
- "IfcLagTime", self.props.time_lag_attributes, data, self.import_attributes
- )
- def import_attributes(self, name, prop, data):
- if name == "LagValue":
- prop = self.props.time_lag_attributes.add()
- prop.name = name
- prop.is_null = data[name] is None
- prop.is_optional = False
- if isinstance(data[name], isodate.Duration):
- prop.data_type = "string"
- prop.string_value = (
- "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
- )
- return True
- else:
- prop.data_type = "float"
- prop.float_value = 0.0 if prop.is_null else data[name]
- return True
-
-
-class UnassignLagTime(bpy.types.Operator):
+class UnassignLagTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.unassign_lag_time"
bl_label = "Unassign Time Lag"
bl_options = {"REGISTER", "UNDO"}
sequence: 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_lag_time", self.file, **{"rel_sequence": self.file.by_id(self.sequence)}
- )
- Data.load(IfcStore.get_file())
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.unassign_lag_time(tool.Ifc, tool.Sequence, rel_sequence=tool.Ifc.get().by_id(self.sequence))
-class AssignLagTime(bpy.types.Operator):
+class AssignLagTime(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.assign_lag_time"
bl_label = "Assign Time Lag"
bl_options = {"REGISTER", "UNDO"}
sequence: 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.assign_lag_time",
- self.file,
- **{"rel_sequence": self.file.by_id(self.sequence), "lag_value": "P0D"},
- )
- Data.load(IfcStore.get_file())
- return {"FINISHED"}
+ core.assign_lag_time(tool.Ifc, rel_sequence=tool.Ifc.get().by_id(self.sequence))
-class EditSequenceAttributes(bpy.types.Operator):
+class EditSequenceAttributes(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.edit_sequence_attributes"
bl_label = "Edit Sequence"
bl_options = {"REGISTER", "UNDO"}
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- attributes = blenderbim.bim.helper.export_attributes(props.sequence_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_sequence",
- self.file,
- **{"rel_sequence": self.file.by_id(props.active_sequence_id), "attributes": attributes},
+ core.edit_sequence_attributes(
+ tool.Ifc,
+ tool.Sequence,
+ rel_sequence=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_sequence_id),
)
- Data.load(self.file)
- bpy.ops.bim.disable_editing_sequence()
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
-class EditSequenceTimeLag(bpy.types.Operator):
- bl_idname = "bim.edit_sequence_time_lag"
+class EditSequenceTimeLag(bpy.types.Operator, tool.Ifc.Operator):
+ bl_idname = "bim.edit_sequence_lag_time"
bl_label = "Edit Time Lag"
bl_options = {"REGISTER", "UNDO"}
lag_time: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
-
def _execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- attributes = blenderbim.bim.helper.export_attributes(props.time_lag_attributes)
- self.file = IfcStore.get_file()
- ifcopenshell.api.run(
- "sequence.edit_lag_time",
- self.file,
- **{"lag_time": self.file.by_id(self.lag_time), "attributes": attributes},
- )
- Data.load(self.file)
- bpy.ops.bim.disable_editing_sequence()
- bpy.ops.bim.load_task_properties()
- return {"FINISHED"}
+ core.edit_sequence_lag_time(tool.Ifc, tool.Sequence, lag_time=tool.Ifc.get().by_id(self.lag_time))
class DisableEditingSequence(bpy.types.Operator):
@@ -1739,29 +1013,28 @@ class DisableEditingSequence(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- context.scene.BIMWorkScheduleProperties.active_sequence_id = 0
+ core.disable_editing_rel_sequence(tool.Sequence)
return {"FINISHED"}
-class SelectTaskRelatedProducts(bpy.types.Operator):
+class SelectTaskRelatedProducts(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.select_task_related_products"
- bl_label = "Select Similar Type"
+ bl_label = "Select All Output Products"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- return IfcStore.execute_ifc_operator(self, context)
+ def _execute(self, context):
+ core.select_task_outputs(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
+
+
+class SelectTaskRelatedInputs(bpy.types.Operator, tool.Ifc.Operator):
+ bl_idname = "bim.select_task_related_inputs"
+ bl_label = "Select All Input Products"
+ bl_options = {"REGISTER", "UNDO"}
+ task: bpy.props.IntProperty()
def _execute(self, context):
- self.file = IfcStore.get_file()
- related_products = ifcopenshell.api.run(
- "sequence.get_related_products", self.file, **{"related_object": self.file.by_id(self.task)}
- )
- for obj in context.visible_objects:
- obj.select_set(False)
- if obj.BIMObjectProperties.ifc_definition_id in related_products:
- obj.select_set(True)
- return {"FINISHED"}
+ core.select_task_inputs(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
class VisualiseWorkScheduleDate(bpy.types.Operator):
@@ -1805,8 +1078,8 @@ class VisualiseWorkScheduleDate(bpy.types.Operator):
for rel in task.IsNestedBy or []:
for related_object in rel.RelatedObjects:
self.preprocess_task(related_object)
- start = helper.derive_date(task.id(), "ScheduleStart", is_earliest=True)
- finish = helper.derive_date(task.id(), "ScheduleFinish", is_latest=True)
+ start = helper.derive_date(task, "ScheduleStart", is_earliest=True)
+ finish = helper.derive_date(task, "ScheduleFinish", is_latest=True)
if not start or not finish:
return
products = [r.RelatingProduct.id() for r in task.HasAssignments or [] if r.is_a("IfcRelAssignsToProduct")]
@@ -2026,10 +1299,12 @@ class VisualiseWorkScheduleDateRange(bpy.types.Operator):
for rel in task.IsNestedBy or []:
for related_object in rel.RelatedObjects:
self.preprocess_task(related_object)
- start = helper.derive_date(task.id(), "ScheduleStart", is_earliest=True)
- finish = helper.derive_date(task.id(), "ScheduleFinish", is_latest=True)
+ start = helper.derive_date(task, "ScheduleStart", is_earliest=True)
+ finish = helper.derive_date(task, "ScheduleFinish", is_latest=True)
if not start or not finish:
return
+ if not Data.is_loaded:
+ Data.load(self.file) # TO DO: REFACTOR OPERATOR
for output_id in Data.tasks[task.id()]["Outputs"]:
self.add_product_frame(output_id, task, start, finish, "output")
for input_id in Data.tasks[task.id()]["Inputs"]:
@@ -2149,22 +1424,14 @@ class BlenderBIM_RedrawDatePicker(bpy.types.Operator):
return {"FINISHED"}
-class RecalculateSchedule(bpy.types.Operator):
+class RecalculateSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.recalculate_schedule"
bl_label = "Recalculate Schedule"
bl_options = {"REGISTER", "UNDO"}
work_schedule: 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.recalculate_schedule", self.file, work_schedule=self.file.by_id(self.work_schedule)
- )
- Data.load(self.file)
- return {"FINISHED"}
+ core.recalculate_schedule(tool.Ifc, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
class AddTaskColumn(bpy.types.Operator):
@@ -2176,10 +1443,7 @@ class AddTaskColumn(bpy.types.Operator):
data_type: bpy.props.StringProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- new = self.props.columns.add()
- new.name = f"{self.column_type}.{self.name}"
- new.data_type = self.data_type
+ core.add_task_column(tool.Sequence, self.column_type, self.name, self.data_type)
return {"FINISHED"}
@@ -2190,8 +1454,7 @@ class RemoveTaskColumn(bpy.types.Operator):
name: bpy.props.StringProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.props.columns.remove(self.props.columns.find(self.name))
+ core.remove_task_column(tool.Sequence, self.name)
return {"FINISHED"}
@@ -2202,9 +1465,7 @@ class SetTaskSortColumn(bpy.types.Operator):
column: bpy.props.StringProperty()
def execute(self, context):
- self.props = context.scene.BIMWorkScheduleProperties
- self.props.sort_column = self.column
- bpy.ops.bim.enable_editing_tasks(work_schedule=self.props.active_work_schedule_id)
+ core.set_task_sort_column(tool.Sequence, self.column)
return {"FINISHED"}
@@ -2214,16 +1475,7 @@ class LoadTaskResources(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- self.file = IfcStore.get_file()
- self.props = context.scene.BIMWorkScheduleProperties
- self.tprops = context.scene.BIMTaskTreeProperties
- ifc_definition_id = self.tprops.tasks[self.props.active_task_index].ifc_definition_id
- self.props.task_resources.clear()
- for resource_id in Data.tasks[ifc_definition_id]["Resources"]:
- resource = self.file.by_id(resource_id)
- new = self.props.task_resources.add()
- new.ifc_definition_id = resource_id
- new.name = resource.Name or "Unnamed"
+ core.load_task_resources(tool.Sequence)
return {"FINISHED"}
@@ -2233,16 +1485,7 @@ class LoadTaskInputs(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- self.file = IfcStore.get_file()
- self.props = context.scene.BIMWorkScheduleProperties
- self.tprops = context.scene.BIMTaskTreeProperties
- ifc_definition_id = self.tprops.tasks[self.props.active_task_index].ifc_definition_id
- self.props.task_inputs.clear()
- for input_id in Data.tasks[ifc_definition_id]["Inputs"]:
- product = self.file.by_id(input_id)
- new = self.props.task_inputs.add()
- new.ifc_definition_id = input_id
- new.name = product.Name or "Unnamed"
+ core.load_task_inputs(tool.Sequence)
return {"FINISHED"}
@@ -2252,29 +1495,15 @@ class LoadTaskOutputs(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
- self.file = IfcStore.get_file()
- self.props = context.scene.BIMWorkScheduleProperties
- self.tprops = context.scene.BIMTaskTreeProperties
- ifc_definition_id = self.tprops.tasks[self.props.active_task_index].ifc_definition_id
- self.props.task_outputs.clear()
- for output_id in Data.tasks[ifc_definition_id]["Outputs"]:
- product = self.file.by_id(output_id)
- new = self.props.task_outputs.add()
- new.ifc_definition_id = output_id
- new.name = product.Name or "Unnamed"
+ core.load_task_outputs(tool.Sequence)
return {"FINISHED"}
-class CalculateTaskDuration(bpy.types.Operator):
+class CalculateTaskDuration(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.calculate_task_duration"
bl_label = "Calculate Task Duration"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- def execute(self, context):
- props = context.scene.BIMWorkScheduleProperties
- self.file = IfcStore.get_file()
- ifcopenshell.api.run("sequence.calculate_task_duration", self.file, task=self.file.by_id(self.task))
- Data.load(self.file)
- bpy.ops.bim.enable_editing_tasks(work_schedule=props.active_work_schedule_id)
- return {"FINISHED"}
+ def _execute(self, context):
+ core.calculate_task_duration(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task))
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py
index 5c3eb6a18f..a0d1e7f188 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py
@@ -21,8 +21,9 @@ import isodate
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.module.sequence.data import SequenceData
+import blenderbim.bim.module.pset.data
from blenderbim.bim.prop import StrProperty, Attribute
from dateutil import parser
from bpy.types import PropertyGroup
@@ -78,17 +79,18 @@ def getTaskTimeColumns(self, context):
def getWorkSchedules(self, context):
- return [(str(k), v["Name"], "") for k, v in Data.work_schedules.items()]
+ return [(str(k), v["Name"], "") for k, v in SequenceData.data["work_schedules"].items()]
def getWorkCalendars(self, context):
- return [(str(k), v["Name"], "") for k, v in Data.work_calendars.items()]
+ return [(str(k), v["Name"], "") for k, v in SequenceData.data["work_calendars"].items()]
def update_active_task_index(self, context):
bpy.ops.bim.load_task_inputs()
bpy.ops.bim.load_task_resources()
bpy.ops.bim.load_task_outputs()
+ blenderbim.bim.module.pset.data.refresh()
def updateTaskName(self, context):
@@ -101,7 +103,7 @@ def updateTaskName(self, context):
self.file,
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
)
- Data.load(IfcStore.get_file())
+ SequenceData.load()
if props.active_task_id == self.ifc_definition_id:
attribute = props.task_attributes.get("Name")
attribute.string_value = self.name
@@ -117,7 +119,7 @@ def updateTaskIdentification(self, context):
self.file,
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
)
- Data.load(self.file)
+ SequenceData.load()
if props.active_task_id == self.ifc_definition_id:
attribute = props.task_attributes.get("Identification")
attribute.string_value = self.identification
@@ -163,10 +165,10 @@ def updateTaskTimeDateTime(self, context, startfinish):
task_time = task.TaskTime
else:
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task)
- Data.load(IfcStore.get_file())
+ SequenceData.load()
startfinish_key = "Schedule" + startfinish.capitalize()
- if Data.task_times[task_time.id()][startfinish_key] == startfinish_datetime:
+ if SequenceData.data["task_times"][task_time.id()][startfinish_key] == startfinish_datetime:
canonical_startfinish_value = canonicalise_time(startfinish_datetime)
if startfinish_value != canonical_startfinish_value:
setattr(self, startfinish, canonical_startfinish_value)
@@ -177,7 +179,7 @@ def updateTaskTimeDateTime(self, context, startfinish):
self.file,
**{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}},
)
- Data.load(IfcStore.get_file())
+ SequenceData.load()
bpy.ops.bim.load_task_properties()
@@ -201,13 +203,13 @@ def updateTaskDuration(self, context):
task_time = task.TaskTime
else:
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task)
- Data.load(IfcStore.get_file())
+ SequenceData.load()
ifcopenshell.api.run(
"sequence.edit_task_time",
self.file,
**{"task_time": task_time, "attributes": {"ScheduleDuration": self.duration}},
)
- Data.load(IfcStore.get_file())
+ SequenceData.load()
if props.active_task_id == self.ifc_definition_id:
attribute = props.task_time_attributes.get("Duration")
if attribute:
@@ -285,6 +287,7 @@ class BIMWorkPlanProperties(PropertyGroup):
active_work_plan_index: IntProperty(name="Active Work Plan Index")
active_work_plan_id: IntProperty(name="Active Work Plan Id")
work_schedules: EnumProperty(items=getWorkSchedules, name="Work Schedules")
+ show_work_plan_hints: BoolProperty(name="Show Hints", default=False)
class BIMWorkScheduleProperties(PropertyGroup):
@@ -326,7 +329,7 @@ class BIMWorkScheduleProperties(PropertyGroup):
editing_sequence_type: StringProperty(name="Editing Sequence Type")
active_sequence_id: IntProperty(name="Active Sequence Id")
sequence_attributes: CollectionProperty(name="Sequence Attributes", type=Attribute)
- time_lag_attributes: CollectionProperty(name="Time Lag Attributes", type=Attribute)
+ lag_time_attributes: CollectionProperty(name="Time Lag Attributes", type=Attribute)
visualisation_start: StringProperty(name="Visualisation Start", update=update_visualisation_start)
visualisation_finish: StringProperty(name="Visualisation Finish", update=update_visualisation_finish)
speed_multiplier: FloatProperty(name="Speed Multiplier", default=10000)
@@ -349,6 +352,10 @@ class BIMWorkScheduleProperties(PropertyGroup):
task_outputs: CollectionProperty(name="Task Outputs", type=TaskProduct)
active_task_output_index: IntProperty(name="Active Task Output Index")
+class BIMDuration(PropertyGroup):
+ duration_days: IntProperty(name="Days ")
+ duration_hours: IntProperty(name="Hours")
+ duration_minutes: IntProperty(name="Minutes")
class BIMTaskTreeProperties(PropertyGroup):
# This belongs by itself for performance reasons. https://developer.blender.org/T87737
diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py
index dbe68ca97e..5f5957d5fe 100644
--- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py
@@ -21,11 +21,8 @@ import blenderbim.bim.helper
from bpy.types import Panel, UIList
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.helper import draw_attributes
-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 blenderbim.bim.module.sequence.data import SequenceData
-from datetime import datetime
+from ifcopenshell.api.resource.data import Data as ResourceData
class BIM_PT_work_plans(Panel):
@@ -48,13 +45,15 @@ class BIM_PT_work_plans(Panel):
self.props = context.scene.BIMWorkPlanProperties
row = self.layout.row()
- row.label(
- text="{} Work Plans Found".format(SequenceData.number_of_work_plans_loaded),
- icon="TEXT",
- )
+ if SequenceData.data["has_work_plans"]:
+ row.label(
+ text="{} Work Plans Found".format(SequenceData.data["number_of_work_plans_loaded"]),
+ icon="TEXT",
+ )
+ else:
+ row.label(text="No Work Plans found.", icon="TEXT")
row.operator("bim.add_work_plan", icon="ADD", text="")
-
- for work_plan_id, work_plan in SequenceData.work_plans.items():
+ for work_plan_id, work_plan in SequenceData.data["work_plans"].items():
self.draw_work_plan_ui(work_plan_id, work_plan)
def draw_work_plan_ui(self, work_plan_id, work_plan):
@@ -62,7 +61,8 @@ class BIM_PT_work_plans(Panel):
row.label(text=work_plan["Name"] or "Unnamed", icon="TEXT")
if self.props.active_work_plan_id == work_plan_id:
- row.operator("bim.edit_work_plan", text="", icon="CHECKMARK")
+ if self.props.editing_type == "ATTRIBUTES":
+ row.operator("bim.edit_work_plan", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_work_plan", text="", icon="CANCEL")
elif self.props.active_work_plan_id:
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = work_plan_id
@@ -83,19 +83,25 @@ class BIM_PT_work_plans(Panel):
draw_attributes(self.props.work_plan_attributes, self.layout)
def draw_work_schedule_ui(self):
- row = self.layout.row(align=True)
- row.prop(self.props, "work_schedules", text="")
- op = row.operator("bim.assign_work_schedule", text="", icon="ADD")
- op.work_plan = self.props.active_work_plan_id
- op.work_schedule = int(self.props.work_schedules)
+ if not SequenceData.is_loaded:
+ SequenceData.load()
- for work_schedule_id in Data.work_plans[self.props.active_work_plan_id]["IsDecomposedBy"]:
- work_schedule = Data.work_schedules[work_schedule_id]
+ if SequenceData.data["has_work_schedules"]:
row = self.layout.row(align=True)
- row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
- op = row.operator("bim.unassign_work_schedule", text="", icon="X")
+ row.prop(self.props, "work_schedules", text="")
+ op = row.operator("bim.assign_work_schedule", text="", icon="ADD")
op.work_plan = self.props.active_work_plan_id
op.work_schedule = int(self.props.work_schedules)
+ for work_schedule_id in SequenceData.data["work_plans"][self.props.active_work_plan_id]["IsDecomposedBy"]:
+ work_schedule = SequenceData.data["work_schedules"][work_schedule_id]
+ row = self.layout.row(align=True)
+ row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
+ op = row.operator("bim.unassign_work_schedule", text="", icon="X")
+ op.work_plan = self.props.active_work_plan_id
+ op.work_schedule = int(self.props.work_schedules)
+ else:
+ row = self.layout.row()
+ row.label(text="Must Create a WorkSchedule First. See Work Schedule Panel", icon="INFO")
class BIM_PT_work_schedules(Panel):
@@ -113,23 +119,29 @@ class BIM_PT_work_schedules(Panel):
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
def draw(self, context):
+ if not SequenceData.is_loaded:
+ SequenceData.load()
self.props = context.scene.BIMWorkScheduleProperties
self.tprops = context.scene.BIMTaskTreeProperties
- if not Data.is_loaded:
- Data.load(IfcStore.get_file())
-
row = self.layout.row()
- row.operator("bim.add_work_schedule", icon="ADD")
+ if SequenceData.data["has_work_schedules"]:
+ row.label(
+ text="{} Work Schedules Found".format(SequenceData.data["number_of_work_schedules_loaded"]),
+ icon="TEXT",
+ )
+ else:
+ row.label(text="No Work Schedules found.", icon="TEXT")
+ row.operator("bim.add_work_schedule", text="", icon="ADD")
- for work_schedule_id, work_schedule in Data.work_schedules.items():
+ for work_schedule_id, work_schedule in SequenceData.data["work_schedules"].items():
self.draw_work_schedule_ui(work_schedule_id, work_schedule)
def draw_work_schedule_ui(self, work_schedule_id, work_schedule):
row = self.layout.row(align=True)
row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON")
- if self.props.active_work_schedule_id and self.props.active_work_schedule_id == work_schedule_id:
+ if self.props.active_work_schedule_id == work_schedule_id:
if self.props.editing_type == "WORK_SCHEDULE":
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
elif self.props.editing_type == "TASKS":
@@ -139,23 +151,23 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.recalculate_schedule", text="", icon="FILE_REFRESH").work_schedule = work_schedule_id
row.operator("bim.add_summary_task", text="", icon="ADD").work_schedule = work_schedule_id
row.operator("bim.disable_editing_work_schedule", text="", icon="CANCEL")
- elif self.props.active_work_schedule_id:
- row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id
else:
- row.operator("bim.enable_editing_tasks", text="", icon="ACTION").work_schedule = work_schedule_id
+ row.operator(
+ "bim.enable_editing_work_schedule_tasks", text="", icon="ACTION"
+ ).work_schedule = work_schedule_id
row.operator(
"bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL"
).work_schedule = work_schedule_id
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id
if self.props.active_work_schedule_id == work_schedule_id:
- if self.props.should_show_column_ui:
- self.draw_column_ui()
- if self.props.should_show_visualisation_ui:
- self.draw_visualisation_ui()
if self.props.editing_type == "WORK_SCHEDULE":
self.draw_editable_work_schedule_ui()
elif self.props.editing_type == "TASKS":
+ if self.props.should_show_column_ui:
+ self.draw_column_ui()
+ if self.props.should_show_visualisation_ui:
+ self.draw_visualisation_ui()
self.draw_editable_task_ui(work_schedule_id)
def draw_task_operators(self):
@@ -250,68 +262,70 @@ class BIM_PT_work_schedules(Panel):
self.draw_editable_task_time_attributes_ui()
def draw_editable_task_sequence_ui(self):
- task = Data.tasks[self.props.active_task_id]
+ task = SequenceData.data["tasks"][self.props.active_task_id]
row = self.layout.row()
row.label(text="{} Predecessors".format(len(task["IsSuccessorFrom"])), icon="BACK")
for sequence_id in task["IsSuccessorFrom"]:
- self.draw_editable_sequence_ui(Data.sequences[sequence_id], "RelatingProcess")
+ self.draw_editable_sequence_ui(SequenceData.data["sequences"][sequence_id], "RelatingProcess")
row = self.layout.row()
row.label(text="{} Successors".format(len(task["IsPredecessorTo"])), icon="FORWARD")
for sequence_id in task["IsPredecessorTo"]:
- self.draw_editable_sequence_ui(Data.sequences[sequence_id], "RelatedProcess")
+ self.draw_editable_sequence_ui(SequenceData.data["sequences"][sequence_id], "RelatedProcess")
def draw_editable_sequence_ui(self, sequence, process_type):
- task = Data.tasks[sequence[process_type]]
+ task = SequenceData.data["tasks"][sequence[process_type]]
row = self.layout.row(align=True)
row.label(text=task["Identification"] or "XXX")
row.label(text=task["Name"] or "Unnamed")
row.label(text=sequence["SequenceType"] or "N/A")
if sequence["TimeLag"]:
row.operator("bim.unassign_lag_time", text="", icon="X").sequence = sequence["id"]
- row.label(text=isodate.duration_isoformat(Data.lag_times[sequence["TimeLag"]]["LagValue"]))
+ row.label(text=isodate.duration_isoformat(SequenceData.data["lag_times"][sequence["TimeLag"]]["LagValue"]))
else:
- row.operator("bim.assign_lag_time", text="", icon="ADD").sequence = sequence["id"]
- row.label(text="N/A")
+ row.operator("bim.assign_lag_time", text="Add Time Lag", icon="ADD").sequence = sequence["id"]
if self.props.active_sequence_id == sequence["id"]:
if self.props.editing_sequence_type == "ATTRIBUTES":
row.operator("bim.edit_sequence_attributes", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
self.draw_editable_sequence_attributes_ui()
- elif self.props.editing_sequence_type == "TIME_LAG":
- op = row.operator("bim.edit_sequence_time_lag", text="", icon="CHECKMARK")
+ elif self.props.editing_sequence_type == "LAG_TIME":
+ op = row.operator("bim.edit_sequence_lag_time", text="", icon="CHECKMARK")
op.lag_time = sequence["TimeLag"]
row.operator("bim.disable_editing_sequence", text="", icon="CANCEL")
- self.draw_editable_sequence_time_lag_ui()
+ self.draw_editable_sequence_lag_time_ui()
else:
if sequence["TimeLag"]:
- op = row.operator("bim.enable_editing_sequence_time_lag", text="", icon="CON_LOCKTRACK")
+ op = row.operator("bim.enable_editing_sequence_lag_time", text="Edit Time Lag", icon="CON_LOCKTRACK")
op.sequence = sequence["id"]
op.lag_time = sequence["TimeLag"]
- op = row.operator("bim.enable_editing_sequence_attributes", text="", icon="GREASEPENCIL")
+ op = row.operator("bim.enable_editing_sequence_attributes", text="Edit Sequence", icon="GREASEPENCIL")
op.sequence = sequence["id"]
def draw_editable_sequence_attributes_ui(self):
blenderbim.bim.helper.draw_attributes(self.props.sequence_attributes, self.layout)
- def draw_editable_sequence_time_lag_ui(self):
- blenderbim.bim.helper.draw_attributes(self.props.time_lag_attributes, self.layout)
+ def draw_editable_sequence_lag_time_ui(self):
+ blenderbim.bim.helper.draw_attributes(self.props.lag_time_attributes, self.layout)
def draw_editable_task_calendar_ui(self):
- task = Data.tasks[self.props.active_task_id]
+ task = SequenceData.data["tasks"][self.props.active_task_id]
if task["HasAssignmentsWorkCalendar"]:
row = self.layout.row(align=True)
- calendar = Data.work_calendars[task["HasAssignmentsWorkCalendar"][0]]
+ calendar = SequenceData.data["work_calendars"][task["HasAssignmentsWorkCalendar"][0]]
row.label(text=calendar["Name"] or "Unnamed")
op = row.operator("bim.remove_task_calendar", text="", icon="X")
op.work_calendar = task["HasAssignmentsWorkCalendar"][0]
op.task = self.props.active_task_id
- else:
+ elif SequenceData.data["has_work_calendars"]:
row = self.layout.row(align=True)
row.prop(self.props, "work_calendars", text="")
op = row.operator("bim.edit_task_calendar", text="", icon="ADD")
op.work_calendar = int(self.props.work_calendars)
op.task = self.props.active_task_id
+ else:
+ row = self.layout.row(align=True)
+ row.label(text="Must Create a Calendar First. See Work Calendar Panel", icon="INFO")
def draw_editable_task_attributes_ui(self):
blenderbim.bim.helper.draw_attributes(
@@ -353,16 +367,22 @@ class BIM_PT_task_icom(Panel):
row2 = col.row(align=True)
row2.label(text="Inputs")
+ total_task_inputs = len(self.props.task_inputs)
if context.selected_objects:
op = row2.operator("bim.assign_process", icon="ADD", text="")
op.task = task.ifc_definition_id
op.related_object_type = "PRODUCT"
- op.related_object = ""
+ if total_task_inputs:
op = row2.operator("bim.unassign_process", icon="REMOVE", text="")
op.task = task.ifc_definition_id
op.related_object_type = "PRODUCT"
- op.related_object = ""
+ if not context.selected_objects and self.props.active_task_input_index < total_task_inputs:
+ input_id = self.props.task_inputs[self.props.active_task_input_index].ifc_definition_id
+ op.related_object = input_id
+
+ op = row2.operator("bim.select_task_related_inputs", icon="RESTRICT_SELECT_OFF", text="")
+ op.task = task.ifc_definition_id
row2 = col.row()
row2.template_list("BIM_UL_task_inputs", "", self.props, "task_inputs", self.props, "active_task_input_index")
@@ -376,14 +396,17 @@ class BIM_PT_task_icom(Panel):
op = row2.operator("bim.calculate_task_duration", text="", icon="TEMP")
op.task = task.ifc_definition_id
- total_resources = len(context.scene.BIMResourceTreeProperties.resources)
+ resource_props = context.scene.BIMResourceProperties
+ resource_tprops = context.scene.BIMResourceTreeProperties
+ total_resources = len(resource_tprops.resources)
if total_resources and context.scene.BIMResourceProperties.active_resource_index < total_resources:
- op = row2.operator("bim.assign_process", icon="ADD", text="")
- op.task = task.ifc_definition_id
- op.related_object_type = "RESOURCE"
- op.resource = context.scene.BIMResourceTreeProperties.resources[
- context.scene.BIMResourceProperties.active_resource_index
- ].ifc_definition_id
+ resource_id = resource_tprops.resources[resource_props.active_resource_index].ifc_definition_id
+ ResourceData.load(IfcStore.get_file())
+ resource = ResourceData.resources[resource_id]
+ if resource["type"] != "IfcCrewResource":
+ op = row2.operator("bim.assign_process", icon="ADD", text="")
+ op.task = task.ifc_definition_id
+ op.related_object_type = "RESOURCE"
total_task_resources = len(self.props.task_resources)
if total_task_resources and self.props.active_task_resource_index < total_task_resources:
@@ -402,14 +425,17 @@ class BIM_PT_task_icom(Panel):
row2 = col.row(align=True)
row2.label(text="Outputs")
+ total_task_outputs = len(self.props.task_outputs)
if context.selected_objects:
op = row2.operator("bim.assign_product", icon="ADD", text="")
op.task = task.ifc_definition_id
- op.relating_product = ""
+ if total_task_outputs:
op = row2.operator("bim.unassign_product", icon="REMOVE", text="")
op.task = task.ifc_definition_id
- op.relating_product = ""
+ if not context.selected_objects and self.props.active_task_output_index < total_task_outputs:
+ output_id = self.props.task_outputs[self.props.active_task_output_index].ifc_definition_id
+ op.relating_product = output_id
op = row2.operator("bim.select_task_related_products", icon="RESTRICT_SELECT_OFF", text="")
op.task = task.ifc_definition_id
@@ -558,14 +584,20 @@ class BIM_PT_work_calendars(Panel):
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
def draw(self, context):
- if not Data.is_loaded:
- Data.load(IfcStore.get_file())
+ if not SequenceData.is_loaded:
+ SequenceData.load()
+
self.props = context.scene.BIMWorkCalendarProperties
-
row = self.layout.row()
- row.operator("bim.add_work_calendar", icon="ADD")
-
- for work_calendar_id, work_calendar in Data.work_calendars.items():
+ if SequenceData.data["has_work_calendars"]:
+ row.label(
+ text="{} Work Calendars Found".format(SequenceData.data["number_of_work_calendars_loaded"]),
+ icon="TEXT",
+ )
+ else:
+ row.label(text="No Work Calendars found.", icon="TEXT")
+ row.operator("bim.add_work_calendar", icon="ADD", text="")
+ for work_calendar_id, work_calendar in SequenceData.data["work_calendars"].items():
self.draw_work_calendar_ui(work_calendar_id, work_calendar)
def draw_work_calendar_ui(self, work_calendar_id, work_calendar):
@@ -600,10 +632,10 @@ class BIM_PT_work_calendars(Panel):
op.time_type = "ExceptionTimes"
for work_time_id in work_calendar["WorkingTimes"]:
- self.draw_work_time_ui(Data.work_times[work_time_id], time_type="WorkingTimes")
+ self.draw_work_time_ui(SequenceData.data["work_times"][work_time_id], time_type="WorkingTimes")
for work_time_id in work_calendar["ExceptionTimes"]:
- self.draw_work_time_ui(Data.work_times[work_time_id], time_type="ExceptionTimes")
+ self.draw_work_time_ui(SequenceData.data["work_times"][work_time_id], time_type="ExceptionTimes")
def draw_work_time_ui(self, work_time, time_type):
row = self.layout.row(align=True)
@@ -628,7 +660,9 @@ class BIM_PT_work_calendars(Panel):
def draw_editable_work_time_ui(self, work_time):
draw_attributes(self.props.work_time_attributes, self.layout)
if work_time["RecurrencePattern"]:
- self.draw_editable_recurrence_pattern_ui(Data.recurrence_patterns[work_time["RecurrencePattern"]])
+ self.draw_editable_recurrence_pattern_ui(
+ SequenceData.data["recurrence_patterns"][work_time["RecurrencePattern"]]
+ )
else:
row = self.layout.row(align=True)
row.prop(self.props, "recurrence_types", icon="RECOVER_LAST", text="")
@@ -650,7 +684,7 @@ class BIM_PT_work_calendars(Panel):
op.recurrence_pattern = recurrence_pattern["id"]
for time_period_id in recurrence_pattern["TimePeriods"]:
- time_period = Data.time_periods[time_period_id]
+ time_period = SequenceData.data["time_periods"][time_period_id]
row = box.row(align=True)
row.label(text="{} - {}".format(time_period["StartTime"], time_period["EndTime"]), icon="TIME")
op = row.operator("bim.remove_time_period", text="", icon="X")
diff --git a/src/blenderbim/blenderbim/core/sequence.py b/src/blenderbim/blenderbim/core/sequence.py
index d214ba68fc..68e7329a6a 100644
--- a/src/blenderbim/blenderbim/core/sequence.py
+++ b/src/blenderbim/blenderbim/core/sequence.py
@@ -17,14 +17,12 @@
# along with BlenderBIM Add-on. If not, see .
-def add_work_plan(ifc, sequence):
- ifc.run("sequence.add_work_plan")
- sequence.load_work_plans()
+def add_work_plan(ifc):
+ return ifc.run("sequence.add_work_plan")
-def remove_work_plan(ifc, sequence, work_plan=None):
+def remove_work_plan(ifc, work_plan=None):
ifc.run("sequence.remove_work_plan", work_plan=work_plan)
- sequence.load_work_plans()
def enable_editing_work_plan(sequence, work_plan=None):
@@ -36,9 +34,419 @@ def disable_editing_work_plan(sequence):
sequence.disable_editing_work_plan()
-def edit_work_plan(ifc, sequence):
- work_plan = sequence.get_current_ifc_work_plan()
+def edit_work_plan(ifc, sequence, work_plan=None):
attributes = sequence.get_work_plan_attributes()
ifc.run("sequence.edit_work_plan", work_plan=work_plan, attributes=attributes)
sequence.disable_editing_work_plan()
- sequence.load_work_plans()
+
+
+def edit_work_schedule(ifc, sequence, work_schedule=None):
+ attributes = sequence.get_work_schedule_attributes()
+ ifc.run("sequence.edit_work_schedule", **{"work_schedule": work_schedule, "attributes": attributes})
+ sequence.disable_editing_work_schedule()
+
+
+def enable_editing_work_plan_schedules(sequence, work_plan=None):
+ sequence.enable_editing_work_plan_schedules(work_plan)
+
+
+def add_work_schedule(ifc):
+ return ifc.run("sequence.add_work_schedule")
+
+
+def remove_work_schedule(ifc, work_schedule=None):
+ ifc.run("sequence.remove_work_schedule", **{"work_schedule": work_schedule})
+
+
+def assign_work_schedule(ifc, sequence, work_plan=None, work_schedule=None):
+ if work_schedule:
+ sequence.hide_work_plan_hints()
+ return ifc.run("aggregate.assign_object", **{"relating_object": work_plan, "product": work_schedule})
+ else:
+ sequence.show_work_plan_hints()
+
+
+def unassign_work_schedule(ifc, work_plan=None, work_schedule=None):
+ ifc.run("aggregate.unassign_object", **{"relating_object": work_plan, "product": work_schedule})
+
+
+def enable_editing_work_schedule(sequence, work_schedule=None):
+ sequence.load_work_schedule_attributes(work_schedule)
+ sequence.enable_editing_work_schedule(work_schedule)
+
+
+def enable_editing_work_schedule_tasks(sequence, work_schedule=None):
+ sequence.enable_editing_work_schedule_tasks(work_schedule)
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def create_task_tree(sequence, work_schedule):
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def expand_task(sequence, task=None):
+ sequence.expand_task(task)
+ work_schedule = sequence.get_active_work_schedule()
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def contract_task(sequence, task=None):
+ sequence.contract_task(task)
+ work_schedule = sequence.get_active_work_schedule()
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def remove_task(ifc, sequence, task=None):
+ ifc.run("sequence.remove_task", **{"task": task})
+ work_schedule = sequence.get_active_work_schedule()
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+ sequence.disable_selecting_deleted_task()
+
+
+def load_task_properties(sequence):
+ sequence.load_task_properties()
+
+
+def disable_editing_work_schedule(sequence):
+ sequence.disable_editing_work_schedule()
+
+
+def add_summary_task(ifc, sequence, work_schedule=None):
+ ifc.run("sequence.add_task", **{"work_schedule": work_schedule})
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def add_task(ifc, sequence, parent_task=None):
+ ifc.run("sequence.add_task", **{"parent_task": parent_task})
+ work_schedule = sequence.get_active_work_schedule()
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
+
+
+def enable_editing_task(sequence, task=None):
+ sequence.load_task_attributes(task)
+ sequence.enable_editing_task(task)
+
+
+def edit_task(ifc, sequence, task=None):
+ attributes = sequence.get_task_attributes()
+ ifc.run("sequence.edit_task", **{"task": task, "attributes": attributes})
+ sequence.load_task_properties(task=task)
+ sequence.disable_editing_task()
+
+
+def copy_task_attribute(ifc, sequence, attribute_name=None):
+ for task in sequence.get_checked_tasks():
+ ifc.run(
+ "sequence.edit_task",
+ **{"task": task, "attributes": {attribute_name: sequence.get_task_attribute_value(attribute_name)}}
+ )
+ sequence.load_task_properties(task)
+
+
+def disable_editing_task(sequence):
+ sequence.disable_editing_task()
+
+
+def enable_editing_task_time(ifc, sequence, task=None):
+ task_time = sequence.get_task_time(task)
+ if task_time is None:
+ task_time = ifc.run("sequence.add_task_time", **{"task": task})
+ sequence.load_task_time_attributes(task_time)
+ sequence.enable_editing_task_time(task)
+
+
+def edit_task_time(ifc, sequence, task_time=None):
+ attributes = sequence.get_task_time_attributes()
+ print(task_time)
+ ifc.run("sequence.edit_task_time", **{"task_time": task_time, "attributes": attributes}) # nasty loop goes on when calendar props are messed up
+ task = sequence.get_active_task()
+ sequence.load_task_properties(task=task)
+ sequence.disable_editing_task_time()
+
+
+def assign_predecessor(ifc, sequence, task=None):
+ predecessor_task = sequence.get_active_task()
+ ifc.run("sequence.assign_sequence", **{"relating_process": task, "related_process": predecessor_task})
+ sequence.load_task_properties()
+
+
+def unassign_predecessor(ifc, sequence, task=None):
+ predecessor_task = sequence.get_active_task()
+ ifc.run("sequence.unassign_sequence", **{"relating_process": task, "related_process": predecessor_task})
+ sequence.load_task_properties()
+
+
+def assign_successor(ifc, sequence, task=None):
+ successor_task = sequence.get_active_task()
+ ifc.run("sequence.assign_sequence", **{"relating_process": successor_task, "related_process": task})
+ sequence.load_task_properties()
+
+
+def unassign_successor(ifc, sequence, task=None):
+ successor_task = sequence.get_active_task()
+ ifc.run("sequence.unassign_sequence", **{"relating_process": successor_task, "related_process": task})
+ sequence.load_task_properties()
+
+
+def assign_products(ifc, sequence, task=None, products=None):
+ if not products:
+ products = sequence.get_selected_products()
+ for product in products:
+ ifc.run("sequence.assign_product", **{"relating_product": product, "related_object": task})
+ outputs = sequence.get_task_outputs(task)
+ sequence.load_task_outputs(outputs)
+
+
+def unassign_products(ifc, sequence, task=None, products=None):
+ if not products:
+ products = sequence.get_selected_products()
+ for product in products:
+ ifc.run("sequence.unassign_product", **{"relating_product": product, "related_object": task})
+ outputs = sequence.get_task_outputs(task)
+ sequence.load_task_outputs(outputs)
+
+
+def assign_input_products(ifc, sequence, task=None, products=None):
+ if not products:
+ products = sequence.get_selected_products()
+ for product in products:
+ ifc.run("sequence.assign_process", **{"relating_process": task, "related_object": product})
+ inputs = sequence.get_task_inputs(task)
+ sequence.load_task_inputs(inputs)
+
+
+def unassign_input_products(ifc, sequence, task=None, products=None):
+ if not products:
+ products = sequence.get_selected_products()
+ for product in products:
+ ifc.run("sequence.unassign_process", **{"relating_process": task, "related_object": product})
+ inputs = sequence.get_task_inputs(task)
+ sequence.load_task_inputs(inputs)
+
+
+def assign_resource(ifc, sequence, task=None):
+ resource = sequence.get_selected_resource()
+ sub_resource = ifc.run(
+ "resource.add_resource", **{"parent_resource": resource, "ifc_class": resource.is_a(), "name": resource.Name}
+ )
+ ifc.run("sequence.assign_process", **{"relating_process": task, "related_object": sub_resource})
+ resources = sequence.get_task_resources(task)
+ sequence.load_task_resources(resources)
+ sequence.load_resources()
+
+
+def unassign_resource(ifc, sequence, task=None, resource=None):
+ ifc.run("sequence.unassign_process", **{"relating_process": task, "related_object": resource})
+ ifc.run("resource.remove_resource", **{"resource": resource})
+ resources = sequence.get_task_resources(task)
+ sequence.load_task_resources(resources)
+ sequence.load_resources()
+
+
+def load_task_outputs(sequence):
+ task = sequence.get_highlighted_task()
+ outputs = sequence.get_task_outputs(task)
+ sequence.load_task_outputs(outputs)
+
+
+def load_task_inputs(sequence):
+ task = sequence.get_highlighted_task()
+ inputs = sequence.get_task_inputs(task)
+ sequence.load_task_inputs(inputs)
+
+
+def load_task_resources(sequence):
+ task = sequence.get_highlighted_task()
+ resources = sequence.get_task_resources(task)
+ sequence.load_task_resources(resources)
+
+
+def remove_work_calendar(ifc, work_calendar=None):
+ ifc.run("sequence.remove_work_calendar", **{"work_calendar": work_calendar})
+
+
+def add_work_calendar(ifc):
+ return ifc.run("sequence.add_work_calendar")
+
+
+def edit_work_calendar(ifc, sequence, work_calendar=None):
+ attributes = sequence.get_work_calendar_attributes()
+ ifc.run("sequence.edit_work_calendar", **{"work_calendar": work_calendar, "attributes": attributes})
+ sequence.disable_editing_work_calendar()
+ sequence.load_task_properties()
+
+
+def enable_editing_work_calendar(sequence, work_calendar=None):
+ sequence.load_work_calendar_attributes(work_calendar)
+ sequence.enable_editing_work_calendar(work_calendar)
+
+
+def disable_editing_work_calendar(sequence):
+ sequence.disable_editing_work_calendar()
+
+
+def enable_editing_work_calendar_times(sequence, work_calendar=None):
+ sequence.enable_editing_work_calendar_times(work_calendar)
+
+
+def add_work_time(ifc, work_calendar=None, time_type=None):
+ return ifc.run("sequence.add_work_time", **{"work_calendar": work_calendar, "time_type": time_type})
+
+
+def enable_editing_work_time(sequence, work_time=None):
+ sequence.load_work_time_attributes(work_time)
+ sequence.enable_editing_work_time(work_time)
+
+
+def disable_editing_work_time(sequence):
+ sequence.disable_editing_work_time()
+
+
+def remove_work_time(ifc, work_time=None):
+ ifc.run("sequence.remove_work_time", **{"work_time": work_time})
+
+
+def edit_work_time(ifc, sequence):
+ work_time = sequence.get_active_work_time()
+ ifc.run("sequence.edit_work_time", **{"work_time": work_time, "attributes": sequence.get_work_time_attributes()})
+ recurrence_pattern = work_time.RecurrencePattern
+ if recurrence_pattern:
+ ifc.run(
+ "sequence.edit_recurrence_pattern",
+ **{
+ "recurrence_pattern": recurrence_pattern,
+ "attributes": sequence.get_recurrence_pattern_attributes(recurrence_pattern),
+ }
+ )
+ sequence.disable_editing_work_time()
+
+
+def assign_recurrence_pattern(ifc, work_time=None, recurrence_type=None):
+ return ifc.run("sequence.assign_recurrence_pattern", **{"parent": work_time, "recurrence_type": recurrence_type})
+
+
+def unassign_recurrence_pattern(ifc, recurrence_pattern=None):
+ ifc.run("sequence.unassign_recurrence_pattern", **{"recurrence_pattern": recurrence_pattern})
+
+
+def add_time_period(ifc, sequence, recurrence_pattern=None):
+ start_time, end_time = sequence.get_recurrence_pattern_times()
+ ifc.run(
+ "sequence.add_time_period",
+ **{"recurrence_pattern": recurrence_pattern, "start_time": start_time, "end_time": end_time}
+ )
+ sequence.reset_time_period()
+
+
+def remove_time_period(ifc, time_period=None):
+ ifc.run("sequence.remove_time_period", **{"time_period": time_period})
+
+
+def enable_editing_task_calendar(sequence, task=None):
+ sequence.enable_editing_task_calendar(task)
+
+
+def edit_task_calendar(ifc, sequence, task=None, work_calendar=None):
+ ifc.run("control.assign_control", **{"relating_control": work_calendar, "related_object": task})
+ ifc.run("sequence.cascade_schedule", **{"task": task})
+ sequence.load_task_properties()
+
+
+def remove_task_calendar(ifc, sequence, task=None, work_calendar=None):
+ ifc.run("control.unassign_control", **{"relating_control": work_calendar, "related_object": task})
+ ifc.run("sequence.cascade_schedule", **{"task": task})
+ sequence.load_task_properties()
+
+
+def enable_editing_task_sequence(sequence, task=None):
+ sequence.enable_editing_task_sequence(task)
+ sequence.load_task_properties()
+
+
+def disable_editing_task_sequence(sequence, task=None):
+ sequence.enable_editing_task_sequence(task)
+ sequence.load_task_properties()
+
+
+def disable_editing_task_time(sequence):
+ sequence.disable_editing_task_time()
+
+
+def enable_editing_sequence_attributes(sequence, rel_sequence=None):
+ sequence.enable_editing_rel_sequence_attributes(rel_sequence)
+ sequence.load_rel_sequence_attributes(rel_sequence)
+
+
+def enable_editing_sequence_lag_time(sequence, rel_sequence=None, lag_time=None):
+ sequence.load_lag_time_attributes(lag_time)
+ sequence.enable_editing_sequence_lag_time(rel_sequence)
+
+
+def unassign_lag_time(ifc, sequence, rel_sequence=None):
+ ifc.run("sequence.unassign_lag_time", **{"rel_sequence": rel_sequence})
+ sequence.load_task_properties()
+
+
+def assign_lag_time(ifc, rel_sequence=None):
+ ifc.run("sequence.assign_lag_time", **{"rel_sequence": rel_sequence, "lag_value": "P1D"})
+
+
+def edit_sequence_attributes(ifc, sequence, rel_sequence=None):
+ attributes = sequence.get_rel_sequence_attributes()
+ ifc.run("sequence.edit_sequence", **{"rel_sequence": rel_sequence, "attributes": attributes})
+ sequence.disable_editing_rel_sequence()
+ sequence.load_task_properties()
+
+
+def edit_sequence_lag_time(ifc, sequence, lag_time=None):
+ attributes = sequence.get_lag_time_attributes()
+ ifc.run("sequence.edit_lag_time", **{"lag_time": lag_time, "attributes": attributes})
+ sequence.disable_editing_rel_sequence()
+ sequence.load_task_properties()
+
+
+def disable_editing_rel_sequence(sequence):
+ sequence.disable_editing_rel_sequence()
+
+
+def select_task_outputs(ifc, sequence, task=None):
+ outputs = sequence.get_task_outputs(task) ## should this be from the api instead ?
+ sequence.select_task_products(outputs)
+
+
+def select_task_inputs(ifc, sequence, task=None):
+ inputs = sequence.get_task_inputs(task) ## should this be from the api instead ?
+ print(inputs)
+ sequence.select_task_products(inputs)
+
+
+def recalculate_schedule(ifc, work_schedule=None):
+ ifc.run("sequence.recalculate_schedule", **{"work_schedule": work_schedule})
+
+
+def add_task_column(sequence, column_type=None, name=None, data_type=None):
+ sequence.add_task_column(column_type, name, data_type)
+
+
+def remove_task_column(sequence, name=None):
+ sequence.remove_task_column(name)
+
+
+def set_task_sort_column(sequence, column=None):
+ sequence.set_task_sort_column(column)
+
+
+def calculate_task_duration(ifc, sequence, task=None):
+ ifc.run("sequence.calculate_task_duration", **{"task": task})
+ work_schedule = sequence.get_active_work_schedule()
+ if work_schedule:
+ sequence.create_task_tree(work_schedule)
+ sequence.load_task_properties()
diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py
index 2081d61d48..f086674d5b 100644
--- a/src/blenderbim/blenderbim/core/tool.py
+++ b/src/blenderbim/blenderbim/core/tool.py
@@ -423,13 +423,79 @@ class Selector:
@interface
class Sequence:
- def disable_editing_work_plan(cls): pass
+ def get_work_plan_attributes(cls): pass
+ def load_work_plan_attributes(cls, work_plan): pass
+ def get_nested_tasks(cls, task):pass
def enable_editing_work_plan(cls, work_plan): pass
- def export_attributes(cls): pass
- def get_current_ifc_work_plan(cls): pass
- def get_ifc_work_plan_attributes(cls): pass
- def load_work_plan_attributes(cls): pass
- def load_work_plans(cls): pass
+ def disable_editing_work_plan(cls): pass
+ def enable_editing_work_plan_schedules(cls, work_plan): pass
+ def get_work_schedule_attributes(cls): pass
+ def load_work_schedule_attributes(cls, work_schedule): pass
+ def enable_editing_work_schedule(cls,work_schedule): pass
+ def disable_editing_work_schedule(cls): pass
+ def enable_editing_work_schedule_tasks(cls, work_schedule): pass
+ def create_task_tree(cls, work_schedule): pass
+ def load_task_properties(cls, task): pass
+ def hide_work_plan_hints(cls): pass
+ def show_work_plan_hints(cls): pass
+ def get_active_work_schedule_id(cls): pass
+ def get_selected_resource(cls): pass
+ def expand_task(cls, task): pass
+ def contract_task(cls, task): pass
+ def disable_work_schedule(cls): pass
+ def disable_selecting_deleted_task(cls): pass
+ def get_checked_tasks(cls): pass
+ def get_task_attribute_value(cls, attribute_name): pass
+ def get_active_task(cls): pass
+ def get_task_time(cls, task): pass
+ def load_task_attributes(cls, task): pass
+ def get_selected_products(cls): pass
+ def enable_editing_task(cls, task): pass
+ def get_task_attributes(cls): pass
+ def load_task_time_attributes(cls, task_time): pass
+ def enable_editing_task_time(cls, task): pass
+ def disable_editing_task(cls): pass
+ def get_task_time_attributes(cls): pass
+ def load_task_resources(cls,resources): pass
+ def load_resources(cls): pass
+ def get_task_inputs(cls, task): pass
+ def load_task_inputs(cls, inputs): pass
+ def load_task_outputs(cls, outputs): pass
+ def get_highlighted_task(cls): pass
+ def get_task_outputs(cls, task): pass
+ def get_task_resources(cls, task):pass
+ def enable_editing_work_calendar_times(cls, work_calendar): pass
+ def load_work_calendar_attributes(cls, work_calendar): pass
+ def enable_editing_work_calendar(cls, work_calendar): pass
+ def disable_editing_work_calendar(cls): pass
+ def get_work_calendar_attributes(cls): pass
+ def load_work_time_attributes(cls, work_time): pass
+ def enable_editing_work_time(cls, work_time): pass
+ def get_work_time_attributes(cls): pass
+ def get_recurrence_pattern_attributes(cls, recurrence_pattern): pass
+ def disable_editing_work_time(cls): pass
+ def get_recurrence_pattern_times(cls): pass
+ def reset_time_period(cls): pass
+ def enable_editing_task_calendar(cls, task): pass
+ def enable_editing_task_sequence(cls, task): pass
+ def disable_editing_task_time(cls): pass
+ def load_rel_sequence_attributes(cls, rel_sequence): pass
+ def enable_editing_rel_sequence_attributes(cls, rel_sequence): pass
+ def load_lag_time_attributes(cls, lag_time): pass
+ def enable_editing_sequence_lag_time(cls, rel_sequence): pass
+ def get_rel_sequence_attributes(cls): pass
+ def disable_editing_rel_sequence(cls): pass
+ def get_lag_time_attributes(cls): pass
+ def select_task_products(cls, products): pass
+ def add_task_column(cls, column_type, name, data_type): pass
+ def remove_task_column(cls, name): pass
+ def set_task_sort_column(cls, column): pass
+ def find_related_output_tasks(cls, column): pass
+ def get_root_task(cls, task): pass
+ def get_task_work_schedule(cls, task): pass
+ def is_work_schedule_active(cls, work_schedule): pass
+ def highlight_task(cls, task): pass
+
@interface
diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py
index 261812284d..62e6410c98 100644
--- a/src/blenderbim/blenderbim/tool/__init__.py
+++ b/src/blenderbim/blenderbim/tool/__init__.py
@@ -40,9 +40,9 @@ from blenderbim.tool.project import Project
from blenderbim.tool.pset import Pset
from blenderbim.tool.qto import Qto
from blenderbim.tool.root import Root
+from blenderbim.tool.sequence import Sequence
from blenderbim.tool.spatial import Spatial
from blenderbim.tool.structural import Structural
-from blenderbim.tool.sequence import Sequence
from blenderbim.tool.style import Style
from blenderbim.tool.surveyor import Surveyor
from blenderbim.tool.system import System
diff --git a/src/blenderbim/blenderbim/tool/sequence.py b/src/blenderbim/blenderbim/tool/sequence.py
index ac2560f4f1..2c27335196 100644
--- a/src/blenderbim/blenderbim/tool/sequence.py
+++ b/src/blenderbim/blenderbim/tool/sequence.py
@@ -15,8 +15,11 @@
#
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
-
+from dateutil import parser
import bpy
+import re
+import isodate
+from datetime import datetime
import ifcopenshell
import json
import blenderbim.core.tool
@@ -26,40 +29,6 @@ import blenderbim.bim.module.sequence.helper as helper
class Sequence(blenderbim.core.tool.Sequence):
- @classmethod
- def load_work_plans(cls):
- props = bpy.context.scene.BIMWorkPlanProperties
- props.work_plans.clear()
- for work_plan in tool.Ifc.get().by_type("IfcWorkPlan"):
- new = props.work_plans.add()
- new.ifc_definition_id = work_plan.id()
- new.name = work_plan.Name or "Unnamed"
-
- @classmethod
- def enable_editing_work_plan(cls, work_plan):
- if work_plan:
- bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
- bpy.context.scene.BIMWorkPlanProperties.editing_type = "ATTRIBUTES"
-
- @classmethod
- def disable_editing_work_plan(cls):
- bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = 0
-
- @classmethod
- def get_current_ifc_work_plan(cls):
- return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id)
-
- @classmethod
- def load_work_plan_attributes(cls, work_plan):
- def callback(name, prop, data):
- if name in ["CreationDate", "StartTime", "FinishTime"]:
- prop.string_value = "" if prop.is_null else data[name]
- return True
-
- props = bpy.context.scene.BIMWorkPlanProperties
- props.work_plan_attributes.clear()
- blenderbim.bim.helper.import_attributes2(work_plan, props.work_plan_attributes, callback)
-
@classmethod
def get_work_plan_attributes(cls):
def callback(attributes, prop):
@@ -78,3 +47,708 @@ class Sequence(blenderbim.core.tool.Sequence):
props = bpy.context.scene.BIMWorkPlanProperties
return blenderbim.bim.helper.export_attributes(props.work_plan_attributes, callback)
+
+ @classmethod
+ def load_work_plan_attributes(cls, work_plan):
+ def callback(name, prop, data):
+ if name in ["CreationDate", "StartTime", "FinishTime"]:
+ prop.string_value = "" if prop.is_null else data[name]
+ return True
+
+ props = bpy.context.scene.BIMWorkPlanProperties
+ props.work_plan_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(work_plan, props.work_plan_attributes, callback)
+
+ @classmethod
+ def enable_editing_work_plan(cls, work_plan):
+ if work_plan:
+ bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
+ bpy.context.scene.BIMWorkPlanProperties.editing_type = "ATTRIBUTES"
+
+ @classmethod
+ def disable_editing_work_plan(cls):
+ bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = 0
+
+ @classmethod
+ def enable_editing_work_plan_schedules(cls, work_plan):
+ if work_plan:
+ bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
+ bpy.context.scene.BIMWorkPlanProperties.editing_type = "SCHEDULES"
+
+ @classmethod
+ def get_work_schedule_attributes(cls):
+ def callback(attributes, prop):
+ if "Date" in prop.name or "Time" in prop.name:
+ if prop.is_null:
+ attributes[prop.name] = None
+ return True
+ attributes[prop.name] = helper.parse_datetime(prop.string_value)
+ return True
+ elif prop.name == "Duration" or prop.name == "TotalFloat":
+ if prop.is_null:
+ attributes[prop.name] = None
+ return True
+ attributes[prop.name] = helper.parse_duration(prop.string_value)
+ return True
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ return blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, callback)
+
+ @classmethod
+ def load_work_schedule_attributes(cls, work_schedule):
+ def callback(name, prop, data):
+ if name in ["CreationDate", "StartTime", "FinishTime"]:
+ prop.string_value = "" if prop.is_null else data[name]
+ return True
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.work_schedule_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback)
+
+ @classmethod
+ def enable_editing_work_schedule(cls, work_schedule):
+ bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = work_schedule.id()
+ bpy.context.scene.BIMWorkScheduleProperties.editing_type = "WORK_SCHEDULE"
+
+ @classmethod
+ def disable_editing_work_schedule(cls):
+ bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
+
+ @classmethod
+ def enable_editing_work_schedule_tasks(cls, work_schedule):
+ if work_schedule:
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_work_schedule_id = work_schedule.id()
+ props.editing_type = "TASKS"
+
+ @classmethod
+ def create_task_tree(cls, work_schedule):
+ def get_work_schedule_root_tasks(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_work_schedule_root_tasks(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}
+ related_objects_ids = sorted(cls.sort_keys, key=cls.natural_sort_key)
+ if props.is_sort_reversed:
+ related_objects_ids.reverse()
+ for related_object_id in related_objects_ids:
+ cls.create_new_task_li(related_object_id, 0)
+
+ @classmethod
+ def create_new_task_li(cls, related_object_id, level_index):
+ task = tool.Ifc.get().by_id(related_object_id)
+ new = bpy.context.scene.BIMTaskTreeProperties.tasks.add()
+ new.ifc_definition_id = related_object_id
+ new.is_expanded = related_object_id not in cls.contracted_tasks
+ new.level_index = level_index
+ if task.IsNestedBy:
+ new.has_children = True
+ if new.is_expanded:
+ cls.sort_keys = {subtask.id(): cls.get_sort_key(subtask) for subtask in helper.get_nested_tasks(task)}
+ related_object_ids = sorted(cls.sort_keys, key=cls.natural_sort_key)
+ if bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed:
+ related_object_ids.reverse()
+ for related_object_id in related_object_ids:
+ cls.create_new_task_li(related_object_id, level_index + 1)
+
+ @classmethod
+ def natural_sort_key(cls, i, _nsre=re.compile("([0-9]+)")):
+ s = cls.sort_keys[i]
+ return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
+
+ @classmethod
+ def get_sort_key(cls, task):
+ # Sorting only applies to actual tasks, not the WBS
+ for rel in task.IsNestedBy:
+ for object in rel.RelatedObjects:
+ if object.is_a("IfcTask"):
+ return "0000000000" + (task.Identification or "")
+ if not bpy.context.scene.BIMWorkScheduleProperties.sort_column:
+ return task.Identification or ""
+ column_type, name = bpy.context.scene.BIMWorkScheduleProperties.sort_column.split(".")
+ if column_type == "IfcTask":
+ return task.Name or ""
+ elif column_type == "IfcTaskTime" and task.TaskTime:
+ return task.TaskTime.Name or ""
+ return task.Identification or ""
+
+ @classmethod
+ def load_task_properties(cls, task=None):
+ def canonicalise_time(time):
+ if not time:
+ return "-"
+ return time.strftime("%d/%m/%y")
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ task_props = bpy.context.scene.BIMTaskTreeProperties
+ props.is_task_update_enabled = False
+
+ for item in task_props.tasks:
+ # if task and item.ifc_definition_id != task.id():
+ # continue
+ task = tool.Ifc.get().by_id(item.ifc_definition_id)
+ item.name = task.Name or "Unnamed"
+ item.identification = task.Identification or "XXX"
+ if props.active_task_id:
+ item.is_predecessor = props.active_task_id in [rel.RelatedProcess.id() for rel in task.IsPredecessorTo]
+ item.is_successor = props.active_task_id in [rel.RelatingProcess.id() for rel in task.IsSuccessorFrom]
+
+ calendar = ifcopenshell.util.sequence.derive_calendar(task)
+ if task.HasAssignments:
+ for rel in task.HasAssignments:
+ if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcWorkCalendar"):
+ item.calendar = calendar.Name if calendar else ""
+ else:
+ item.calendar = ""
+ item.derived_calendar = calendar.Name if calendar else ""
+
+ if task.TaskTime:
+ task_time = task.TaskTime
+ item.start = (
+ canonicalise_time(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleStart))
+ if task_time.ScheduleStart
+ else "-"
+ )
+ item.finish = (
+ canonicalise_time(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleFinish))
+ if task_time.ScheduleFinish
+ else "-"
+ )
+ item.duration = (
+ isodate.duration_isoformat(ifcopenshell.util.date.ifc2datetime(task_time.ScheduleDuration))
+ if task_time.ScheduleDuration
+ else "-"
+ )
+ else:
+ derived_start = helper.derive_date(task, "ScheduleStart", is_earliest=True)
+ derived_finish = helper.derive_date(task, "ScheduleFinish", is_latest=True)
+ item.derived_start = canonicalise_time(derived_start) if derived_start else ""
+ item.derived_finish = canonicalise_time(derived_finish) if derived_finish else ""
+ if derived_start and derived_finish and calendar:
+ derived_duration = ifcopenshell.util.sequence.count_working_days(
+ derived_start, derived_finish, calendar
+ )
+ item.derived_duration = f"P{derived_duration}D"
+ item.start = "-"
+ item.finish = "-"
+ item.duration = "-"
+
+ bpy.context.scene.BIMWorkScheduleProperties.is_task_update_enabled = True
+
+ @classmethod
+ def hide_work_plan_hints(cls):
+ bpy.context.scene.BIMWorkPlanProperties.show_hints = False
+
+ @classmethod
+ def show_work_plan_hints(cls):
+ bpy.context.scene.BIMWorkPlanProperties.show_hints = True
+
+ @classmethod
+ def get_active_work_schedule(cls):
+ if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
+ return None
+ return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id)
+
+ @classmethod
+ def get_selected_resource(cls):
+ if bpy.context.scene.BIMResourceTreeProperties.resources:
+ selected_resource_id = bpy.context.scene.BIMResourceTreeProperties.resources[
+ bpy.context.scene.BIMResourceProperties.active_resource_index
+ ].ifc_definition_id
+ return tool.Ifc.get().by_id(selected_resource_id)
+
+ @classmethod
+ def expand_task(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ contracted_tasks = json.loads(props.contracted_tasks)
+ contracted_tasks.remove(task.id())
+ props.contracted_tasks = json.dumps(contracted_tasks)
+
+ @classmethod
+ def contract_task(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ contracted_tasks = json.loads(props.contracted_tasks)
+ contracted_tasks.append(task.id())
+ props.contracted_tasks = json.dumps(contracted_tasks)
+
+ @classmethod
+ def disable_work_schedule(cls):
+ bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
+
+ @classmethod
+ def disable_selecting_deleted_task(cls):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ if props.active_task_id not in [
+ task.ifc_definition_id for task in bpy.context.scene.BIMTaskTreeProperties.tasks
+ ]: # Task was deleted
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+
+ @classmethod
+ def get_checked_tasks(cls):
+ return [
+ tool.Ifc.get().by_id(task.ifc_definition_id)
+ for task in bpy.context.scene.BIMTaskTreeProperties.tasks
+ if task.is_selected
+ ]
+
+ @classmethod
+ def get_task_attribute_value(cls, attribute_name):
+ return bpy.context.scene.BIMWorkScheduleProperties.task_attributes.get(attribute_name).get_value()
+
+ @classmethod
+ def get_active_task(cls):
+ return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_task_id)
+
+ @classmethod
+ def get_active_work_time(cls):
+ return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id)
+
+ @classmethod
+ def get_task_time(cls, task):
+ return task.TaskTime if task.TaskTime else None
+
+ @classmethod
+ def load_task_attributes(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.task_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(task, props.task_attributes)
+
+ @classmethod
+ def get_selected_products(cls):
+ return [
+ tool.Ifc.get_entity(obj)
+ for obj in bpy.context.selected_objects
+ if obj.BIMObjectProperties.ifc_definition_id
+ ] or []
+
+ @classmethod
+ def enable_editing_task(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_task_id = task.id()
+ props.editing_task_type = "ATTRIBUTES"
+
+ @classmethod
+ def get_task_attributes(cls):
+ return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.task_attributes)
+
+ @classmethod
+ def load_task_time_attributes(cls, task_time):
+ def callback(name, prop, data):
+ if prop.data_type == "string":
+ if name == "ScheduleDuration" and data[name] and isinstance(data[name], str):
+ time_object = ifcopenshell.util.date.ifc2datetime(data[name])
+ minutes = int(round(time_object.seconds/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_hours = round(hours)
+ bpy.context.scene.BIMDuration.duration_minutes = minutes # should consider years, months and seconds.
+ if isinstance(data[name], datetime):
+ prop.string_value = "" if prop.is_null else data[name].isoformat()
+ if name == "ScheduleDuration":
+ 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_minutes = data[name].seconds/60 % 60
+ return True
+ elif isinstance(data[name], isodate.Duration):
+ prop.string_value = (
+ "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
+ )
+ return True
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.task_time_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback)
+
+ @classmethod
+ def enable_editing_task_time(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_task_id = task.id()
+ props.active_task_time_id = task.TaskTime.id()
+ props.editing_task_type = "TASKTIME"
+
+ @classmethod
+ def disable_editing_task(cls):
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+
+ @classmethod
+ def get_task_time_attributes(cls):
+ def callback(attributes, prop):
+ if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
+ if prop.is_null:
+ attributes[prop.name] = None
+ return True
+ attributes[prop.name] = helper.parse_datetime(prop.string_value)
+ return True
+ elif prop.name == "ScheduleDuration":
+ if prop.is_null:
+ return True
+ # TODO make this parse PT32 as P4D
+ attributes[prop.name] = helper.parse_duration(prop.string_value)
+ dprops= bpy.context.scene.BIMDuration
+ duration_days = dprops.duration_days if dprops.duration_days else 0
+ duration_hours = dprops.duration_hours if dprops.duration_hours else 0
+ duration_minutes = dprops.duration_minutes if dprops.duration_minutes else 0
+ dprops.duration_days = 0
+ dprops.duration_hours = 0
+ dprops.duration_minutes = 0
+ attributes[prop.name] = helper.parse_duration(f"P{duration_days}DT{duration_hours}H{duration_minutes}M")
+ return True
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ return blenderbim.bim.helper.export_attributes(props.task_time_attributes, callback)
+
+ @classmethod
+ def load_task_resources(cls, resources):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.task_resources.clear()
+ if resources:
+ for resource in resources:
+ new = props.task_resources.add()
+ new.ifc_definition_id = resource.id()
+ new.name = resource.Name or "Unnamed"
+
+ @classmethod
+ def load_resources(cls):
+ bpy.ops.bim.load_resources() # remove and refactor
+
+ @classmethod
+ def get_task_inputs(cls, task):
+ inputs = []
+ for rel in task.OperatesOn:
+ for object in rel.RelatedObjects:
+ if object.is_a("IfcProduct"):
+ inputs.append(object)
+ return inputs
+
+ @classmethod
+ def load_task_inputs(cls, inputs):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.task_inputs.clear()
+ for input in inputs:
+ new = props.task_inputs.add()
+ new.ifc_definition_id = input.id()
+ new.name = input.Name or "Unnamed"
+
+ @classmethod
+ def load_task_outputs(cls, outputs):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.task_outputs.clear()
+ if outputs:
+ for output in outputs:
+ new = props.task_outputs.add()
+ new.ifc_definition_id = output.id()
+ new.name = output.Name or "Unnamed"
+
+ @classmethod
+ def get_highlighted_task(cls):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ task_props = bpy.context.scene.BIMTaskTreeProperties
+ return tool.Ifc.get().by_id(task_props.tasks[props.active_task_index].ifc_definition_id)
+
+ @classmethod
+ def get_task_outputs(cls, task):
+ return [rel.RelatingProduct for rel in task.HasAssignments if rel.is_a("IfcRelAssignsToProduct")]
+
+ @classmethod
+ def get_task_resources(cls, task):
+ resources = []
+ for rel in task.OperatesOn:
+ for object in rel.RelatedObjects:
+ if object.is_a("IfcResource"):
+ resources.append(object)
+ return resources
+
+ @classmethod
+ def enable_editing_work_calendar_times(cls, work_calendar):
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ props.active_work_calendar_id = work_calendar.id()
+ props.editing_type = "WORKTIMES"
+
+ @classmethod
+ def load_work_calendar_attributes(cls, work_calendar):
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ props.work_calendar_attributes.clear()
+ return blenderbim.bim.helper.import_attributes2(work_calendar, props.work_calendar_attributes)
+
+ @classmethod
+ def enable_editing_work_calendar(cls, work_calendar):
+ bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = work_calendar.id()
+ bpy.context.scene.BIMWorkCalendarProperties.editing_type = "ATTRIBUTES"
+
+ @classmethod
+ def disable_editing_work_calendar(cls):
+ bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0
+
+ @classmethod
+ def get_work_calendar_attributes(cls):
+ return blenderbim.bim.helper.export_attributes(
+ bpy.context.scene.BIMWorkCalendarProperties.work_calendar_attributes
+ )
+
+ @classmethod
+ def load_work_time_attributes(cls, work_time):
+ def callback(name, prop, data):
+ if name in ["Start", "Finish"]:
+ prop.string_value = "" if prop.is_null else data[name]
+ return True
+
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ props.work_time_attributes.clear()
+
+ blenderbim.bim.helper.import_attributes2(work_time, props.work_time_attributes, callback)
+
+ @classmethod
+ def enable_editing_work_time(cls, work_time):
+ def initialise_recurrence_components(props):
+ if len(props.day_components) == 0:
+ for i in range(0, 31):
+ new = props.day_components.add()
+ new.name = str(i + 1)
+ if len(props.weekday_components) == 0:
+ for d in ["M", "T", "W", "T", "F", "S", "S"]:
+ new = props.weekday_components.add()
+ new.name = d
+ if len(props.month_components) == 0:
+ for m in ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]:
+ new = props.month_components.add()
+ new.name = m
+
+ def load_recurrence_pattern_data(work_time, props):
+ props.position = 0
+ props.interval = 0
+ props.occurrences = 0
+ props.start_time = ""
+ props.end_time = ""
+ for component in props.day_components:
+ component.is_specified = False
+ for component in props.weekday_components:
+ component.is_specified = False
+ for component in props.month_components:
+ component.is_specified = False
+ if not work_time.RecurrencePattern:
+ return
+ recurrence_pattern = work_time.RecurrencePattern
+ for attribute in ["Position", "Interval", "Occurrences"]:
+ if getattr(recurrence_pattern, attribute):
+ setattr(props, attribute.lower(), getattr(recurrence_pattern, attribute))
+ for component in recurrence_pattern.DayComponent or []:
+ props.day_components[component - 1].is_specified = True
+ for component in recurrence_pattern.WeekdayComponent or []:
+ props.weekday_components[component - 1].is_specified = True
+ for component in recurrence_pattern.MonthComponent or []:
+ props.month_components[component - 1].is_specified = True
+
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ initialise_recurrence_components(props)
+ load_recurrence_pattern_data(work_time, props)
+ props.active_work_time_id = work_time.id()
+ props.editing_type = "WORKTIMES"
+
+ @classmethod
+ def get_work_time_attributes(cls):
+ def callback(attributes, prop):
+ if "Start" in prop.name or "Finish" in prop.name:
+ if prop.is_null:
+ attributes[prop.name] = None
+ return True
+ attributes[prop.name] = helper.parse_datetime(prop.string_value)
+ return True
+
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ return blenderbim.bim.helper.export_attributes(props.work_time_attributes, callback)
+
+ @classmethod
+ def get_recurrence_pattern_attributes(cls, recurrence_pattern):
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ attributes = {
+ "Interval": props.interval if props.interval > 0 else None,
+ "Occurrences": props.occurrences if props.occurrences > 0 else None,
+ }
+ applicable_data = {
+ "DAILY": ["Interval", "Occurrences"],
+ "WEEKLY": ["WeekdayComponent", "Interval", "Occurrences"],
+ "MONTHLY_BY_DAY_OF_MONTH": ["DayComponent", "Interval", "Occurrences"],
+ "MONTHLY_BY_POSITION": ["WeekdayComponent", "Position", "Interval", "Occurrences"],
+ "BY_DAY_COUNT": ["Interval", "Occurrences"],
+ "BY_WEEKDAY_COUNT": ["WeekdayComponent", "Interval", "Occurrences"],
+ "YEARLY_BY_DAY_OF_MONTH": ["DayComponent", "MonthComponent", "Interval", "Occurrences"],
+ "YEARLY_BY_POSITION": ["WeekdayComponent", "MonthComponent", "Position", "Interval", "Occurrences"],
+ }
+ if "Position" in applicable_data[recurrence_pattern.RecurrenceType]:
+ attributes["Position"] = props.position if props.position != 0 else None
+ if "DayComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
+ attributes["DayComponent"] = [i + 1 for i, c in enumerate(props.day_components) if c.is_specified]
+ if "WeekdayComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
+ attributes["WeekdayComponent"] = [i + 1 for i, c in enumerate(props.weekday_components) if c.is_specified]
+ if "MonthComponent" in applicable_data[recurrence_pattern.RecurrenceType]:
+ attributes["MonthComponent"] = [i + 1 for i, c in enumerate(props.month_components) if c.is_specified]
+ return attributes
+
+ @classmethod
+ def disable_editing_work_time(cls):
+ bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id = 0
+
+ @classmethod
+ def get_recurrence_pattern_times(cls):
+ props = bpy.context.scene.BIMWorkCalendarProperties
+ try:
+ start_time = parser.parse(props.start_time)
+ end_time = parser.parse(props.end_time)
+ return start_time, end_time
+ except:
+ return # improve UI / refactor to add user hints
+
+ @classmethod
+ def reset_time_period(cls):
+ bpy.context.scene.BIMWorkCalendarProperties.start_time = ""
+ bpy.context.scene.BIMWorkCalendarProperties.end_time = ""
+
+ @classmethod
+ def enable_editing_task_calendar(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_task_id = task.id()
+ props.editing_task_type = "CALENDAR"
+
+ @classmethod
+ def enable_editing_task_sequence(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_task_id = task.id()
+ props.editing_task_type = "SEQUENCE"
+
+ @classmethod
+ def disable_editing_task_time(cls):
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
+ bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
+
+ @classmethod
+ def load_rel_sequence_attributes(cls, rel_sequence):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.sequence_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes)
+
+ @classmethod
+ def enable_editing_rel_sequence_attributes(cls, rel_sequence):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_sequence_id = rel_sequence.id()
+ props.editing_sequence_type = "ATTRIBUTES"
+
+
+ @classmethod
+ def load_lag_time_attributes(cls, lag_time):
+ def callback(name, prop, data):
+ if name == "LagValue":
+ prop = bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes.add()
+ prop.name = name
+ prop.is_null = data[name] is None
+ prop.is_optional = False
+ prop.data_type = "string"
+ prop.string_value = (
+ "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name].wrappedValue, "IfcDuration")
+ )
+ return True
+
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.lag_time_attributes.clear()
+ blenderbim.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback)
+
+ @classmethod
+ def enable_editing_sequence_lag_time(cls, rel_sequence):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.active_sequence_id = rel_sequence.id()
+ props.editing_sequence_type = "LAG_TIME"
+
+ @classmethod
+ def get_rel_sequence_attributes(cls):
+ return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.sequence_attributes)
+
+ @classmethod
+ def disable_editing_rel_sequence(cls):
+ bpy.context.scene.BIMWorkScheduleProperties.active_sequence_id = 0
+
+ @classmethod
+ def get_lag_time_attributes(cls):
+ return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes)
+
+ @classmethod
+ def select_task_products(cls, products):
+ for obj in bpy.context.visible_objects:
+ obj.select_set(False)
+ if obj.BIMObjectProperties.ifc_definition_id in [product.id() for product in products]:
+ obj.select_set(True)
+
+ @classmethod
+ def add_task_column(cls, column_type, name, data_type):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ new = props.columns.add()
+ new.name = f"{column_type}.{name}"
+ new.data_type = data_type
+
+ @classmethod
+ def remove_task_column(cls, name):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.columns.remove(props.columns.find(name))
+
+ @classmethod
+ def set_task_sort_column(cls, column):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ props.sort_column = column
+
+ @classmethod
+ def find_related_input_tasks(cls, object):
+ related_tasks = []
+ for assignment in object.HasAssignments:
+ if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess.is_a("IfcTask"):
+ related_tasks.append(assignment.RelatingProcess)
+ return related_tasks
+
+ @classmethod
+ def find_related_output_tasks(cls, object):
+ related_tasks = []
+ for reference in object.ReferencedBy:
+ if reference.is_a("IfcRelAssignsToProduct") and reference.RelatedObjects[0].is_a("IfcTask"):
+ related_tasks.append(reference.RelatedObjects[0])
+ return related_tasks
+
+ @classmethod
+ def get_root_task(cls, task):
+ return (
+ task
+ if not task.Nests or not task.Nests[0].RelatingObject.is_a("IfcTask")
+ else cls.get_root_task(task.Nests[0].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
+ def is_work_schedule_active(cls, work_schedule):
+ return (
+ True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False
+ )
+
+ @classmethod
+ def highlight_task(cls, task):
+ props = bpy.context.scene.BIMWorkScheduleProperties
+ task_props = bpy.context.scene.BIMTaskTreeProperties
+ task_id = task.id()
+ task_index = [task.ifc_definition_id for task in task_props.tasks].index(task_id)
+ if task_index:
+ props.active_task_index = task_index
diff --git a/src/blenderbim/test/bim/feature/pset.feature b/src/blenderbim/test/bim/feature/pset.feature
index 4101822d3b..4515e57e75 100644
--- a/src/blenderbim/test/bim/feature/pset.feature
+++ b/src/blenderbim/test/bim/feature/pset.feature
@@ -57,7 +57,7 @@ Scenario: Enable pset editing - work schedule
Given an empty IFC project
And I press "bim.add_work_schedule"
And the variable "work_schedule" is "{ifc}.by_type('IfcWorkSchedule')[0].id()"
- And I press "bim.enable_editing_tasks(work_schedule={work_schedule})"
+ And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I set "scene.WorkSchedulePsetProperties.pset_name" to "Pset_WorkControlCommon"
And I press "bim.add_pset(obj_type='WorkSchedule')"
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
@@ -90,7 +90,7 @@ Scenario: Enable pset editing - task
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 I set "scene.TaskPsetProperties.qto_name" to "Qto_TaskBaseQuantities"
And I press "bim.add_qto(obj_type='Task')"
diff --git a/src/blenderbim/test/bim/feature/sequence.feature b/src/blenderbim/test/bim/feature/sequence.feature
index f2f12e8a2d..ee3c42e7ce 100644
--- a/src/blenderbim/test/bim/feature/sequence.feature
+++ b/src/blenderbim/test/bim/feature/sequence.feature
@@ -4,13 +4,225 @@ Feature: Sequence
Scenario: Add work plan
Given an empty IFC project
When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ Then nothing happens
+
+Scenario: Add Work schedule
+ 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()"
+ Then nothing happens
+
+Scenario: Remove work plan
+ Given an empty IFC project
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ When I press "bim.remove_work_plan(work_plan={work_plan})"
+ Then nothing happens
+
+Scenario: Remove work schedule
+ 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()"
+ When I press "bim.remove_work_schedule(work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Enable Editing Work Plan
+ Given an empty IFC project
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ When I press "bim.enable_editing_work_plan(work_plan={work_plan})"
+ Then nothing happens
+
+Scenario: Edit Work Plan
+ Given an empty IFC project
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan(work_plan={work_plan})"
+ And I set "scene.BIMWorkPlanProperties.work_plan_attributes.get('Name').string_value" to "FooPlan"
+ When I press "bim.edit_work_plan()"
+ Then nothing happens
+
+Scenario: Edit Work Schedule
+ 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(work_schedule={work_schedule})"
+ And I set "scene.BIMWorkScheduleProperties.work_schedule_attributes.get('Name').string_value" to "FooSchedule"
+ When I press "bim.edit_work_schedule()"
+ Then nothing happens
+
+Scenario: Disable Editing Work Plan
+ Given an empty IFC project
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan_schedules(work_plan={work_plan})"
+ When I press "bim.disable_editing_work_plan"
+ Then nothing happens
+
+Scenario: Assign work schedule
+ 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()"
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan_schedules(work_plan={work_plan})"
+ And I press "bim.assign_work_schedule(work_plan={work_plan}, work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Unassign work schedule
+ 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()"
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan_schedules(work_plan={work_plan})"
+ And I press "bim.assign_work_schedule(work_plan={work_plan}, work_schedule={work_schedule})"
+ And I press "bim.unassign_work_schedule(work_plan={work_plan}, work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Delete assigned work schedule
+ 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()"
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan_schedules(work_plan={work_plan})"
+ And I press "bim.assign_work_schedule(work_plan={work_plan}, work_schedule={work_schedule})"
+ When I press "bim.remove_work_schedule(work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Remove Assigned work schedule
+ 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()"
+ When I press "bim.add_work_plan"
+ And the variable "work_plan" is "IfcStore.get_file().by_type('IfcWorkPlan')[0].id()"
+ And I press "bim.enable_editing_work_plan_schedules(work_plan={work_plan})"
+ When I press "bim.assign_work_schedule(work_plan={work_plan}, work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Add work calendar
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ Then nothing happens
+
+Scenario: Remove work calendar
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.remove_work_calendar(work_calendar={work_calendar})"
+ Then nothing happens
+
+Scenario: Edit work calendar attributes
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar(work_calendar={work_calendar})"
+ And I set "scene.BIMWorkCalendarProperties.work_calendar_attributes.get('Name').string_value" to "FooCalendar"
+ When I press "bim.edit_work_calendar()"
+ Then nothing happens
+
+Scenario: Enable editing calendar times
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ Then nothing happens
+
+Scenario: Add calendar Working Time
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="WorkingTimes")"
+ Then nothing happens
+
+Scenario: Add calendar Exception Time
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ And I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="ExceptionTimes")"
+ Then nothing happens
+
+Scenario: Enable editing Working Time Attributes
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="WorkingTimes")"
+ And the variable "work_time" is "IfcStore.get_file().by_type('IfcWorkTime')[0].id()"
+ When I press "bim.enable_editing_work_time(work_time={work_time})"
+
+Scenario: Edit Working Time Start and End Period
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type='WorkingTimes')"
+ And the variable "work_time" is "IfcStore.get_file().by_type('IfcWorkTime')[0].id()"
+ When I press "bim.enable_editing_work_time(work_time={work_time})"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Start').string_value" to "2021-01-01"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Finish').string_value" to "2022-01-01"
+ When I press "bim.edit_work_time()"
+ Then nothing happens
+
+Scenario: Add Working Time Period
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type='WorkingTimes')"
+ And the variable "work_time" is "IfcStore.get_file().by_type('IfcWorkTime')[0].id()"
+ When I press "bim.enable_editing_work_time(work_time={work_time})"
+ When I press "bim.assign_recurrence_pattern(work_time={work_time}, recurrence_type='DAILY')"
+ And the variable "recurrence_pattern" is "IfcStore.get_file().by_type('IfcRecurrencePattern')[0].id()"
+ And I set "scene.BIMWorkCalendarProperties.start_time" to "9AM"
+ And I set "scene.BIMWorkCalendarProperties.end_time" to "1PM"
+ When I press "bim.add_time_period(recurrence_pattern={recurrence_pattern})"
+ When I press "bim.edit_work_time()"
+ Then nothing happens
+
+Scenario: Edit Working Time start finish and time period
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type='WorkingTimes')"
+ And the variable "work_time" is "IfcStore.get_file().by_type('IfcWorkTime')[0].id()"
+ When I press "bim.enable_editing_work_time(work_time={work_time})"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Start').string_value" to "2021-01-01"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Finish').string_value" to "2022-01-01"
+ When I press "bim.assign_recurrence_pattern(work_time={work_time}, recurrence_type='DAILY')"
+ And the variable "recurrence_pattern" is "IfcStore.get_file().by_type('IfcRecurrencePattern')[0].id()"
+ And I set "scene.BIMWorkCalendarProperties.start_time" to "9AM"
+ And I set "scene.BIMWorkCalendarProperties.end_time" to "1PM"
+ When I press "bim.add_time_period(recurrence_pattern={recurrence_pattern})"
+ When I press "bim.edit_work_time()"
+ Then nothing happens
+
+
+Scenario: Unassign Calendar to task
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ And I press "bim.enable_editing_task_calendar(task={task})"
+ And I press "bim.edit_task_calendar(work_calendar={work_calendar}, task={task})"
+ When I press "bim.remove_task_calendar(work_calendar={work_calendar}, task={task})"
Then nothing happens
Scenario: Enable editing task
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
When I press "bim.enable_editing_task(task={task})"
@@ -20,7 +232,7 @@ Scenario: Copy task attribute
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
@@ -30,11 +242,86 @@ Scenario: Copy task attribute
And I press "bim.copy_task_attribute(name='Description')"
Then nothing happens
+Scenario: Unassign task Successor
+ 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})"
+ When I press "bim.add_summary_task(work_schedule={work_schedule})"
+ And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ And I press "bim.enable_editing_task(task={nested_task_one})"
+ And I press "bim.assign_successor(task={nested_task_two})"
+ When I press "bim.unassign_successor(task={nested_task_two})"
+ Then nothing happens
+
+Scenario: Edit time Lag
+ 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})"
+ When I press "bim.add_summary_task(work_schedule={work_schedule})"
+ And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ And I press "bim.enable_editing_task(task={nested_task_one})"
+ When I press "bim.assign_successor(task={nested_task_two})"
+ And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
+ When I press "bim.assign_lag_time(sequence={rel_sequence})"
+ And the variable "lag_time" is "IfcStore.get_file().by_type('IfcLagTime')[0].id()"
+ And I press "bim.enable_editing_sequence_lag_time(sequence={rel_sequence}, lag_time={lag_time})"
+ And I set "scene.BIMWorkScheduleProperties.lag_time_attributes.get('LagValue').string_value" to "P5D"
+ When I press "bim.edit_sequence_lag_time(lag_time={lag_time})"
+ Then nothing happens
+
+Scenario: Unassign time Lag
+ 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})"
+ When I press "bim.add_summary_task(work_schedule={work_schedule})"
+ And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ And I press "bim.enable_editing_task(task={nested_task_one})"
+ When I press "bim.assign_successor(task={nested_task_two})"
+ And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
+ When I press "bim.assign_lag_time(sequence={rel_sequence})"
+ And the variable "lag_time" is "IfcStore.get_file().by_type('IfcLagTime')[0].id()"
+ When I press "bim.unassign_lag_time(sequence={rel_sequence})"
+ Then nothing happens
+
+Scenario: Edit Sequence Relationship
+ 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})"
+ When I press "bim.add_summary_task(work_schedule={work_schedule})"
+ And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ And I press "bim.enable_editing_task(task={nested_task_one})"
+ When I press "bim.assign_successor(task={nested_task_two})"
+ And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
+ And I press "bim.enable_editing_sequence_attributes(sequence={rel_sequence})"
+ And I set "scene.BIMWorkScheduleProperties.sequence_attributes.get('SequenceType').enum_value" to "START_START"
+ When I press "bim.edit_sequence_attributes()"
+ Then nothing happens
+
Scenario: See the current frame date as text
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -50,7 +337,7 @@ Scenario: Animate the construction of a wall
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
@@ -65,7 +352,7 @@ Scenario: Animate the construction of a wall
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
- And I press "bim.assign_product(task={task}, relating_product='')"
+ And I press "bim.assign_product(task={task})"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -86,7 +373,7 @@ Scenario: Animate the demolition of a wall
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
@@ -101,7 +388,7 @@ Scenario: Animate the demolition of a wall
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
- And I press "bim.assign_product(task={task}, relating_product='')"
+ And I press "bim.assign_product(task={task}, relating_product=0)"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -125,7 +412,7 @@ Scenario: Animate the operation of a wall
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
@@ -140,7 +427,7 @@ Scenario: Animate the operation of a wall
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
- And I press "bim.assign_product(task={task}, relating_product='')"
+ And I press "bim.assign_product(task={task}, relating_product=0)"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -158,7 +445,7 @@ Scenario: Animate the movement of a wall
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
@@ -174,13 +461,13 @@ Scenario: Animate the movement of a wall
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/ToObject" is selected
- And I press "bim.assign_product(task={task}, relating_product='')"
+ And I press "bim.assign_product(task={task}, relating_product=0)"
When I add a cube
And I rename the object "Cube" to "FromObject"
And the object "FromObject" is selected
And I press "bim.assign_class"
And the object "IfcWall/FromObject" is selected
- And I press "bim.assign_process(task={task}, related_object_type='PRODUCT', related_object='')"
+ And I press "bim.assign_process(task={task}, related_object_type='PRODUCT', related_object=0)"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -212,7 +499,7 @@ Scenario: Animate the consumption of a wall
Given an empty IFC project
And 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_tasks(work_schedule={work_schedule})"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task_time(task={task})"
@@ -224,7 +511,7 @@ Scenario: Animate the consumption of a wall
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
And I press "bim.assign_class"
And the object "IfcWall/Cube" is selected
- And I press "bim.assign_process(task={task}, related_object_type='PRODUCT', related_object='')"
+ And I press "bim.assign_process(task={task}, related_object_type='PRODUCT', related_object=0)"
And I set "scene.BIMWorkScheduleProperties.visualisation_start" to "01/01/21"
And I set "scene.BIMWorkScheduleProperties.visualisation_finish" to "01/02/21"
And I set "scene.BIMWorkScheduleProperties.speed_types" to "FRAME_SPEED"
@@ -243,3 +530,135 @@ Scenario: Animate the consumption of a wall
Then "scene.objects.get('IfcWall/Cube').color" is "[0.0, 0.0, 0.0, 1]"
Then "scene.objects.get('IfcWall/Cube').hide_viewport" is "True"
Then "scene.objects.get('IfcWall/Cube').hide_render" is "True"
+
+
+Scenario: Generate Gantt Chart
+ Given an empty IFC project
+ And 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ And I press "bim.enable_editing_task(task={task})"
+ And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
+ And I press "bim.edit_task"
+ And I press "bim.enable_editing_task_time(task={task})"
+ 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 press "bim.edit_task_time"
+ And I press "bim.generate_gantt_chart(work_schedule={work_schedule})"
+ Then nothing happens
+
+Scenario: Edit task with calendar
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_three" is "IfcStore.get_file().by_type('IfcTask')[3].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="WorkingTimes")"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="ExceptionTimes")"
+ And I press "bim.disable_editing_work_calendar"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_one})"
+ And I press "bim.assign_successor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_three})"
+ And I press "bim.assign_predecessor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_time(task={nested_task_three})"
+ 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 press "bim.edit_task_time"
+ And I press "bim.enable_editing_task_calendar(task={task})"
+ And I press "bim.edit_task_calendar(work_calendar={work_calendar}, task={task})"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_one})"
+ And I press "bim.disable_editing_task()"
+ Then nothing happens
+
+
+Scenario: Assign task calendar with Working Time
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_three" is "IfcStore.get_file().by_type('IfcTask')[3].id()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="WorkingTimes")"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="ExceptionTimes")"
+ And I press "bim.disable_editing_work_calendar"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_one})"
+ And I press "bim.assign_successor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_three})"
+ And I press "bim.assign_predecessor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_calendar(task={task})"
+ And I press "bim.edit_task_calendar(work_calendar={work_calendar}, task={nested_task_three})"
+ And I press "bim.disable_editing_task()"
+ And I press "bim.enable_editing_task_time(task={nested_task_three})"
+ 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 press "bim.edit_task_time"
+ Then nothing happens
+
+Scenario: Assign task calendar with no working time
+ Given an empty IFC project
+ When I press "bim.add_work_calendar"
+ And the variable "work_calendar" is "IfcStore.get_file().by_type('IfcWorkCalendar')[0].id()"
+ 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 "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
+ When I press "bim.add_task(task={task})"
+ And the variable "nested_task_three" is "IfcStore.get_file().by_type('IfcTask')[3].id()"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_one})"
+ And I press "bim.assign_successor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_sequence(task={nested_task_three})"
+ And I press "bim.assign_predecessor(task={nested_task_two})"
+ And I press "bim.disable_editing_task"
+ And I press "bim.enable_editing_task_calendar(task={task})"
+ And I press "bim.edit_task_calendar(work_calendar={work_calendar}, task={nested_task_three})"
+ And I press "bim.disable_editing_task()"
+ When I press "bim.enable_editing_work_calendar_times(work_calendar={work_calendar})"
+ When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="WorkingTimes")"
+ And the variable "work_time" is "IfcStore.get_file().by_type('IfcWorkTime')[0].id()"
+ When I press "bim.enable_editing_work_time(work_time={work_time})"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Start').string_value" to "01-08-2021"
+ And I set "scene.BIMWorkCalendarProperties.work_time_attributes.get('Finish').string_value" to "01-08-2022"
+ When I press "bim.assign_recurrence_pattern(work_time={work_time}, recurrence_type='DAILY')"
+ And the variable "recurrence_pattern" is "IfcStore.get_file().by_type('IfcRecurrencePattern')[0].id()"
+ And I set "scene.BIMWorkCalendarProperties.start_time" to "9AM"
+ And I set "scene.BIMWorkCalendarProperties.end_time" to "1PM"
+ When I press "bim.add_time_period(recurrence_pattern={recurrence_pattern})"
+ And I press "bim.edit_work_time"
+ # When I press "bim.add_work_time(work_calendar={work_calendar}, time_type="ExceptionTimes")"
+ And I press "bim.disable_editing_work_calendar"
+ And I press "bim.enable_editing_task_time(task={nested_task_three})"
+ 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 press "bim.edit_task_time"
+ Then nothing happens
\ No newline at end of file
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
index 834a32661d..a3eda30f38 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/cascade_schedule.py
@@ -62,7 +62,7 @@ class Usecase:
predecessor = rel.RelatingProcess
predecessor_duration = (
ifcopenshell.util.date.ifc2datetime(predecessor.TaskTime.ScheduleDuration)
- if predecessor.TaskTime.ScheduleDuration
+ if predecessor.TaskTime and predecessor.TaskTime.ScheduleDuration
else datetime.timedelta()
)
if rel.SequenceType == "FINISH_START":
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py
index 4996460308..d13adbd0e5 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py
@@ -21,7 +21,6 @@ import ifcopenshell.util.date
class Data:
is_loaded = False
- work_plans = {}
work_schedules = {}
work_calendars = {}
work_times = {}
@@ -35,7 +34,6 @@ class Data:
@classmethod
def purge(cls):
cls.is_loaded = False
- cls.work_plans = {}
cls.work_schedules = {}
cls.work_calendars = {}
cls.work_times = {}
@@ -51,7 +49,6 @@ class Data:
cls._file = file
if not cls._file:
return
- cls.load_work_plans()
cls.load_work_schedules()
cls.load_work_calendars()
cls.load_work_times()
@@ -63,23 +60,6 @@ class Data:
cls.load_sequences()
cls.is_loaded = True
- @classmethod
- def load_work_plans(cls):
- cls.work_plans = {}
- for work_plan in cls._file.by_type("IfcWorkPlan"):
- data = work_plan.get_info()
- del data["OwnerHistory"]
- if data["Creators"]:
- data["Creators"] = [p.id() for p in data["Creators"]]
- data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"])
- data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"])
- if data["FinishTime"]:
- data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"])
- data["IsDecomposedBy"] = []
- for rel in work_plan.IsDecomposedBy:
- data["IsDecomposedBy"].extend([o.id() for o in rel.RelatedObjects])
- cls.work_plans[work_plan.id()] = data
-
@classmethod
def load_work_schedules(cls):
cls.work_schedules = {}
diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
index b20dacf50c..61467dba69 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py
@@ -34,4 +34,11 @@ class Usecase:
definition=self.settings["work_calendar"],
relating_context=self.file.by_type("IfcContext")[0],
)
+ if self.settings["work_calendar"].Controls:
+ for rel in self.settings["work_calendar"].Controls:
+ for object in rel.RelatedObjects:
+ ifcopenshell.api.run(
+ "control.unassign_control",
+ self.file,
+ **{"relating_control": self.settings["work_calendar"], "related_object": object})
self.file.remove(self.settings["work_calendar"])