See #6570. Add a button to refresh an imported csv file

Now the imported csv filepath is stored so it is possible to refresh the cost schedule without the need of reimporting it
This commit is contained in:
Massimo Fabbro
2025-04-18 08:07:16 +02:00
committed by Massimo Fabbro
parent 0608f92bc4
commit f8e52c8a12
8 changed files with 117 additions and 4 deletions
@@ -61,6 +61,7 @@ classes = (
operator.ExportCostSchedules,
operator.HighlightProductCostItem,
operator.ImportCostScheduleCsv,
operator.RefreshCostScheduleCsv,
operator.LoadCostItemElementQuantities,
operator.LoadCostItemQuantities,
operator.LoadCostItemResourceQuantities,
@@ -82,6 +83,7 @@ classes = (
prop.CostItem,
prop.CostItemQuantity,
prop.CostItemType,
prop.CostItemsMapping,
prop.ScheduleColumn,
prop.BIMCostProperties,
ui.BIM_PT_cost_schedules,
+11 -1
View File
@@ -552,9 +552,19 @@ class ImportCostScheduleCsv(bpy.types.Operator, ImportHelper, tool.Ifc.Operator)
return True
def _execute(self, context):
core.import_cost_schedule_csv(tool.Cost, self.filepath, self.is_schedule_of_rates)
cost_schedule = core.import_cost_schedule_csv(tool.Cost, self.filepath, self.is_schedule_of_rates)
core.add_csv_filepath(tool.Cost, self.filepath, self.is_schedule_of_rates, cost_schedule)
return {"FINISHED"}
class RefreshCostScheduleCsv(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.refresh_cost_schedule_csv"
bl_label = "Refresh Cost Schedule CSV"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def _execute(self, context):
core.refresh_cost_schedule_csv(tool.Ifc, tool.Cost)
return {"FINISHED"}
class AddCostColumn(bpy.types.Operator):
bl_idname = "bim.add_cost_column"
@@ -166,6 +166,13 @@ class CostItemQuantity(PropertyGroup):
unit_symbol: str
total_cost_quantity: float
class CostItemsMapping(PropertyGroup):
cost_item_id: IntProperty(name="cost_item_id")
csv_filepath: StringProperty(name="filepath")
if TYPE_CHECKING:
cost_item_id: int
csv_filepath: str
class CostItemType(PropertyGroup):
name: StringProperty(name="Name")
@@ -272,6 +279,7 @@ class BIMCostProperties(PropertyGroup):
custom_currency: StringProperty(
name="Custom Currency", default="USD", description="Custom Currency in ISO 4217 format"
)
cost_schedule_files: CollectionProperty(name="Cost Schedule Files", type=CostItemsMapping)
if TYPE_CHECKING:
cost_schedule_predefined_types: str
@@ -326,3 +334,4 @@ class BIMCostProperties(PropertyGroup):
show_cost_item_operators: bool
currency: str
custom_currency: str
cost_schedule_files: bpy.types.bpy_prop_collection_idprop[CostItemsMapping]
+13
View File
@@ -71,6 +71,19 @@ class BIM_PT_cost_schedules(Panel):
text="Currently editing: {}[{}]".format(cost_schedule["name"], cost_schedule["predefined_type"]),
icon="LINENUMBERS_ON",
)
row0 = self.layout.row(align=True)
col = row0.column()
col.label(text="Linked CSV:")
row_1 = col.row(align=True)
if self.props.active_cost_schedule_id in [item.cost_item_id for item in self.props.cost_schedule_files]:
file = next((item.csv_filepath for item in self.props.cost_schedule_files if item.cost_item_id == self.props.active_cost_schedule_id), None)
row_1.label(text=file)
row_1.operator("bim.refresh_cost_schedule_csv", icon="FILE_REFRESH", text="")
else:
row_1.label(text="No CSV file found")
row_1.operator("bim.import_cost_schedule_csv", icon="IMPORT", text="")
grid = self.layout.grid_flow(columns=2, even_columns=True)
col = grid.column()
row1 = col.row(align=True)
+13 -2
View File
@@ -42,6 +42,7 @@ def disable_editing_cost_schedule(cost: tool.Cost) -> None:
def remove_cost_schedule(ifc: tool.Ifc, cost: 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)
@@ -311,10 +312,20 @@ def select_cost_schedule_products(
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) -> ifcopenshell.entity_instance:
cost_schedule = cost.import_cost_schedule_csv(file_path, is_schedule_of_rates)
return cost_schedule
def import_cost_schedule_csv(cost: tool.Cost, file_path: str, is_schedule_of_rates: bool) -> None:
cost.import_cost_schedule_csv(file_path, is_schedule_of_rates)
def add_csv_filepath(cost: 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:
cost.remove_csv_filepath(cost_schedule)
def refresh_cost_schedule_csv(ifc: tool.Ifc, cost: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:
cost.add_cost_column(name)
+4
View File
@@ -246,6 +246,10 @@ class Cost:
def has_schedules(cls): pass
def highlight_cost_item(cls, cost_item): pass
def import_cost_schedule_csv(cls, file_path, is_schedule_of_rates): pass
def add_csv_filepath(cls, file_path, is_schedule_of_rates, cost_schedule): pass
def remove_csv_filepath(cls, cost_schedule): pass
def delete_all_cost_items(cls): pass
def refresh_cost_schedule_csv(cls): pass
def is_active_schedule_of_rates(cls): pass
def is_cost_schedule_active(cls, cost_schedule): pass
def is_root_cost_item(cls, cost_item): pass
+61 -1
View File
@@ -561,7 +561,7 @@ class Cost(bonsai.core.tool.Cost):
return products
@classmethod
def import_cost_schedule_csv(cls, file_path: Optional[str] = None, is_schedule_of_rates: bool = False) -> None:
def import_cost_schedule_csv(cls, file_path: Optional[str] = None, is_schedule_of_rates: bool = False) -> ifcopenshell.entity_instance:
if not file_path:
return
from ifc5d.csv2ifc import Csv2Ifc
@@ -571,7 +571,67 @@ class Cost(bonsai.core.tool.Cost):
csv2ifc = Csv2Ifc(file_path, tool.Ifc.get(), is_schedule_of_rates=is_schedule_of_rates)
csv2ifc.execute()
print("Import finished in {:.2f} seconds".format(time.time() - start))
return csv2ifc.cost_schedule
@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:
return
props = cls.get_cost_props()
if not props.active_cost_schedule_id in [item.cost_item_id for item in props.cost_schedule_files]:
item = props.cost_schedule_files.add()
item.cost_item_id = cost_schedule.id()
item.csv_filepath = file_path
else:
return
@classmethod
def remove_csv_filepath(cls, cost_schedule: ifcopenshell.entity_instance = None) -> None:
if not cost_schedule:
return
props = cls.get_cost_props()
cost_schedule_id = cost_schedule.id()
if cost_schedule_id in [item.cost_item_id for item in props.cost_schedule_files]:
for i, item in enumerate(props.cost_schedule_files):
if item.cost_item_id == cost_schedule_id:
props.cost_schedule_files.remove(i)
print(f"Cost schedule id={cost_schedule_id} csv filepath correctly removed")
return
else:
return
@classmethod
def delete_all_cost_items(cls):
cost_schedule = tool.Cost.get_active_cost_schedule()
items = ifcopenshell.util.cost.get_root_cost_items(cost_schedule)
for item in items:
cost_item_id = item.id()
ifcopenshell.api.run("cost.remove_cost_item", tool.Ifc.get(), cost_item = item)
tool.Cost.clean_up_cost_item_tree(cost_item_id)
@classmethod
def refresh_cost_schedule_csv(cls):
from ifc5d.csv2ifc import Csv2Ifc
props = cls.get_cost_props()
cost_schedule_id = props.active_cost_schedule_id
file_path = next((item.csv_filepath for item in props.cost_schedule_files if item.cost_item_id == cost_schedule_id), None)
if not file_path:
return
cost_schedule = tool.Ifc.get_entity_by_id(cost_schedule_id)
csv2ifc = Csv2Ifc()
csv2ifc.csv = file_path
csv2ifc.file = tool.Ifc.get()
csv2ifc.cost_schedule = cost_schedule
csv2ifc.is_schedule_of_rates = False
csv2ifc.refresh()
print("Csv file correctly refreshed")
@classmethod
def add_cost_column(cls, name: str) -> None:
props = cls.get_cost_props()
+4
View File
@@ -127,6 +127,10 @@ class Csv2Ifc:
self.parse_csv()
self.create_ifc()
def refresh(self) -> None:
self.parse_csv()
self.create_cost_items(self.cost_items)
def parse_csv(self) -> None:
"""Fill ``headers`` and ``cost_items`` based on data from csv file."""
self.cost_items = []