From bf4a3f074f67046bdd84432319a5e448e2622a1d Mon Sep 17 00:00:00 2001 From: Andrej Date: Fri, 6 Jun 2025 12:40:08 +0500 Subject: [PATCH] typing --- .../bonsai/bim/module/classification/data.py | 5 +- src/bonsai/bonsai/bim/module/cost/operator.py | 16 ++ src/bonsai/bonsai/bim/module/cost/prop.py | 41 +++-- .../bonsai/bim/module/profile/operator.py | 7 +- src/bonsai/bonsai/bim/module/root/operator.py | 7 +- src/bonsai/bonsai/core/cost.py | 160 ++++++++++-------- src/bonsai/bonsai/tool/blender.py | 4 +- src/bonsai/bonsai/tool/cost.py | 17 +- src/bonsai/test/tool/test_root.py | 4 +- .../ifcopenshell/ifcopenshell_wrapper.pyi | 2 +- 10 files changed, 160 insertions(+), 103 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/classification/data.py b/src/bonsai/bonsai/bim/module/classification/data.py index bdc70acfb0..aca0b3b1cb 100644 --- a/src/bonsai/bonsai/bim/module/classification/data.py +++ b/src/bonsai/bonsai/bim/module/classification/data.py @@ -21,6 +21,7 @@ import ifcopenshell import ifcopenshell.util.date import ifcopenshell.util.classification import bonsai.tool as tool +from typing import Any from bonsai.bim.ifc import IfcStore @@ -157,8 +158,8 @@ class CostClassificationsData(ReferencesData): cls.data["object_type"] = "Cost" @classmethod - def references(cls): - results = [] + def references(cls) -> list[dict[str, Any]]: + results: list[dict[str, Any]] = [] props = tool.Cost.get_cost_props() element = tool.Ifc.get().by_id(props.cost_items[props.active_cost_item_index].ifc_definition_id) if element: diff --git a/src/bonsai/bonsai/bim/module/cost/operator.py b/src/bonsai/bonsai/bim/module/cost/operator.py index 867d2ba162..963d33e0e9 100644 --- a/src/bonsai/bonsai/bim/module/cost/operator.py +++ b/src/bonsai/bonsai/bim/module/cost/operator.py @@ -46,6 +46,7 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator): def draw(self, context): layout = self.layout + assert layout props = tool.Cost.get_cost_props() layout.prop(self, "name", text="Name") layout.prop(props, "cost_schedule_predefined_types", text="Type") @@ -149,6 +150,9 @@ class ExpandCostItem(bpy.types.Operator, tool.Ifc.Operator): bl_description = "Expand this cost item" cost_item: bpy.props.IntProperty() + if TYPE_CHECKING: + cost_item: int + def _execute(self, context): core.expand_cost_item(tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item)) @@ -171,6 +175,9 @@ class ContractCostItem(bpy.types.Operator, tool.Ifc.Operator): bl_description = "Contract a cost item" cost_item: bpy.props.IntProperty() + if TYPE_CHECKING: + cost_item: int + def _execute(self, context): core.contract_cost_item(tool.Cost, cost_item=tool.Ifc.get().by_id(self.cost_item)) @@ -192,6 +199,9 @@ class RemoveCostItem(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} cost_item: bpy.props.IntProperty() + if TYPE_CHECKING: + cost_item: int + def _execute(self, context): core.remove_cost_item(tool.Ifc, tool.Cost, cost_item_id=self.cost_item) @@ -700,6 +710,9 @@ class ExpandCostItemRate(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} cost_item: bpy.props.IntProperty() + if TYPE_CHECKING: + cost_item: int + def _execute(self, context): core.expand_cost_item_rate(tool.Cost, self.cost_item) return {"FINISHED"} @@ -711,6 +724,9 @@ class ContractCostItemRate(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} cost_item: bpy.props.IntProperty() + if TYPE_CHECKING: + cost_item: int + def _execute(self, context): core.contract_cost_item_rate(tool.Cost, self.cost_item) return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/cost/prop.py b/src/bonsai/bonsai/bim/module/cost/prop.py index 8c47185822..f232c9fc1a 100644 --- a/src/bonsai/bonsai/bim/module/cost/prop.py +++ b/src/bonsai/bonsai/bim/module/cost/prop.py @@ -18,6 +18,7 @@ import bpy import ifcopenshell.api +import ifcopenshell.api.cost import bonsai.tool as tool from bonsai.bim.module.classification.data import CostClassificationsData from bonsai.bim.module.cost.data import CostSchedulesData, CostItemRatesData, CostItemQuantitiesData @@ -36,42 +37,48 @@ from bpy.props import ( from typing import TYPE_CHECKING, Literal -def get_schedule_of_rates(self, context): +def get_schedule_of_rates(self: "BIMCostProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: if not CostItemRatesData.is_loaded: CostItemRatesData.load() # return CostItemRatesData.data["schedule_of_rates"] return CostItemRatesData.data["cost_schedules"] -def update_schedule_of_rates(self, context): +def update_schedule_of_rates(self: "BIMCostProperties", context: bpy.types.Context) -> None: tool.Cost.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(self.schedule_of_rates))) -def get_quantity_types(self, context): +def get_quantity_types(self: "BIMCostProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: if not CostSchedulesData.is_loaded: CostSchedulesData.load() return CostSchedulesData.data["quantity_types"] -def get_product_quantity_names(self, context): +def get_product_quantity_names( + self: "BIMCostProperties", context: bpy.types.Context +) -> tool.Blender.BLENDER_ENUM_ITEMS: if not CostItemQuantitiesData.is_loaded: CostItemQuantitiesData.load() return CostItemQuantitiesData.data["product_quantity_names"] -def get_process_quantity_names(self, context): +def get_process_quantity_names( + self: "BIMCostProperties", context: bpy.types.Context +) -> tool.Blender.BLENDER_ENUM_ITEMS: if not CostItemQuantitiesData.is_loaded: CostItemQuantitiesData.load() return CostItemQuantitiesData.data["process_quantity_names"] -def get_resource_quantity_names(self, context): +def get_resource_quantity_names( + self: "BIMCostProperties", context: bpy.types.Context +) -> tool.Blender.BLENDER_ENUM_ITEMS: if not CostItemQuantitiesData.is_loaded: CostItemQuantitiesData.load() return CostItemQuantitiesData.data["resource_quantity_names"] -def update_active_cost_item_index(self, context): +def update_active_cost_item_index(self: "BIMCostProperties", context: bpy.types.Context) -> None: schedule = tool.Ifc.get().by_id(self.active_cost_schedule_id) if schedule.PredefinedType == "SCHEDULEOFRATES": tool.Cost.load_cost_item_types() @@ -84,11 +91,11 @@ def update_cost_item_identification(self: "CostItem", context: bpy.types.Context props = tool.Cost.get_cost_props() if not props.is_cost_update_enabled or self.identification == "XXX": return - self.file = tool.Ifc.get() - ifcopenshell.api.run( - "cost.edit_cost_item", - self.file, - **{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}}, + ifc_file = tool.Ifc.get() + ifcopenshell.api.cost.edit_cost_item( + ifc_file, + cost_item=ifc_file.by_id(self.ifc_definition_id), + attributes={"Identification": self.identification}, ) if props.active_cost_item_id == self.ifc_definition_id: attribute = props.cost_item_attributes["Identification"] @@ -99,11 +106,11 @@ def update_cost_item_name(self: "CostItem", context: bpy.types.Context) -> None: props = tool.Cost.get_cost_props() if not props.is_cost_update_enabled or self.name == "Unnamed": return - self.file = tool.Ifc.get() - ifcopenshell.api.run( - "cost.edit_cost_item", - self.file, - **{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}}, + ifc_file = tool.Ifc.get() + ifcopenshell.api.cost.edit_cost_item( + ifc_file, + cost_item=ifc_file.by_id(self.ifc_definition_id), + attributes={"Name": self.name}, ) if props.active_cost_item_id == self.ifc_definition_id: attribute = props.cost_item_attributes["Name"] diff --git a/src/bonsai/bonsai/bim/module/profile/operator.py b/src/bonsai/bonsai/bim/module/profile/operator.py index 3a2b026778..3f5d3411bd 100644 --- a/src/bonsai/bonsai/bim/module/profile/operator.py +++ b/src/bonsai/bonsai/bim/module/profile/operator.py @@ -102,7 +102,7 @@ class RemoveProfileDef(bpy.types.Operator, tool.Ifc.Operator): self.report({"ERROR"}, error_msg) return {"CANCELLED"} - ifcopenshell.api.run("profile.remove_profile", ifc_file, profile=profile) + ifcopenshell.api.profile.remove_profile(ifc_file, profile=profile) bpy.ops.bim.load_profiles() # preserve selected index if possible @@ -145,7 +145,7 @@ class EditProfile(bpy.types.Operator, tool.Ifc.Operator): props = tool.Profile.get_profile_props() attributes = bonsai.bim.helper.export_attributes(props.profile_attributes) profile = tool.Ifc.get().by_id(props.active_profile_id) - ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes) + ifcopenshell.api.profile.edit_profile(tool.Ifc.get(), profile=profile, attributes=attributes) model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile) bpy.ops.bim.load_profiles() generate_thumbnail_for_active_profile() @@ -179,6 +179,7 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator): points = [(0, 0), (0.1, 0), (0.1, 0.1), (0, 0.1), (0, 0)] profile = ifcopenshell.api.profile.add_arbitrary_profile(tool.Ifc.get(), profile=points) else: + assert obj and isinstance(obj.data, bpy.types.Mesh) if "inner_curves" not in indices: points = [(obj.data.vertices[i].co.x, obj.data.vertices[i].co.y) for i in indices["profile"]] points.append(points[0]) @@ -199,7 +200,7 @@ class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator): ) else: - profile = ifcopenshell.api.run("profile.add_parameterized_profile", tool.Ifc.get(), ifc_class=profile_class) + profile = ifcopenshell.api.profile.add_parameterized_profile(tool.Ifc.get(), ifc_class=profile_class) tool.Profile.set_default_profile_attrs(profile) profile.ProfileName = "New Profile" bpy.ops.bim.load_profiles() diff --git a/src/bonsai/bonsai/bim/module/root/operator.py b/src/bonsai/bonsai/bim/module/root/operator.py index fae8ae8ffa..9a490b5589 100644 --- a/src/bonsai/bonsai/bim/module/root/operator.py +++ b/src/bonsai/bonsai/bim/module/root/operator.py @@ -93,11 +93,14 @@ class ReassignClass(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() + if TYPE_CHECKING: + obj: str + def _execute(self, context): if self.obj: - objects = [bpy.data.objects.get(self.obj)] + objects = [bpy.data.objects[self.obj]] else: - objects = set(context.selected_objects + [context.active_object]) + objects = tool.Blender.get_selected_objects() self.file = tool.Ifc.get() root_props = tool.Root.get_root_props() ifc_product = root_props.ifc_product diff --git a/src/bonsai/bonsai/core/cost.py b/src/bonsai/bonsai/core/cost.py index afd4c7d420..b0ede21948 100644 --- a/src/bonsai/bonsai/core/cost.py +++ b/src/bonsai/bonsai/core/cost.py @@ -26,32 +26,34 @@ if TYPE_CHECKING: import bonsai.tool as tool -def add_cost_schedule(ifc: tool.Ifc, name: str, predefined_type: str) -> None: +def add_cost_schedule(ifc: type[tool.Ifc], name: str, predefined_type: str) -> None: ifc.run("cost.add_cost_schedule", name=name, predefined_type=predefined_type) -def edit_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None: +def edit_cost_schedule(ifc: type[tool.Ifc], cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance) -> None: attributes = cost.get_cost_schedule_attributes() ifc.run("cost.edit_cost_schedule", cost_schedule=cost_schedule, attributes=attributes) cost.disable_editing_cost_schedule() -def disable_editing_cost_schedule(cost: tool.Cost) -> None: +def disable_editing_cost_schedule(cost: type[tool.Cost]) -> None: cost.disable_editing_cost_schedule() -def remove_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None: +def remove_cost_schedule( + ifc: type[tool.Ifc], cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance +) -> None: cost.remove_stored_schedule_columns(cost_schedule) cost.remove_csv_filepath(cost_schedule) ifc.run("cost.remove_cost_schedule", cost_schedule=cost_schedule) -def enable_editing_cost_schedule_attributes(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_schedule_attributes(cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance) -> None: cost.load_cost_schedule_attributes(cost_schedule) cost.enable_editing_cost_schedule_attributes(cost_schedule) -def enable_editing_cost_items(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_items(cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance) -> None: cost.enable_editing_cost_items(cost_schedule) cost.load_active_schedule_columns() cost.load_cost_schedule_tree() @@ -60,55 +62,57 @@ def enable_editing_cost_items(cost: tool.Cost, cost_schedule: ifcopenshell.entit cost.play_sound() -def add_summary_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None: +def add_summary_cost_item( + ifc: type[tool.Ifc], cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance +) -> None: ifc.run("cost.add_cost_item", cost_schedule=cost_schedule) cost.load_cost_schedule_tree() # cost.play_sound() -def add_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def add_cost_item(ifc: type[tool.Ifc], cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: ifc.run("cost.add_cost_item", cost_item=cost_item) cost.load_cost_schedule_tree() # cost.enable_editing_cost_schedule_attributes(cost_schedule) -def expand_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def expand_cost_item(cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: cost.expand_cost_item(cost_item) cost.load_cost_schedule_tree() -def expand_cost_items(cost: tool.Cost) -> None: +def expand_cost_items(cost: type[tool.Cost]) -> None: cost.expand_cost_items() cost.load_cost_schedule_tree() -def contract_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def contract_cost_item(cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: cost.contract_cost_item(cost_item) cost.load_cost_schedule_tree() -def contract_cost_items(cost: tool.Cost) -> None: +def contract_cost_items(cost: type[tool.Cost]) -> None: cost.contract_cost_items() cost.load_cost_schedule_tree() -def remove_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item_id: int) -> None: +def remove_cost_item(ifc: type[tool.Ifc], cost: type[tool.Cost], cost_item_id: int) -> None: cost_item = ifc.get().by_id(cost_item_id) ifc.run("cost.remove_cost_item", cost_item=cost_item) cost.clean_up_cost_item_tree(cost_item_id) cost.load_cost_schedule_tree() -def enable_editing_cost_item_attributes(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_attributes(cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: cost.enable_editing_cost_item_attributes(cost_item) cost.load_cost_item_attributes(cost_item) -def disable_editing_cost_item(cost: tool.Cost) -> None: +def disable_editing_cost_item(cost: type[tool.Cost]) -> None: cost.disable_editing_cost_item() -def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None: +def edit_cost_item(ifc: type[tool.Ifc], cost: type[tool.Cost]) -> None: attributes = cost.get_cost_item_attributes() ifc.run("cost.edit_cost_item", cost_item=cost.get_active_cost_item(), attributes=attributes) cost.disable_editing_cost_item() @@ -116,7 +120,11 @@ def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None: def assign_cost_item_type( - ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance, prop_name + ifc: type[tool.Ifc], + cost: type[tool.Cost], + spatial: type[tool.Spatial], + cost_item: ifcopenshell.entity_instance, + prop_name, ) -> list[ifcopenshell.entity_instance]: """ Returns: @@ -132,9 +140,9 @@ def assign_cost_item_type( def unassign_cost_item_type( - ifc: tool.Ifc, - cost: tool.Cost, - spatial: tool.Spatial, + ifc: type[tool.Ifc], + cost: type[tool.Cost], + spatial: type[tool.Spatial], cost_item: ifcopenshell.entity_instance, product_types: Optional[list[ifcopenshell.entity_instance]] = None, ) -> list[ifcopenshell.entity_instance]: @@ -152,16 +160,16 @@ def unassign_cost_item_type( return product_types -def load_cost_item_types(cost: tool.Cost) -> None: +def load_cost_item_types(cost: type[tool.Cost]) -> None: cost_item = cost.get_active_cost_item() cost.load_cost_item_types(cost_item) def assign_cost_item_quantity( - ifc: tool.Ifc, - cost: tool.Cost, + ifc: type[tool.Ifc], + cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance, - related_object_type: tool.Cost.RELATED_OBJECT_TYPE, + related_object_type: type[tool.Cost].RELATED_OBJECT_TYPE, prop_name: str, ) -> bool: products = cost.get_products(related_object_type) @@ -173,27 +181,30 @@ def assign_cost_item_quantity( return False -def load_cost_item_quantities(cost: tool.Cost) -> None: +def load_cost_item_quantities(cost: type[tool.Cost]) -> None: cost.load_cost_item_quantities() -def load_cost_item_element_quantities(cost: tool.Cost) -> None: +def load_cost_item_element_quantities(cost: type[tool.Cost]) -> None: cost_item = cost.get_highlighted_cost_item() cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT") -def load_cost_item_task_quantities(cost: tool.Cost) -> None: +def load_cost_item_task_quantities(cost: type[tool.Cost]) -> None: cost_item = cost.get_highlighted_cost_item() cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PROCESS") -def load_cost_item_resource_quantities(cost: tool.Cost) -> None: +def load_cost_item_resource_quantities(cost: type[tool.Cost]) -> None: cost_item = cost.get_highlighted_cost_item() cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE") def assign_cost_value( - ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, cost_rate: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + cost: type[tool.Cost], + cost_item: ifcopenshell.entity_instance, + cost_rate: ifcopenshell.entity_instance, ) -> None: ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate) existing_cost_rate = cost.get_assigned_rate_cost_item(cost_item) @@ -204,13 +215,13 @@ def assign_cost_value( ifc.run("control.assign_control", relating_control=cost_rate, related_object=cost_item) -def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates: ifcopenshell.entity_instance) -> None: +def load_schedule_of_rates(cost: type[tool.Cost], schedule_of_rates: ifcopenshell.entity_instance) -> None: cost.load_schedule_of_rates_tree(schedule_of_rates) def unassign_cost_item_quantity( - ifc: tool.Ifc, - cost: tool.Cost, + ifc: type[tool.Ifc], + cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance], ) -> None: @@ -218,34 +229,36 @@ def unassign_cost_item_quantity( cost.load_cost_item_quantities() -def enable_editing_cost_item_quantities(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_quantities(cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: cost.enable_editing_cost_item_quantities(cost_item) -def enable_editing_cost_item_values(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_values(cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance) -> None: cost.enable_editing_cost_item_values(cost_item) -def add_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, ifc_class: str) -> None: +def add_cost_item_quantity(ifc: type[tool.Ifc], cost_item: ifcopenshell.entity_instance, ifc_class: str) -> None: ifc.run("cost.add_cost_item_quantity", cost_item=cost_item, ifc_class=ifc_class) def remove_cost_item_quantity( - ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, physical_quantity: ifcopenshell.entity_instance + ifc: type[tool.Ifc], cost_item: ifcopenshell.entity_instance, physical_quantity: ifcopenshell.entity_instance ) -> None: ifc.run("cost.remove_cost_item_quantity", cost_item=cost_item, physical_quantity=physical_quantity) -def enable_editing_cost_item_quantity(cost: tool.Cost, physical_quantity: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_quantity(cost: type[tool.Cost], physical_quantity: ifcopenshell.entity_instance) -> None: cost.load_cost_item_quantity_attributes(physical_quantity) cost.enable_editing_cost_item_quantity(physical_quantity) -def disable_editing_cost_item_quantity(cost: tool.Cost) -> None: +def disable_editing_cost_item_quantity(cost: type[tool.Cost]) -> None: cost.disable_editing_cost_item_quantity() -def edit_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, physical_quantity: ifcopenshell.entity_instance) -> None: +def edit_cost_item_quantity( + ifc: type[tool.Ifc], cost: type[tool.Cost], physical_quantity: ifcopenshell.entity_instance +) -> None: attributes = cost.get_cost_item_quantity_attributes() ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes) cost.disable_editing_cost_item_quantity() @@ -253,8 +266,8 @@ def edit_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, physical_quantity: i def add_cost_value( - ifc: tool.Ifc, - cost: tool.Cost, + ifc: type[tool.Ifc], + cost: type[tool.Cost], parent: ifcopenshell.entity_instance, cost_type: Literal["FIXED", "SUM", "CATEGORY"], cost_category: Optional[str] = None, @@ -268,32 +281,34 @@ def add_cost_value( def remove_cost_value( - ifc: tool.Ifc, parent: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance + ifc: type[tool.Ifc], parent: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance ) -> None: ifc.run("cost.remove_cost_value", parent=parent, cost_value=cost_value) -def enable_editing_cost_item_value(cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_value(cost: type[tool.Cost], cost_value: ifcopenshell.entity_instance) -> None: cost.load_cost_item_value_attributes(cost_value) cost.enable_editing_cost_item_value(cost_value) -def disable_editing_cost_item_value(cost: tool.Cost) -> None: +def disable_editing_cost_item_value(cost: type[tool.Cost]) -> None: cost.disable_editing_cost_item_value() -def enable_editing_cost_item_value_formula(cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None: +def enable_editing_cost_item_value_formula(cost: type[tool.Cost], cost_value: ifcopenshell.entity_instance) -> None: cost.load_cost_item_value_formula_attributes(cost_value) cost.enable_editing_cost_item_value_formula(cost_value) -def edit_cost_item_value_formula(ifc: tool.Ifc, cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None: +def edit_cost_item_value_formula( + ifc: type[tool.Ifc], cost: type[tool.Cost], cost_value: ifcopenshell.entity_instance +) -> None: formula = cost.get_cost_item_value_formula() ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula) cost.disable_editing_cost_item_value() -def edit_cost_value(ifc: tool.Ifc, cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None: +def edit_cost_value(ifc: type[tool.Ifc], cost: type[tool.Cost], cost_value: ifcopenshell.entity_instance) -> None: attributes = cost.get_cost_value_attributes() ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes) cost.disable_editing_cost_item_value() @@ -301,67 +316,72 @@ def edit_cost_value(ifc: tool.Ifc, cost: tool.Cost, cost_value: ifcopenshell.ent def copy_cost_item_values( - ifc: tool.Ifc, cost: tool.Cost, source: ifcopenshell.entity_instance, destination: ifcopenshell.entity_instance + ifc: type[tool.Ifc], + cost: type[tool.Cost], + source: ifcopenshell.entity_instance, + destination: ifcopenshell.entity_instance, ) -> None: ifc.run("cost.copy_cost_item_values", source=source, destination=destination) -def select_cost_item_products(cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance) -> None: +def select_cost_item_products( + cost: type[tool.Cost], spatial: type[tool.Spatial], cost_item: ifcopenshell.entity_instance +) -> None: is_deep = cost.show_nested_cost_item_elements() products = cost.get_cost_item_products(cost_item, is_deep) spatial.select_products(products) def select_cost_schedule_products( - cost: tool.Cost, spatial: tool.Spatial, cost_schedule: ifcopenshell.entity_instance + cost: type[tool.Cost], spatial: type[tool.Spatial], cost_schedule: ifcopenshell.entity_instance ) -> None: products = cost.get_cost_schedule_products(cost_schedule) spatial.select_products(products) def import_cost_schedule_csv( - cost: tool.Cost, file_path: str, is_schedule_of_rates: bool + cost: type[tool.Cost], file_path: str, is_schedule_of_rates: bool ) -> ifcopenshell.entity_instance: cost_schedule = cost.import_cost_schedule_csv(file_path, is_schedule_of_rates) return cost_schedule -def add_csv_filepath(cost: tool.Cost, file_path: str, is_schedule_of_rates: bool, cost_schedule) -> None: +def add_csv_filepath(cost: type[tool.Cost], file_path: str, is_schedule_of_rates: bool, cost_schedule) -> None: cost.add_csv_filepath(file_path, is_schedule_of_rates, cost_schedule) -def remove_csv_filepath(cost: tool.Cost, cost_schedule) -> None: +def remove_csv_filepath(cost: type[tool.Cost], cost_schedule) -> None: cost.remove_csv_filepath(cost_schedule) -def refresh_cost_schedule_csv(ifc: tool.Ifc, cost: tool.Cost) -> None: +def refresh_cost_schedule_csv(ifc: type[tool.Ifc], cost: type[tool.Cost]) -> None: cost.delete_all_cost_items() cost.refresh_cost_schedule_csv() cost.load_cost_schedule_tree() -def add_cost_column(cost: tool.Cost, name: str) -> None: +def add_cost_column(cost: type[tool.Cost], name: str) -> None: cost.add_cost_column(name) -def remove_cost_column(cost: tool.Cost, name: str) -> None: +def remove_cost_column(cost: type[tool.Cost], name: str) -> None: cost.remove_cost_column(name) -def expand_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def expand_cost_item_rate(cost: type[tool.Cost], cost_item: int) -> None: cost.expand_cost_item_rate(cost_item) -def contract_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None: +def contract_cost_item_rate(cost: type[tool.Cost], cost_item: int) -> None: cost.contract_cost_item_rate(cost_item) -def calculate_cost_item_resource_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance) -> None: +def calculate_cost_item_resource_value(ifc: type[tool.Ifc], cost_item: ifcopenshell.entity_instance) -> None: ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item) def export_cost_schedules( - cost: tool.Cost, + cost: type[tool.Cost], dirpath: str, format: Literal["CSV", "ODS", "XLSX"], cost_schedule: Union[ifcopenshell.entity_instance, None] = None, @@ -371,8 +391,8 @@ def export_cost_schedules( def clear_cost_item_assignments( - ifc: tool.Ifc, - cost: tool.Cost, + ifc: type[tool.Ifc], + cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance, related_object_type: ifcopenshell.util.cost.FILTER_BY_TYPE, ) -> None: @@ -383,7 +403,7 @@ def clear_cost_item_assignments( cost.load_cost_schedule_tree() -def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial) -> None: +def select_unassigned_products(ifc: type[tool.Ifc], cost: type[tool.Cost], spatial: type[tool.Spatial]) -> None: spatial.deselect_objects() products = ifc.get().by_type("IfcElement") cost_schedule = cost.get_active_cost_schedule() @@ -391,12 +411,12 @@ def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spa spatial.select_products(selection) -def load_product_cost_items(cost: tool.Cost, product: ifcopenshell.entity_instance) -> None: +def load_product_cost_items(cost: type[tool.Cost], product: ifcopenshell.entity_instance) -> None: cost.load_product_cost_items(product) def highlight_product_cost_item( - spatial: tool.Spatial, cost: tool.Cost, cost_item: ifcopenshell.entity_instance + spatial: type[tool.Spatial], cost: type[tool.Cost], cost_item: ifcopenshell.entity_instance ) -> Union[str, None]: cost_schedule = cost.get_cost_schedule(cost_item) is_cost_schedule_active = cost.is_cost_schedule_active(cost_schedule) @@ -406,7 +426,7 @@ def highlight_product_cost_item( return "Cost schedule is not active" -def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent) -> Union[str, None]: +def change_parent_cost_item(ifc: type[tool.Ifc], cost: type[tool.Cost], new_parent) -> Union[str, None]: cost_item = cost.get_active_cost_item() if cost_item and cost.is_root_cost_item(cost_item): return "Cannot change root cost item" @@ -416,7 +436,7 @@ def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent) -> Union cost.load_cost_schedule_tree() -def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None: +def copy_cost_item(ifc: type[tool.Ifc], cost: type[tool.Cost]) -> None: cost_item = cost.get_highlighted_cost_item() if cost_item: cost_item = ifc.run("cost.copy_cost_item", cost_item=cost_item) @@ -424,7 +444,7 @@ def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None: cost.load_cost_schedule_tree() -def add_currency(ifc: tool.Ifc, cost: tool.Cost) -> ifcopenshell.entity_instance: +def add_currency(ifc: type[tool.Ifc], cost: type[tool.Cost]) -> ifcopenshell.entity_instance: unit = ifc.run("unit.add_monetary_unit") attributes = cost.get_currency_attributes() ifc.run("unit.edit_monetary_unit", unit=unit, attributes=attributes) @@ -432,5 +452,7 @@ def add_currency(ifc: tool.Ifc, cost: tool.Cost) -> ifcopenshell.entity_instance return unit -def generate_cost_schedule_browser(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> bpy.types.Panel: +def generate_cost_schedule_browser( + cost: type[tool.Cost], cost_schedule: ifcopenshell.entity_instance +) -> bpy.types.Panel: return cost.generate_cost_schedule_browser(cost_schedule) diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index c02a9ce1d8..9c10522cca 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -42,7 +42,7 @@ from mathutils import Vector from pathlib import Path from functools import lru_cache, cache from bonsai.bim.ifc import IFC_CONNECTED_TYPE -from typing import Any, Optional, Union, Literal, Iterable, Callable, TypeVar, Generator, TYPE_CHECKING +from typing import Any, Optional, Union, Literal, Iterable, Callable, TypeVar, Generator, TYPE_CHECKING, Sequence from typing_extensions import assert_never if TYPE_CHECKING: @@ -657,7 +657,7 @@ class Blender(bonsai.core.tool.Blender): cls, context: bpy.types.Context, active_object: Optional[bpy.types.Object] = None, - selected_objects: list[bpy.types.Object] = list(), + selected_objects: Sequence[bpy.types.Object] = (), clear_previous_selection=True, ) -> None: if clear_previous_selection: diff --git a/src/bonsai/bonsai/tool/cost.py b/src/bonsai/bonsai/tool/cost.py index ef01feea9c..d9d1df369e 100644 --- a/src/bonsai/bonsai/tool/cost.py +++ b/src/bonsai/bonsai/tool/cost.py @@ -38,6 +38,11 @@ if TYPE_CHECKING: class Cost(bonsai.core.tool.Cost): RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"] + contracted_cost_items: list[int] + """List of contracted cost item ids.""" + + contracted_cost_item_rates: list[int] + """List of contracted const item rates ids.""" @classmethod def get_cost_props(cls) -> BIMCostProperties: @@ -662,18 +667,18 @@ class Cost(bonsai.core.tool.Cost): return tool.Ifc.get().by_id(int(schedule_id)) @classmethod - def expand_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None: + def expand_cost_item_rate(cls, cost_item_id: int) -> None: props = cls.get_cost_props() contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates) - contracted_cost_item_rates.remove(cost_item) + contracted_cost_item_rates.remove(cost_item_id) props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates))) @classmethod - def contract_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None: + def contract_cost_item_rate(cls, cost_item_id: int) -> None: props = cls.get_cost_props() contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates) - contracted_cost_item_rates.append(cost_item) + contracted_cost_item_rates.append(cost_item_id) props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates))) @@ -807,8 +812,8 @@ class Cost(bonsai.core.tool.Cost): @classmethod def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None: - def expand_ancestors(cost_item): - cls.expand_cost_item(cost_item) + def expand_ancestors(cost_item: ifcopenshell.entity_instance) -> None: + cls.expand_cost_item(cost_item.id()) for rel in cost_item.Nests or []: parent_cost = rel.RelatingObject if rel.RelatingObject.is_a("IfcCostItem") else None if parent_cost: diff --git a/src/bonsai/test/tool/test_root.py b/src/bonsai/test/tool/test_root.py index 266494b0ff..5b3e132be2 100644 --- a/src/bonsai/test/tool/test_root.py +++ b/src/bonsai/test/tool/test_root.py @@ -221,7 +221,9 @@ class TestReassignClass(NewFile): bpy.ops.bim.add_occurrence(relating_type_id=relating_type_id) bpy.ops.bim.add_occurrence(relating_type_id=relating_type_id) - slabs = [tool.Ifc.get_object(e) for e in ifc_file.by_type("IfcSlab")] + slabs = [ + obj for e in ifc_file.by_type("IfcSlab") if isinstance(obj := tool.Ifc.get_object(e), bpy.types.Object) + ] assert len(slabs) == 3 tool.Blender.set_objects_selection(context, slabs[0], (slabs[1],)) diff --git a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi index 5e631804ae..a4a88e8f01 100644 --- a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi +++ b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi @@ -715,7 +715,7 @@ class declaration: def index_in_schema(self): ... def name(self) -> str: ... def name_uc(self): ... - def schema(self): ... + def schema(self) -> schema_definition: ... def type(self): ... class direction3: