From b59bdb585b46f41d5a90af8b0a0a20256063d974 Mon Sep 17 00:00:00 2001 From: falken10 Date: Fri, 27 Jun 2025 13:40:02 +0200 Subject: [PATCH] Refactor cost documents following developrs advice --- src/bonsai/bonsai/bim/module/cost/data.py | 10 +--- src/bonsai/bonsai/bim/module/cost/operator.py | 7 +-- src/bonsai/bonsai/core/cost.py | 27 ++------- src/bonsai/bonsai/tool/cost.py | 59 +++++++------------ 4 files changed, 28 insertions(+), 75 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/cost/data.py b/src/bonsai/bonsai/bim/module/cost/data.py index fb28039827..c5274b2c15 100644 --- a/src/bonsai/bonsai/bim/module/cost/data.py +++ b/src/bonsai/bonsai/bim/module/cost/data.py @@ -64,15 +64,7 @@ class CostSchedulesData: def get_csv_filepaths(cls) -> dict[int, str]: """Get CSV filepaths for cost schedules from document references.""" filepaths = {} - ifc_file = tool.Ifc.get() - cost_docs_document = next( - ( - document - for document in ifc_file.by_type("IfcDocumentInformation") - if document.Name == "BBIM_Cost_Documents" - ), - None, - ) + cost_docs_document = tool.Cost.get_or_create_cost_documents() if cost_docs_document: references = tool.Document.get_document_references(cost_docs_document) diff --git a/src/bonsai/bonsai/bim/module/cost/operator.py b/src/bonsai/bonsai/bim/module/cost/operator.py index 34e75ba092..5f61aab9c4 100644 --- a/src/bonsai/bonsai/bim/module/cost/operator.py +++ b/src/bonsai/bonsai/bim/module/cost/operator.py @@ -606,7 +606,7 @@ class RefreshCostScheduleCsv(bpy.types.Operator, tool.Ifc.Operator): cls.poll_message_set("No active cost schedule") return False - file_path = core.get_cost_schedule_csv_filepath(tool.Cost, props.active_cost_schedule_id) + file_path = tool.Cost.get_cost_schedule_csv_filepath(props.active_cost_schedule_id) if not file_path: cls.poll_message_set("No CSV file associated with this cost schedule") return False @@ -614,10 +614,7 @@ class RefreshCostScheduleCsv(bpy.types.Operator, tool.Ifc.Operator): return True def _execute(self, context): - error_message = core.refresh_cost_schedule_csv(tool.Cost) - if error_message: - self.report({"ERROR"}, error_message) - return {"CANCELLED"} + core.refresh_cost_schedule_csv(tool.Cost) CostSchedulesData.is_loaded = False return {"FINISHED"} diff --git a/src/bonsai/bonsai/core/cost.py b/src/bonsai/bonsai/core/cost.py index d5456cd56f..03dff47a7d 100644 --- a/src/bonsai/bonsai/core/cost.py +++ b/src/bonsai/bonsai/core/cost.py @@ -18,7 +18,6 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Union, Literal -import bonsai.tool as tool import os from bonsai.bim.module.cost.data import CostSchedulesData @@ -26,6 +25,7 @@ if TYPE_CHECKING: import bpy import ifcopenshell import ifcopenshell.util.cost + import bonsai.tool as tool def add_cost_schedule(ifc: type[tool.Ifc], name: str, predefined_type: str) -> None: @@ -342,13 +342,9 @@ def select_cost_schedule_products( def import_cost_schedule_csv( - cost: type[tool.Cost], file_path: str, is_schedule_of_rates: bool + cost: type[tool.Cost], resolved_path: str, is_schedule_of_rates: bool ) -> ifcopenshell.entity_instance: - resolved_path = tool.Ifc.resolve_uri(file_path) - if not os.path.exists(resolved_path): - print(f"Error: Could not find CSV file at {file_path} (resolved to {resolved_path})") - return None cost_schedule = cost.import_cost_schedule_csv(resolved_path, is_schedule_of_rates) return cost_schedule @@ -364,18 +360,7 @@ def remove_csv_filepath(cost: type[tool.Cost], cost_schedule) -> None: def refresh_cost_schedule_csv(cost: type[tool.Cost]) -> Optional[str]: - props = cost.get_cost_props() - cost_schedule_id = props.active_cost_schedule_id - file_path = cost.get_cost_schedule_csv_filepath(cost_schedule_id) - - if not file_path: - return "No CSV file associated with this cost schedule" - - resolved_path = tool.Ifc.resolve_uri(file_path) - - if not os.path.exists(resolved_path): - return f"Could not find CSV file at {file_path} (resolved to {resolved_path})" - + cost.delete_all_cost_items() cost.refresh_cost_schedule_csv() cost.load_cost_schedule_tree() @@ -487,9 +472,7 @@ def generate_cost_schedule_browser(cost: type[tool.Cost], cost_schedule: ifcopen return cost.generate_cost_schedule_browser(cost_schedule) -def get_cost_schedule_csv_filepath(cost: type[tool.Cost], cost_schedule_id: int) -> Optional[str]: - return cost.get_cost_schedule_csv_filepath(cost_schedule_id) -def is_schedule_of_rates_csv(cost: type[tool.Cost], cost_schedule_id: int) -> bool: - return cost.is_schedule_of_rates_csv(cost_schedule_id) + + diff --git a/src/bonsai/bonsai/tool/cost.py b/src/bonsai/bonsai/tool/cost.py index 0ab82f6e7b..afcc157158 100644 --- a/src/bonsai/bonsai/tool/cost.py +++ b/src/bonsai/bonsai/tool/cost.py @@ -593,10 +593,7 @@ class Cost(bonsai.core.tool.Cost): return csv2ifc.cost_schedule @classmethod - def add_csv_filepath(cls, file_path: str, is_schedule_of_rates: bool, cost_schedule) -> None: - if not file_path or not cost_schedule: - return - + def get_or_create_cost_documents(cls) -> ifcopenshell.entity_instance: ifc_file = tool.Ifc.get() cost_docs_document = next( ( @@ -606,11 +603,26 @@ class Cost(bonsai.core.tool.Cost): ), None, ) - + if not cost_docs_document: cost_docs_document = ifcopenshell.api.document.add_information(ifc_file) cost_docs_document.Name = "BBIM_Cost_Documents" cost_docs_document.Description = "Bonsai internal document containing references to cost CSV files" + + return cost_docs_document + + @classmethod + def add_csv_filepath( + cls, + file_path: Optional[str] = None, + is_schedule_of_rates: bool = False, + cost_schedule: ifcopenshell.entity_instance = None, + ) -> None: + if not file_path or not cost_schedule: + return + + ifc_file = tool.Ifc.get() + cost_docs_document = cls.get_or_create_cost_documents() reference = ifcopenshell.api.document.add_reference(ifc_file, cost_docs_document) reference.Location = file_path @@ -628,14 +640,7 @@ class Cost(bonsai.core.tool.Cost): return ifc_file = tool.Ifc.get() - cost_docs_document = next( - ( - document - for document in ifc_file.by_type("IfcDocumentInformation") - if document.Name == "BBIM_Cost_Documents" - ), - None, - ) + cost_docs_document = cls.get_or_create_cost_documents() if not cost_docs_document: return @@ -646,7 +651,6 @@ class Cost(bonsai.core.tool.Cost): for reference in references: if reference.Description and f"Cost Schedule ID: {cost_schedule_id}" in reference.Description: ifcopenshell.api.document.remove_reference(ifc_file, reference) - print(f"Cost schedule id={cost_schedule_id} csv filepath correctly removed") return @classmethod @@ -661,15 +665,7 @@ class Cost(bonsai.core.tool.Cost): @classmethod def is_schedule_of_rates_csv(cls, cost_schedule_id: int) -> bool: """Check if a cost schedule is a schedule of rates based on document references.""" - ifc_file = tool.Ifc.get() - cost_docs_document = next( - ( - document - for document in ifc_file.by_type("IfcDocumentInformation") - if document.Name == "BBIM_Cost_Documents" - ), - None, - ) + cost_docs_document = cls.get_or_create_cost_documents() if not cost_docs_document: return False @@ -684,15 +680,7 @@ class Cost(bonsai.core.tool.Cost): @classmethod def get_cost_schedule_csv_filepath(cls, cost_schedule_id: int) -> Optional[str]: - ifc_file = tool.Ifc.get() - cost_docs_document = next( - ( - document - for document in ifc_file.by_type("IfcDocumentInformation") - if document.Name == "BBIM_Cost_Documents" - ), - None, - ) + cost_docs_document = cls.get_or_create_cost_documents() if not cost_docs_document: return None @@ -1110,12 +1098,5 @@ class Cost(bonsai.core.tool.Cost): results["unit_symbol"] = "U" return results - @classmethod - def get_cost_schedule_documents(cls) -> Union[ifcopenshell.entity_instance, None]: - """Get the document information entity that stores CSV references.""" - for document in tool.Ifc.get().by_type("IfcDocumentInformation"): - if document.Name == "BBIM_Cost_Documents": - return document - return None