Work schedule UI - fix issue with some durations props not loading

E.g. IfcWorkSchedule.Duration wasn't recognized as such or IfcWorkSchedule.TotalFloat wasn't imported properly and was uneditable.

Now we also check IfcDuration type on attributes explicitly, instead of relying on hardcoded list
This commit is contained in:
Andrej730
2025-07-24 17:57:38 +05:00
parent 973b6be2aa
commit cb724dbfde
4 changed files with 69 additions and 42 deletions
+1 -1
View File
@@ -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:
@@ -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)
+12 -1
View File
@@ -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):
+53 -37
View File
@@ -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)