diff --git a/src/bonsai/bonsai/bim/module/cost/operator.py b/src/bonsai/bonsai/bim/module/cost/operator.py
index 61138268f0..00d5f5fec0 100644
--- a/src/bonsai/bonsai/bim/module/cost/operator.py
+++ b/src/bonsai/bonsai/bim/module/cost/operator.py
@@ -16,12 +16,15 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+# pyright: reportUnnecessaryTypeIgnoreComment=error
+
import bpy
import ifcopenshell.api
import bonsai.tool as tool
from bpy_extras.io_utils import ImportHelper
import bonsai.tool as tool
import bonsai.core.cost as core
+from typing import get_args, TYPE_CHECKING
class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
@@ -261,9 +264,14 @@ class AssignCostItemQuantity(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Assign Cost Item Quantity"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
- related_object_type: bpy.props.StringProperty()
+ related_object_type: bpy.props.EnumProperty( # type: ignore [reportRedeclaration]
+ items=[(i, i, "") for i in get_args(tool.Cost.RELATED_OBJECT_TYPE)],
+ )
prop_name: bpy.props.StringProperty()
+ if TYPE_CHECKING:
+ related_object_type: tool.Cost.RELATED_OBJECT_TYPE
+
@classmethod
def description(cls, context, properties) -> str:
descr = f"Assign cost item quantity to the active cost item from active {properties.related_object_type}"
@@ -708,7 +716,12 @@ class ClearCostItemAssignments(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Clear Cost Item Product Assignments"
bl_options = {"REGISTER", "UNDO"}
cost_item: bpy.props.IntProperty()
- related_object_type: bpy.props.StringProperty()
+ related_object_type: bpy.props.EnumProperty( # type: ignore [reportRedeclaration]
+ items=[(i, i, "") for i in get_args(tool.Cost.RELATED_OBJECT_TYPE)],
+ )
+
+ if TYPE_CHECKING:
+ related_object_type: tool.Cost.RELATED_OBJECT_TYPE
def _execute(self, context):
core.clear_cost_item_assignments(
diff --git a/src/bonsai/bonsai/bim/module/sequence/operator.py b/src/bonsai/bonsai/bim/module/sequence/operator.py
index a855d8d79d..a6729a444d 100644
--- a/src/bonsai/bonsai/bim/module/sequence/operator.py
+++ b/src/bonsai/bonsai/bim/module/sequence/operator.py
@@ -16,6 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see .
+# pyright: reportUnnecessaryTypeIgnoreComment=error
+
import os
import bpy
import json
@@ -31,6 +33,8 @@ from datetime import datetime
from dateutil import parser, relativedelta
from bonsai.bim.ifc import IfcStore
from bpy_extras.io_utils import ImportHelper
+from typing import get_args, TYPE_CHECKING
+from typing_extensions import assert_never
class EnableStatusFilters(bpy.types.Operator):
@@ -507,9 +511,14 @@ class AssignProcess(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Assign Process"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- related_object_type: bpy.props.StringProperty()
+ related_object_type: bpy.props.EnumProperty( # type: ignore [reportRedeclaration]
+ items=[(i, i, "") for i in get_args(tool.Sequence.RELATED_OBJECT_TYPE)],
+ )
related_object: bpy.props.IntProperty()
+ if TYPE_CHECKING:
+ related_object_type: tool.Sequence.RELATED_OBJECT_TYPE
+
@classmethod
def description(cls, context, properties):
return f"Assign selected {properties.related_object_type} to the selected task"
@@ -530,6 +539,8 @@ class AssignProcess(bpy.types.Operator, tool.Ifc.Operator):
core.assign_input_products(tool.Ifc, tool.Sequence, tool.Spatial, task=tool.Ifc.get().by_id(self.task))
elif self.related_object_type == "CONTROL":
self.report({"ERROR"}, "Assigning process control is not yet supported") # TODO
+ else:
+ assert_never(self.related_object_type)
class UnassignProcess(bpy.types.Operator, tool.Ifc.Operator):
@@ -537,10 +548,15 @@ class UnassignProcess(bpy.types.Operator, tool.Ifc.Operator):
bl_label = "Unassign Process"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
- related_object_type: bpy.props.StringProperty()
+ related_object_type: bpy.props.EnumProperty( # type: ignore [reportRedeclaration]
+ items=[(i, i, "") for i in get_args(tool.Sequence.RELATED_OBJECT_TYPE)],
+ )
related_object: bpy.props.IntProperty()
resource: bpy.props.IntProperty()
+ if TYPE_CHECKING:
+ related_object_type: tool.Sequence.RELATED_OBJECT_TYPE
+
@classmethod
def description(cls, context, properties):
return f"Unassign selected {properties.related_object_type} from the selected task"
@@ -570,6 +586,9 @@ class UnassignProcess(bpy.types.Operator, tool.Ifc.Operator):
)
elif self.related_object_type == "CONTROL":
pass # TODO
+ self.report({"INFO"}, "Unassigning process control is not yet supported.")
+ else:
+ assert_never(self.related_object_type)
return {"FINISHED"}
diff --git a/src/bonsai/bonsai/tool/resource.py b/src/bonsai/bonsai/tool/resource.py
index 45c64d3653..d3372c9afa 100644
--- a/src/bonsai/bonsai/tool/resource.py
+++ b/src/bonsai/bonsai/tool/resource.py
@@ -22,7 +22,6 @@ import bpy
import bonsai.core.tool
import bonsai.tool as tool
import bonsai.bim.helper
-import bonsai.bim.module.sequence.helper as helper
import json
import time
import isodate
@@ -362,6 +361,8 @@ class Resource(bonsai.core.tool.Resource):
)
time_consumed = ifcopenshell.util.resource.get_unit_consumed(productivity)
if time_consumed:
+ import bonsai.bim.module.sequence.helper as helper
+
durations_attributes = helper.parse_duration_as_blender_props(time_consumed)
duration_props.years = durations_attributes["years"]
duration_props.months = durations_attributes["months"]
@@ -375,6 +376,8 @@ class Resource(bonsai.core.tool.Resource):
props = bpy.context.scene.BIMResourceProductivity
productivity = {}
if props.quantity_consumed:
+ import bonsai.bim.module.sequence.helper as helper
+
productivity["BaseQuantityConsumed"] = helper.blender_props_to_iso_duration(
props.quantity_consumed, "ELAPSEDTIME", "BaseQuantityConsumed"
)
diff --git a/src/bonsai/bonsai/tool/sequence.py b/src/bonsai/bonsai/tool/sequence.py
index 175e588365..66fda638f1 100644
--- a/src/bonsai/bonsai/tool/sequence.py
+++ b/src/bonsai/bonsai/tool/sequence.py
@@ -32,15 +32,19 @@ import ifcopenshell.util.unit
import bonsai.core.tool
import bonsai.tool as tool
import bonsai.bim.helper
-import bonsai.bim.module.sequence.helper as helper
from dateutil import parser
from datetime import datetime
-from typing import Optional, Any, Union
+from typing import Optional, Any, Union, Literal
class Sequence(bonsai.core.tool.Sequence):
+
+ RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"]
+
@classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]:
+ import bonsai.bim.module.sequence.helper as helper
+
def callback(attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
if prop.is_null:
@@ -87,6 +91,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_work_schedule_attributes(cls) -> dict[str, Any]:
+ import bonsai.bim.module.sequence.helper as helper
+
def callback(attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
if prop.is_null:
@@ -336,6 +342,8 @@ 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
+
def callback(name, prop, data):
if prop and prop.data_type == "string":
duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add()
@@ -373,6 +381,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_task_time_attributes(cls) -> dict[str, Any]:
+ import bonsai.bim.module.sequence.helper as helper
+
def callback(attributes, prop):
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if prop.is_null:
@@ -560,6 +570,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_work_time_attributes(cls) -> dict[str, Any]:
+ import bonsai.bim.module.sequence.helper as helper
+
def callback(attributes, prop):
if "Start" in prop.name or "Finish" in prop.name:
if prop.is_null: