From ee46e76340a7b5e124389ec51c0235b53f62ade0 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 5 May 2021 11:59:33 +1000 Subject: [PATCH] Derive start and finish dates and elapsed durations where possible in the task tree --- .../bim/module/sequence/operator.py | 45 ++++++++++++++++--- .../blenderbim/bim/module/sequence/prop.py | 3 ++ .../blenderbim/bim/module/sequence/ui.py | 17 +++++-- .../ifcopenshell/util/date.py | 26 ++++++----- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index bff9392025..6865110198 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -307,12 +307,43 @@ class LoadTaskProperties(bpy.types.Operator): isodate.duration_isoformat(task_time["ScheduleDuration"]) if task_time["ScheduleDuration"] else "-" ) else: + derived_start = self.derive_start(item.ifc_definition_id) + derived_finish = self.derive_finish(item.ifc_definition_id) + 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: + derived_duration = ifcopenshell.util.date.timedelta2duration(derived_finish - derived_start) + item.derived_duration = isodate.duration_isoformat(derived_duration) item.start = "-" item.finish = "-" item.duration = "-" self.props.is_task_update_enabled = True return {"FINISHED"} + def derive_start(self, ifc_definition_id, start=None): + task = Data.tasks[ifc_definition_id] + if task["TaskTime"]: + schedule_start = Data.task_times[task["TaskTime"]]["ScheduleStart"] + if schedule_start: + return schedule_start + for subtask in task["RelatedObjects"]: + schedule_start = self.derive_start(subtask, start) + if schedule_start and (start is None or schedule_start < start): + start = schedule_start + return start + + def derive_finish(self, ifc_definition_id, finish=None): + task = Data.tasks[ifc_definition_id] + if task["TaskTime"]: + schedule_finish = Data.task_times[task["TaskTime"]]["ScheduleFinish"] + if schedule_finish: + return schedule_finish + for subtask in task["RelatedObjects"]: + schedule_finish = self.derive_finish(subtask, finish) + if schedule_finish and (finish is None or schedule_finish > finish): + finish = schedule_finish + return finish + def canonicalise_time(self, time): if not time: return "-" @@ -850,7 +881,7 @@ class ImportP6(bpy.types.Operator, ImportHelper): p62ifc = P62Ifc() p62ifc.xml = self.filepath p62ifc.file = self.file - p62ifc.work_plan = self.file.by_type("IfcWorkPlan")[0] + p62ifc.work_plan = self.file.by_type("IfcWorkPlan")[0] if self.file.by_type("IfcWorkPlan") else None p62ifc.execute() Data.load(IfcStore.get_file()) print("Import finished in {:.2f} seconds".format(time.time() - start)) @@ -1284,7 +1315,9 @@ class UnassignLagTime(bpy.types.Operator): 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)}) + ifcopenshell.api.run( + "sequence.unassign_lag_time", self.file, **{"rel_sequence": self.file.by_id(self.sequence)} + ) Data.load(IfcStore.get_file()) return {"FINISHED"} @@ -1296,12 +1329,15 @@ class AssignLagTime(bpy.types.Operator): 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"}) + 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"} - class EditSequenceAttributes(bpy.types.Operator): bl_idname = "bim.edit_sequence_attributes" bl_label = "Edit Sequence" @@ -1357,7 +1393,6 @@ class EditSequenceTimeLag(bpy.types.Operator): return {"FINISHED"} - class DisableEditingSequence(bpy.types.Operator): bl_idname = "bim.disable_editing_sequence" bl_label = "Disable Editing Sequence Attributes" diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 388d76ba2b..ae6e48fe69 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -128,6 +128,9 @@ class Task(PropertyGroup): duration: StringProperty(name="Duration") start: StringProperty(name="Start", update=updateTaskTimeStart) finish: StringProperty(name="Finish", update=updateTaskTimeFinish) + derived_start: StringProperty(name="Derived Start") + derived_finish: StringProperty(name="Derived Finish") + derived_duration: StringProperty(name="Derived Duration") is_predecessor: BoolProperty(name="Is Predecessor") is_successor: BoolProperty(name="Is Successor") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 0593815338..2ec7542127 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -298,9 +298,20 @@ class BIM_UL_tasks(UIList): row.prop(item, "name", emboss=False, text="") if props.should_show_times: - row.prop(item, "start", emboss=False, text="") - row.prop(item, "finish", emboss=False, text="") - row.prop(item, "duration", emboss=False, text="") + if item.derived_start: + row.label(text=item.derived_start + "*") + else: + row.prop(item, "start", emboss=False, text="") + + if item.derived_finish: + row.label(text=item.derived_finish + "*") + else: + row.prop(item, "finish", emboss=False, text="") + + if item.derived_duration: + row.label(text=item.derived_duration + "*") + else: + row.prop(item, "duration", emboss=False, text="") if context.active_object: oprops = context.active_object.BIMObjectProperties diff --git a/src/ifcopenshell-python/ifcopenshell/util/date.py b/src/ifcopenshell-python/ifcopenshell/util/date.py index ea5a06bb91..65a63b75c2 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/date.py +++ b/src/ifcopenshell-python/ifcopenshell/util/date.py @@ -7,21 +7,25 @@ except: pass # Duration parsing not supported +def timedelta2duration(timedelta): + components = { + "days": getattr(timedelta, "days", 0), + "hours": 0, + "minutes": 0, + "seconds": getattr(timedelta, "seconds", 0), + } + if components["seconds"]: + components["hours"], components["minutes"], components["seconds"] = [ + int(i) for i in str(datetime.timedelta(seconds=components["seconds"])).split(":") + ] + return isodate.Duration(**components) + + def ifc2datetime(element): if isinstance(element, str) and element[0] == "P": # IfcDuration duration = isodate.parse_duration(element) if isinstance(duration, datetime.timedelta): - components = { - "days": getattr(duration, "days", 0), - "hours": 0, - "minutes": 0, - "seconds": getattr(duration, "seconds", 0), - } - if components["seconds"]: - components["hours"], components["minutes"], components["seconds"] = [ - int(i) for i in str(datetime.timedelta(seconds=components["seconds"])).split(":") - ] - duration = isodate.Duration(**components) + return timedelta2duration(duration) return duration elif isinstance(element, str) and element[2] == ":": # IfcTime return datetime.time.fromisoformat(element)