diff --git a/src/bonsai/bonsai/bim/helper.py b/src/bonsai/bonsai/bim/helper.py index 8f7134b061..e6c165cd22 100644 --- a/src/bonsai/bonsai/bim/helper.py +++ b/src/bonsai/bonsai/bim/helper.py @@ -111,7 +111,7 @@ def draw_attribute( op = layout.operator("bim.attribute_add_subitem", icon="ADD", text="") op.data_path = data_path - elif attribute.name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"): + elif attribute.special_type == "DURATION": props = tool.Sequence.get_work_schedule_props() for item in props.durations_attributes: if item.name == attribute.name: diff --git a/src/bonsai/bonsai/bim/module/sequence/helper.py b/src/bonsai/bonsai/bim/module/sequence/helper.py index 11cf7336b2..43dcd1d59b 100644 --- a/src/bonsai/bonsai/bim/module/sequence/helper.py +++ b/src/bonsai/bonsai/bim/module/sequence/helper.py @@ -23,7 +23,7 @@ import bpy from dateutil import parser import ifcopenshell.util.date from datetime import timedelta, datetime -from typing import Union +from typing import Union, Any from bonsai.bim.prop import ISODuration @@ -49,8 +49,8 @@ def canonicalise_time(time: Union[datetime, None]) -> str: return time.strftime("%d/%m/%y") -def parse_duration_as_blender_props(dt, simplify=True): - if simplify: +def parse_duration_as_blender_props(dt: Union[Any, str]) -> dict[str, int]: + if True: if isinstance(dt, str): dt = ifcopenshell.util.date.ifc2datetime(dt) diff --git a/src/bonsai/bonsai/bim/prop.py b/src/bonsai/bonsai/bim/prop.py index 70adc430b7..7a4464c986 100644 --- a/src/bonsai/bonsai/bim/prop.py +++ b/src/bonsai/bonsai/bim/prop.py @@ -274,7 +274,18 @@ def get_display_name(self: "Attribute") -> str: AttributeDataType = Literal["string", "integer", "float", "boolean", "enum", "file", "list[string]"] -AttributeSpecialType = Literal["", "DATE", "DATETIME", "LENGTH", "AREA", "VOLUME", "FORCE", "LOGICAL", "URI"] +AttributeSpecialType = Literal[ + "", + "DATE", + "DATETIME", + "LENGTH", + "AREA", + "VOLUME", + "FORCE", + "LOGICAL", + "URI", + "DURATION", +] class Attribute(PropertyGroup): diff --git a/src/bonsai/bonsai/tool/sequence.py b/src/bonsai/bonsai/tool/sequence.py index 7497bba0e4..b24341c5d9 100644 --- a/src/bonsai/bonsai/tool/sequence.py +++ b/src/bonsai/bonsai/tool/sequence.py @@ -146,12 +146,8 @@ class Sequence(bonsai.core.tool.Sequence): 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 + elif prop.special_type == "DURATION": + return cls.export_duration_prop(prop, attributes) return False props = cls.get_work_schedule_props() @@ -159,16 +155,58 @@ class Sequence(bonsai.core.tool.Sequence): @classmethod def load_work_schedule_attributes(cls, work_schedule: ifcopenshell.entity_instance) -> None: + schema = tool.Ifc.schema() + entity = schema.declaration_by_name("IfcWorkSchedule").as_entity() + assert entity + def callback(name: str, prop: Union[Attribute, None], data: dict[str, Any]) -> None | Literal[True]: if name in ["CreationDate", "StartTime", "FinishTime"]: assert prop prop.string_value = "" if prop.is_null else data[name] return True + else: + attr = entity.attribute_by_index(entity.attribute_index(name)) + if not attr.type_of_attribute()._is("IfcDuration"): + return + assert prop + cls.add_duration_prop(prop, data[name]) props = cls.get_work_schedule_props() props.work_schedule_attributes.clear() bonsai.bim.helper.import_attributes(work_schedule, props.work_schedule_attributes, callback) + @classmethod + def add_duration_prop(cls, prop: Attribute, duration_value: Union[str, None]) -> None: + import bonsai.bim.module.sequence.helper as helper + + props = cls.get_work_schedule_props() + prop.special_type = "DURATION" + duration_props = props.durations_attributes.add() + duration_props.name = prop.name + if duration_value is None: + return + for key, value in helper.parse_duration_as_blender_props(duration_value).items(): + setattr(duration_props, key, value) + + @classmethod + def export_duration_prop(cls, prop: Attribute, out_attributes: dict[str, Any]) -> Literal[True]: + import bonsai.bim.module.sequence.helper as helper + + props = cls.get_work_schedule_props() + if prop.is_null: + out_attributes[prop.name] = None + for value in props.durations_attributes.values(): + value = 0 + else: + duration_type = out_attributes["DurationType"] if "DurationType" in out_attributes else None + time_split_iso_duration = helper.blender_props_to_iso_duration( + props.durations_attributes, duration_type, prop.name + ) + out_attributes[prop.name] = time_split_iso_duration + for value in props.durations_attributes.values(): + value = 0 + return True + @classmethod def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None: props = cls.get_work_schedule_props() @@ -401,25 +439,16 @@ class Sequence(bonsai.core.tool.Sequence): @classmethod def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None: - import bonsai.bim.module.sequence.helper as helper - props = cls.get_work_schedule_props() + schema = tool.Ifc.schema() + entity = schema.declaration_by_name("IfcTaskTime").as_entity() + assert entity def callback(name: str, prop: Union[Attribute, None], data: dict[str, Any]) -> Union[bool, None]: - if prop and prop.data_type == "string": - # TODO: Check actual attribute type instead of providing attribute names. - if name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"): - duration_props = props.durations_attributes.add() - duration_props.name = name - if prop.is_null: - for key in duration_props.keys(): - if key != "name": - setattr(duration_props, key, 0) - return True - if data[name]: - for key, value in helper.parse_duration_as_blender_props(data[name]).items(): - duration_props[key] = value - return True + attr = entity.attribute_by_index(entity.attribute_index(name)) + if attr.type_of_attribute()._is("IfcDuration"): + assert prop + cls.add_duration_prop(prop, data[name]) if isinstance(data[name], datetime): assert prop prop.string_value = "" if prop.is_null else data[name].isoformat() @@ -456,21 +485,8 @@ class Sequence(bonsai.core.tool.Sequence): return True attributes[prop.name] = helper.parse_datetime(prop.string_value) return True - elif prop.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]: - if prop.is_null: - attributes[prop.name] = None - for value in props.durations_attributes.values(): - value = 0 - return True - else: - duration_type = attributes["DurationType"] if "DurationType" in attributes else None - time_split_iso_duration = helper.blender_props_to_iso_duration( - props.durations_attributes, duration_type, prop.name - ) - attributes[prop.name] = time_split_iso_duration - for value in props.durations_attributes.values(): - value = 0 - return True + elif prop.special_type == "DURATION": + return cls.export_duration_prop(prop, attributes) return False return bonsai.bim.helper.export_attributes(props.task_time_attributes, callback)