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: