This commit is contained in:
Andrej730
2024-08-07 16:18:34 +05:00
parent 98c4a1d110
commit ffbd3e50c5
4 changed files with 193 additions and 124 deletions
@@ -19,8 +19,10 @@
import bpy import bpy
import json import json
import logging import logging
import ifcdiff
import ifcopenshell import ifcopenshell
import blenderbim.bim.handler import blenderbim.bim.handler
import blenderbim.bim.import_ifc
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -175,7 +177,7 @@ class ExecuteIfcDiff(bpy.types.Operator):
blenderbim.bim.handler.refresh_ui_data() blenderbim.bim.handler.refresh_ui_data()
return {"FINISHED"} return {"FINISHED"}
def load_changed_elements(self, ifc_diff): def load_changed_elements(self, ifc_diff: ifcdiff.IfcDiff):
if not tool.Ifc.get() or self.props.active_file == "NONE" or not self.props.should_load_changed_elements: if not tool.Ifc.get() or self.props.active_file == "NONE" or not self.props.should_load_changed_elements:
return return
+91 -58
View File
@@ -17,25 +17,26 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Union from typing import TYPE_CHECKING, Optional, Union, Literal
if TYPE_CHECKING: if TYPE_CHECKING:
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.cost
import blenderbim.tool as tool import blenderbim.tool as tool
def add_cost_schedule(ifc: tool.Ifc, name, predefined_type): def add_cost_schedule(ifc: tool.Ifc, name: str, predefined_type: str) -> None:
ifc.run("cost.add_cost_schedule", name=name, predefined_type=predefined_type) 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): def edit_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None:
attributes = cost.get_cost_schedule_attributes() attributes = cost.get_cost_schedule_attributes()
ifc.run("cost.edit_cost_schedule", cost_schedule=cost_schedule, attributes=attributes) ifc.run("cost.edit_cost_schedule", cost_schedule=cost_schedule, attributes=attributes)
cost.disable_editing_cost_schedule() cost.disable_editing_cost_schedule()
def disable_editing_cost_schedule(cost: tool.Cost): def disable_editing_cost_schedule(cost: tool.Cost) -> None:
cost.disable_editing_cost_schedule() cost.disable_editing_cost_schedule()
@@ -44,67 +45,67 @@ def remove_cost_schedule(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopens
cost.remove_stored_schedule_columns(cost_schedule) cost.remove_stored_schedule_columns(cost_schedule)
def enable_editing_cost_schedule_attributes(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance): def enable_editing_cost_schedule_attributes(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None:
cost.load_cost_schedule_attributes(cost_schedule) cost.load_cost_schedule_attributes(cost_schedule)
cost.enable_editing_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): def enable_editing_cost_items(cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None:
cost.enable_editing_cost_items(cost_schedule) cost.enable_editing_cost_items(cost_schedule)
cost.load_active_schedule_columns() cost.load_active_schedule_columns()
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
cost.play_sound() cost.play_sound()
def add_summary_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance): def add_summary_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_schedule: ifcopenshell.entity_instance) -> None:
ifc.run("cost.add_cost_item", cost_schedule=cost_schedule) ifc.run("cost.add_cost_item", cost_schedule=cost_schedule)
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
# cost.play_sound() # cost.play_sound()
def add_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def add_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
ifc.run("cost.add_cost_item", cost_item=cost_item) ifc.run("cost.add_cost_item", cost_item=cost_item)
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
# cost.enable_editing_cost_schedule_attributes(cost_schedule) # cost.enable_editing_cost_schedule_attributes(cost_schedule)
def expand_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def expand_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.expand_cost_item(cost_item) cost.expand_cost_item(cost_item)
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def expand_cost_items(cost: tool.Cost): def expand_cost_items(cost: tool.Cost) -> None:
cost.expand_cost_items() cost.expand_cost_items()
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def contract_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def contract_cost_item(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.contract_cost_item(cost_item) cost.contract_cost_item(cost_item)
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def contract_cost_items(cost: tool.Cost): def contract_cost_items(cost: tool.Cost) -> None:
cost.contract_cost_items() cost.contract_cost_items()
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def remove_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item_id: int): def remove_cost_item(ifc: tool.Ifc, cost: tool.Cost, cost_item_id: int) -> None:
cost_item = ifc.get().by_id(cost_item_id) cost_item = ifc.get().by_id(cost_item_id)
ifc.run("cost.remove_cost_item", cost_item=cost_item) ifc.run("cost.remove_cost_item", cost_item=cost_item)
cost.clean_up_cost_item_tree(cost_item_id) cost.clean_up_cost_item_tree(cost_item_id)
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def enable_editing_cost_item_attributes(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def enable_editing_cost_item_attributes(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.enable_editing_cost_item_attributes(cost_item) cost.enable_editing_cost_item_attributes(cost_item)
cost.load_cost_item_attributes(cost_item) cost.load_cost_item_attributes(cost_item)
def disable_editing_cost_item(cost: tool.Cost): def disable_editing_cost_item(cost: tool.Cost) -> None:
cost.disable_editing_cost_item() cost.disable_editing_cost_item()
def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost): def edit_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None:
attributes = cost.get_cost_item_attributes() attributes = cost.get_cost_item_attributes()
ifc.run("cost.edit_cost_item", cost_item=cost.get_active_cost_item(), attributes=attributes) ifc.run("cost.edit_cost_item", cost_item=cost.get_active_cost_item(), attributes=attributes)
cost.disable_editing_cost_item() cost.disable_editing_cost_item()
@@ -148,85 +149,104 @@ def unassign_cost_item_type(
return product_types return product_types
def load_cost_item_types(cost: tool.Cost): def load_cost_item_types(cost: tool.Cost) -> None:
cost_item = cost.get_active_cost_item() cost_item = cost.get_active_cost_item()
cost.load_cost_item_types(cost_item) cost.load_cost_item_types(cost_item)
def assign_cost_item_quantity( def assign_cost_item_quantity(
ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type, prop_name ifc: tool.Ifc,
): cost: tool.Cost,
cost_item: ifcopenshell.entity_instance,
related_object_type: tool.Cost.RELATED_OBJECT_TYPE,
prop_name: str,
) -> None:
products = cost.get_products(related_object_type) products = cost.get_products(related_object_type)
if products: if products:
ifc.run("cost.assign_cost_item_quantity", cost_item=cost_item, products=products, prop_name=prop_name) ifc.run("cost.assign_cost_item_quantity", cost_item=cost_item, products=products, prop_name=prop_name)
cost.load_cost_item_quantity_assignments(cost_item, related_object_type=related_object_type) cost.load_cost_item_quantity_assignments(cost_item, related_object_type=related_object_type)
def load_cost_item_quantities(cost: tool.Cost): def load_cost_item_quantities(cost: tool.Cost) -> None:
cost.load_cost_item_quantities() cost.load_cost_item_quantities()
def load_cost_item_element_quantities(cost: tool.Cost): def load_cost_item_element_quantities(cost: tool.Cost) -> None:
cost_item = cost.get_highlighted_cost_item() cost_item = cost.get_highlighted_cost_item()
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT") cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT")
def load_cost_item_task_quantities(cost: tool.Cost): def load_cost_item_task_quantities(cost: tool.Cost) -> None:
cost_item = cost.get_highlighted_cost_item() cost_item = cost.get_highlighted_cost_item()
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PROCESS") cost.load_cost_item_quantity_assignments(cost_item, related_object_type="PROCESS")
def load_cost_item_resource_quantities(cost: tool.Cost): def load_cost_item_resource_quantities(cost: tool.Cost) -> None:
cost_item = cost.get_highlighted_cost_item() cost_item = cost.get_highlighted_cost_item()
cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE") cost.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE")
def assign_cost_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, cost_rate): def assign_cost_value(
ifc: tool.Ifc, 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) ifc.run("cost.assign_cost_value", cost_item=cost_item, cost_rate=cost_rate)
def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates): def load_schedule_of_rates(cost: tool.Cost, schedule_of_rates: ifcopenshell.entity_instance) -> None:
cost.load_schedule_of_rates_tree(schedule_of_rates) cost.load_schedule_of_rates_tree(schedule_of_rates)
def unassign_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, products): def unassign_cost_item_quantity(
ifc: tool.Ifc,
cost: tool.Cost,
cost_item: ifcopenshell.entity_instance,
products: list[ifcopenshell.entity_instance],
) -> None:
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products) ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
cost.load_cost_item_quantities() cost.load_cost_item_quantities()
def enable_editing_cost_item_quantities(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def enable_editing_cost_item_quantities(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.enable_editing_cost_item_quantities(cost_item) cost.enable_editing_cost_item_quantities(cost_item)
def enable_editing_cost_item_values(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def enable_editing_cost_item_values(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.enable_editing_cost_item_values(cost_item) cost.enable_editing_cost_item_values(cost_item)
def add_cost_item_quantity(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance, ifc_class): def add_cost_item_quantity(ifc: 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) 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): def remove_cost_item_quantity(
ifc: 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) 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): def enable_editing_cost_item_quantity(cost: tool.Cost, physical_quantity: ifcopenshell.entity_instance) -> None:
cost.load_cost_item_quantity_attributes(physical_quantity) cost.load_cost_item_quantity_attributes(physical_quantity)
cost.enable_editing_cost_item_quantity(physical_quantity) cost.enable_editing_cost_item_quantity(physical_quantity)
def disable_editing_cost_item_quantity(cost: tool.Cost): def disable_editing_cost_item_quantity(cost: tool.Cost) -> None:
cost.disable_editing_cost_item_quantity() cost.disable_editing_cost_item_quantity()
def edit_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, physical_quantity): def edit_cost_item_quantity(ifc: tool.Ifc, cost: tool.Cost, physical_quantity: ifcopenshell.entity_instance) -> None:
attributes = cost.get_cost_item_quantity_attributes() attributes = cost.get_cost_item_quantity_attributes()
ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes) ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
cost.disable_editing_cost_item_quantity() cost.disable_editing_cost_item_quantity()
cost.load_cost_item_quantities() cost.load_cost_item_quantities()
def add_cost_value(ifc: tool.Ifc, cost: tool.Cost, parent, cost_type, cost_category): def add_cost_value(
ifc: tool.Ifc,
cost: tool.Cost,
parent: ifcopenshell.entity_instance,
cost_type: Literal["FIXED", "SUM", "CATEGORY"],
cost_category: Optional[str] = None,
) -> None:
value = ifc.run("cost.add_cost_value", parent=parent) value = ifc.run("cost.add_cost_value", parent=parent)
ifc.run( ifc.run(
"cost.edit_cost_value", "cost.edit_cost_value",
@@ -235,84 +255,95 @@ def add_cost_value(ifc: tool.Ifc, cost: tool.Cost, parent, cost_type, cost_categ
) )
def remove_cost_value(ifc: tool.Ifc, parent, cost_value): def remove_cost_value(
ifc: tool.Ifc, parent: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance
) -> None:
ifc.run("cost.remove_cost_value", parent=parent, cost_value=cost_value) ifc.run("cost.remove_cost_value", parent=parent, cost_value=cost_value)
def enable_editing_cost_item_value(cost: tool.Cost, cost_value): def enable_editing_cost_item_value(cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None:
cost.load_cost_item_value_attributes(cost_value) cost.load_cost_item_value_attributes(cost_value)
cost.enable_editing_cost_item_value(cost_value) cost.enable_editing_cost_item_value(cost_value)
def disable_editing_cost_item_value(cost: tool.Cost): def disable_editing_cost_item_value(cost: tool.Cost) -> None:
cost.disable_editing_cost_item_value() cost.disable_editing_cost_item_value()
def enable_editing_cost_item_value_formula(cost: tool.Cost, cost_value): def enable_editing_cost_item_value_formula(cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None:
cost.load_cost_item_value_formula_attributes(cost_value) cost.load_cost_item_value_formula_attributes(cost_value)
cost.enable_editing_cost_item_value_formula(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): def edit_cost_item_value_formula(ifc: tool.Ifc, cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None:
formula = cost.get_cost_item_value_formula() formula = cost.get_cost_item_value_formula()
ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula) ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula)
cost.disable_editing_cost_item_value() cost.disable_editing_cost_item_value()
def edit_cost_value(ifc: tool.Ifc, cost: tool.Cost, cost_value): def edit_cost_value(ifc: tool.Ifc, cost: tool.Cost, cost_value: ifcopenshell.entity_instance) -> None:
attributes = cost.get_cost_value_attributes() attributes = cost.get_cost_value_attributes()
ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes) ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes)
cost.disable_editing_cost_item_value() cost.disable_editing_cost_item_value()
# cost.load_cost_item_values(cost.get_highlighted_cost_item()) # cost.load_cost_item_values(cost.get_highlighted_cost_item())
def copy_cost_item_values(ifc: tool.Ifc, cost: tool.Cost, source, destination): def copy_cost_item_values(
ifc: tool.Ifc, cost: tool.Cost, source: ifcopenshell.entity_instance, destination: ifcopenshell.entity_instance
) -> None:
ifc.run("cost.copy_cost_item_values", source=source, destination=destination) 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): def select_cost_item_products(cost: tool.Cost, spatial: tool.Spatial, cost_item: ifcopenshell.entity_instance) -> None:
is_deep = cost.show_nested_cost_item_elements() is_deep = cost.show_nested_cost_item_elements()
products = cost.get_cost_item_products(cost_item, is_deep) products = cost.get_cost_item_products(cost_item, is_deep)
spatial.select_products(products) spatial.select_products(products)
def select_cost_schedule_products(cost: tool.Cost, spatial: tool.Spatial, cost_schedule: ifcopenshell.entity_instance): def select_cost_schedule_products(
cost: tool.Cost, spatial: tool.Spatial, cost_schedule: ifcopenshell.entity_instance
) -> None:
products = cost.get_cost_schedule_products(cost_schedule) products = cost.get_cost_schedule_products(cost_schedule)
spatial.select_products(products) spatial.select_products(products)
def import_cost_schedule_csv(cost: tool.Cost, file_path, is_schedule_of_rates): 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) cost.import_cost_schedule_csv(file_path, is_schedule_of_rates)
def add_cost_column(cost: tool.Cost, name): def add_cost_column(cost: tool.Cost, name: str) -> None:
cost.add_cost_column(name) cost.add_cost_column(name)
def remove_cost_column(cost: tool.Cost, name): def remove_cost_column(cost: tool.Cost, name: str) -> None:
cost.remove_cost_column(name) cost.remove_cost_column(name)
def expand_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def expand_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.expand_cost_item_rate(cost_item) cost.expand_cost_item_rate(cost_item)
def contract_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def contract_cost_item_rate(cost: tool.Cost, cost_item: ifcopenshell.entity_instance) -> None:
cost.contract_cost_item_rate(cost_item) cost.contract_cost_item_rate(cost_item)
def calculate_cost_item_resource_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance): def calculate_cost_item_resource_value(ifc: tool.Ifc, cost_item: ifcopenshell.entity_instance) -> None:
ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item) ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item)
def export_cost_schedules(cost: tool.Cost, filepath: str, format: str, cost_schedule=None): def export_cost_schedules(
cost: tool.Cost, filepath: str, format: str, cost_schedule: Union[ifcopenshell.entity_instance, None] = None
) -> Union[str, None]:
cost.play_sound() cost.play_sound()
return cost.export_cost_schedules(filepath, format, cost_schedule) return cost.export_cost_schedules(filepath, format, cost_schedule)
def clear_cost_item_assignments( def clear_cost_item_assignments(
ifc: tool.Ifc, cost: tool.Cost, cost_item: ifcopenshell.entity_instance, related_object_type ifc: tool.Ifc,
): cost: tool.Cost,
cost_item: ifcopenshell.entity_instance,
related_object_type: ifcopenshell.util.cost.FILTER_BY_TYPE,
) -> None:
products = cost.get_cost_item_assignments(cost_item, filter_by_type=related_object_type) products = cost.get_cost_item_assignments(cost_item, filter_by_type=related_object_type)
if products: if products:
ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products) ifc.run("cost.unassign_cost_item_quantity", cost_item=cost_item, products=products)
@@ -320,7 +351,7 @@ def clear_cost_item_assignments(
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial): def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spatial) -> None:
spatial.deselect_objects() spatial.deselect_objects()
products = ifc.get().by_type("IfcElement") products = ifc.get().by_type("IfcElement")
cost_schedule = cost.get_active_cost_schedule() cost_schedule = cost.get_active_cost_schedule()
@@ -328,11 +359,13 @@ def select_unassigned_products(ifc: tool.Ifc, cost: tool.Cost, spatial: tool.Spa
spatial.select_products(selection) spatial.select_products(selection)
def load_product_cost_items(cost: tool.Cost, product): def load_product_cost_items(cost: tool.Cost, product: ifcopenshell.entity_instance) -> None:
cost.load_product_cost_items(product) cost.load_product_cost_items(product)
def highlight_product_cost_item(spatial: tool.Spatial, cost: tool.Cost, cost_item: ifcopenshell.entity_instance): def highlight_product_cost_item(
spatial: tool.Spatial, cost: tool.Cost, cost_item: ifcopenshell.entity_instance
) -> Union[str, None]:
cost_schedule = cost.get_cost_schedule(cost_item) cost_schedule = cost.get_cost_schedule(cost_item)
is_cost_schedule_active = cost.is_cost_schedule_active(cost_schedule) is_cost_schedule_active = cost.is_cost_schedule_active(cost_schedule)
if is_cost_schedule_active: if is_cost_schedule_active:
@@ -341,7 +374,7 @@ def highlight_product_cost_item(spatial: tool.Spatial, cost: tool.Cost, cost_ite
return "Cost schedule is not active" return "Cost schedule is not active"
def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent): def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent) -> Union[str, None]:
cost_item = cost.get_active_cost_item() cost_item = cost.get_active_cost_item()
if cost_item and cost.is_root_cost_item(cost_item): if cost_item and cost.is_root_cost_item(cost_item):
return "Cannot change root cost item" return "Cannot change root cost item"
@@ -351,7 +384,7 @@ def change_parent_cost_item(ifc: tool.Ifc, cost: tool.Cost, new_parent):
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost): def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost) -> None:
cost_item = cost.get_highlighted_cost_item() cost_item = cost.get_highlighted_cost_item()
if cost_item: if cost_item:
cost_item = ifc.run("cost.copy_cost_item", cost_item=cost_item) cost_item = ifc.run("cost.copy_cost_item", cost_item=cost_item)
@@ -359,7 +392,7 @@ def copy_cost_item(ifc: tool.Ifc, cost: tool.Cost):
cost.load_cost_schedule_tree() cost.load_cost_schedule_tree()
def add_currency(ifc: tool.Ifc, cost: tool.Cost): def add_currency(ifc: tool.Ifc, cost: tool.Cost) -> ifcopenshell.entity_instance:
unit = ifc.run("unit.add_monetary_unit") unit = ifc.run("unit.add_monetary_unit")
attributes = cost.get_currency_attributes() attributes = cost.get_currency_attributes()
ifc.run("unit.edit_monetary_unit", unit=unit, attributes=attributes) ifc.run("unit.edit_monetary_unit", unit=unit, attributes=attributes)
+66 -50
View File
@@ -9,17 +9,20 @@ import ifcopenshell.util.cost
import ifcopenshell.util.unit import ifcopenshell.util.unit
import blenderbim.bim.helper import blenderbim.bim.helper
import json import json
from typing import Optional, Any, Generator, Union from typing import Optional, Any, Generator, Union, Literal
class Cost(blenderbim.core.tool.Cost): class Cost(blenderbim.core.tool.Cost):
RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"]
@classmethod @classmethod
def get_cost_schedule_attributes(cls): def get_cost_schedule_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
return blenderbim.bim.helper.export_attributes(props.cost_schedule_attributes) return blenderbim.bim.helper.export_attributes(props.cost_schedule_attributes)
@classmethod @classmethod
def disable_editing_cost_schedule(cls): def disable_editing_cost_schedule(cls) -> None:
cls.store_active_schedule_columns() cls.store_active_schedule_columns()
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = 0 bpy.context.scene.BIMCostProperties.active_cost_schedule_id = 0
cls.disable_editing_cost_item() cls.disable_editing_cost_item()
@@ -74,12 +77,12 @@ class Cost(blenderbim.core.tool.Cost):
storage.remove(storage_col_i) storage.remove(storage_col_i)
@classmethod @classmethod
def enable_editing_cost_schedule_attributes(cls, cost_schedule): def enable_editing_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id() bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id()
bpy.context.scene.BIMCostProperties.is_editing = "COST_SCHEDULE_ATTRIBUTES" bpy.context.scene.BIMCostProperties.is_editing = "COST_SCHEDULE_ATTRIBUTES"
@classmethod @classmethod
def load_cost_schedule_attributes(cls, cost_schedule): def load_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
def special_import(name, prop, data): def special_import(name, prop, data):
if name in ["SubmittedOn", "UpdateDate"]: if name in ["SubmittedOn", "UpdateDate"]:
prop.string_value = "" if prop.is_null else ifcopenshell.util.date.ifc2datetime(data[name]).isoformat() prop.string_value = "" if prop.is_null else ifcopenshell.util.date.ifc2datetime(data[name]).isoformat()
@@ -96,12 +99,12 @@ class Cost(blenderbim.core.tool.Cost):
props.is_editing = "COST_ITEMS" props.is_editing = "COST_ITEMS"
@classmethod @classmethod
def play_sound(cls): def play_sound(cls) -> None:
if tool.Blender.get_addon_preferences().should_play_chaching_sound: if tool.Blender.get_addon_preferences().should_play_chaching_sound:
cls.play_chaching_sound() # lol cls.play_chaching_sound() # lol
@classmethod @classmethod
def play_chaching_sound(cls): def play_chaching_sound(cls) -> None:
# TODO: make pitch higher as costs rise # TODO: make pitch higher as costs rise
try: try:
import aud import aud
@@ -118,7 +121,7 @@ class Cost(blenderbim.core.tool.Cost):
pass # ah well pass # ah well
@classmethod @classmethod
def load_cost_schedule_tree(cls): def load_cost_schedule_tree(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
props.is_cost_update_enabled = False props.is_cost_update_enabled = False
cost_schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id) cost_schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
@@ -132,7 +135,7 @@ class Cost(blenderbim.core.tool.Cost):
props.is_cost_update_enabled = True props.is_cost_update_enabled = True
@classmethod @classmethod
def expand_cost_item(cls, cost_item): def expand_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "contracted_cost_items"): if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items) cls.contracted_cost_items = json.loads(props.contracted_cost_items)
@@ -141,7 +144,7 @@ class Cost(blenderbim.core.tool.Cost):
props.contracted_cost_items = json.dumps(cls.contracted_cost_items) props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
@classmethod @classmethod
def expand_cost_items(cls): def expand_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
cls.contracted_cost_items = json.loads(props.contracted_cost_items) cls.contracted_cost_items = json.loads(props.contracted_cost_items)
for cost_item in props.cost_items: for cost_item in props.cost_items:
@@ -150,7 +153,7 @@ class Cost(blenderbim.core.tool.Cost):
props.contracted_cost_items = json.dumps(cls.contracted_cost_items) props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
@classmethod @classmethod
def contract_cost_item(cls, cost_item): def contract_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "contracted_cost_items"): if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items) cls.contracted_cost_items = json.loads(props.contracted_cost_items)
@@ -158,7 +161,7 @@ class Cost(blenderbim.core.tool.Cost):
props.contracted_cost_items = json.dumps(cls.contracted_cost_items) props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
@classmethod @classmethod
def contract_cost_items(cls): def contract_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "contracted_cost_items"): if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items) cls.contracted_cost_items = json.loads(props.contracted_cost_items)
@@ -168,7 +171,7 @@ class Cost(blenderbim.core.tool.Cost):
props.contracted_cost_items = json.dumps(cls.contracted_cost_items) props.contracted_cost_items = json.dumps(cls.contracted_cost_items)
@classmethod @classmethod
def clean_up_cost_item_tree(cls, cost_item_id): def clean_up_cost_item_tree(cls, cost_item_id: int) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "contracted_cost_items"): if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items) cls.contracted_cost_items = json.loads(props.contracted_cost_items)
@@ -180,23 +183,23 @@ class Cost(blenderbim.core.tool.Cost):
cls.enable_editing_cost_items(cost_schedule=tool.Ifc.get().by_id(props.active_cost_schedule_id)) cls.enable_editing_cost_items(cost_schedule=tool.Ifc.get().by_id(props.active_cost_schedule_id))
@classmethod @classmethod
def enable_editing_cost_item_attributes(cls, cost_item): def enable_editing_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance):
bpy.context.scene.BIMCostProperties.active_cost_item_id = cost_item.id() bpy.context.scene.BIMCostProperties.active_cost_item_id = cost_item.id()
bpy.context.scene.BIMCostProperties.cost_item_editing_type = "ATTRIBUTES" bpy.context.scene.BIMCostProperties.cost_item_editing_type = "ATTRIBUTES"
@classmethod @classmethod
def load_cost_item_attributes(cls, cost_item): def load_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
props.cost_item_attributes.clear() props.cost_item_attributes.clear()
blenderbim.bim.helper.import_attributes2(cost_item, props.cost_item_attributes) blenderbim.bim.helper.import_attributes2(cost_item, props.cost_item_attributes)
@classmethod @classmethod
def disable_editing_cost_item(cls): def disable_editing_cost_item(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0 bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
@classmethod @classmethod
def get_cost_item_attributes(cls): def get_cost_item_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
return blenderbim.bim.helper.export_attributes(props.cost_item_attributes) return blenderbim.bim.helper.export_attributes(props.cost_item_attributes)
@@ -208,7 +211,7 @@ class Cost(blenderbim.core.tool.Cost):
return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_item_id) return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_item_id)
@classmethod @classmethod
def get_highlighted_cost_item(cls): def get_highlighted_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
if not props.active_cost_schedule_id: if not props.active_cost_schedule_id:
return return
@@ -240,7 +243,9 @@ class Cost(blenderbim.core.tool.Cost):
new.name = related_object.Name or "Unnamed" new.name = related_object.Name or "Unnamed"
@classmethod @classmethod
def load_cost_item_quantity_assignments(cls, cost_item: ifcopenshell.entity_instance, related_object_type): def load_cost_item_quantity_assignments(
cls, cost_item: ifcopenshell.entity_instance, related_object_type: RELATED_OBJECT_TYPE
) -> None:
def create_list_items(collection, cost_item, is_deep): def create_list_items(collection, cost_item, is_deep):
products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False) products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False)
for product in products: for product in products:
@@ -291,9 +296,9 @@ class Cost(blenderbim.core.tool.Cost):
return selected_quantitites, unit return selected_quantitites, unit
@classmethod @classmethod
def get_products(cls, related_object_type=None): def get_products(cls, related_object_type: RELATED_OBJECT_TYPE) -> list[ifcopenshell.entity_instance]:
if related_object_type == "PRODUCT": if related_object_type == "PRODUCT":
products = tool.Spatial.get_selected_products() products = list(tool.Spatial.get_selected_products())
elif related_object_type == "PROCESS": elif related_object_type == "PROCESS":
products = [tool.Sequence.get_highlighted_task()] products = [tool.Sequence.get_highlighted_task()]
elif related_object_type == "RESOURCE": elif related_object_type == "RESOURCE":
@@ -301,17 +306,17 @@ class Cost(blenderbim.core.tool.Cost):
return products or [] return products or []
@classmethod @classmethod
def enable_editing_cost_item_quantities(cls, cost_item=None): def enable_editing_cost_item_quantities(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
props.active_cost_item_id = cost_item.id() props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "QUANTITIES" props.cost_item_editing_type = "QUANTITIES"
@classmethod @classmethod
def enable_editing_cost_item_quantity(cls, physical_quantity=None): def enable_editing_cost_item_quantity(cls, physical_quantity: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = physical_quantity.id() bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = physical_quantity.id()
@classmethod @classmethod
def load_cost_item_quantity_attributes(cls, physical_quantity=None): def load_cost_item_quantity_attributes(cls, physical_quantity: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
props.quantity_attributes.clear() props.quantity_attributes.clear()
blenderbim.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes) blenderbim.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes)
@@ -323,16 +328,18 @@ class Cost(blenderbim.core.tool.Cost):
props.cost_item_editing_type = "VALUES" props.cost_item_editing_type = "VALUES"
@classmethod @classmethod
def disable_editing_cost_item_quantity(cls): def disable_editing_cost_item_quantity(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = 0 bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = 0
@classmethod @classmethod
def get_cost_item_quantity_attributes(cls): def get_cost_item_quantity_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
return blenderbim.bim.helper.export_attributes(props.quantity_attributes) return blenderbim.bim.helper.export_attributes(props.quantity_attributes)
@classmethod @classmethod
def get_attributes_for_cost_value(cls, cost_type, cost_category): def get_attributes_for_cost_value(
cls, cost_type: Literal["FIXED", "SUM", "CATEGORY"], cost_category: Optional[str] = None
) -> dict[str, Any]:
if cost_type == "FIXED": if cost_type == "FIXED":
category = None category = None
attributes = {"AppliedValue": bpy.context.scene.BIMCostProperties.fixed_cost_value} attributes = {"AppliedValue": bpy.context.scene.BIMCostProperties.fixed_cost_value}
@@ -429,7 +436,7 @@ class Cost(blenderbim.core.tool.Cost):
props.cost_value_editing_type = "FORMULA" props.cost_value_editing_type = "FORMULA"
@classmethod @classmethod
def get_cost_item_value_formula(cls): def get_cost_item_value_formula(cls) -> str:
return bpy.context.scene.BIMCostProperties.cost_value_formula return bpy.context.scene.BIMCostProperties.cost_value_formula
@classmethod @classmethod
@@ -469,7 +476,7 @@ class Cost(blenderbim.core.tool.Cost):
) )
@classmethod @classmethod
def show_nested_cost_item_elements(cls): def show_nested_cost_item_elements(cls) -> bool:
return bpy.context.scene.BIMCostProperties.show_nested_elements return bpy.context.scene.BIMCostProperties.show_nested_elements
@classmethod @classmethod
@@ -532,7 +539,7 @@ class Cost(blenderbim.core.tool.Cost):
props.columns.remove(props.columns.find(name)) props.columns.remove(props.columns.find(name))
@classmethod @classmethod
def expand_cost_item_rate(cls, cost_item): def expand_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates) 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)
@@ -540,7 +547,7 @@ class Cost(blenderbim.core.tool.Cost):
cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates))) cls.load_schedule_of_rates_tree(schedule_of_rates=tool.Ifc.get().by_id(int(props.schedule_of_rates)))
@classmethod @classmethod
def contract_cost_item_rate(cls, cost_item): def contract_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates) 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)
@@ -571,7 +578,7 @@ class Cost(blenderbim.core.tool.Cost):
] ]
@classmethod @classmethod
def load_schedule_of_rates_tree(cls, schedule_of_rates): def load_schedule_of_rates_tree(cls, schedule_of_rates: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
props.is_cost_update_enabled = False props.is_cost_update_enabled = False
props.cost_item_rates.clear() props.cost_item_rates.clear()
@@ -585,7 +592,9 @@ class Cost(blenderbim.core.tool.Cost):
props.is_cost_update_enabled = True props.is_cost_update_enabled = True
@classmethod @classmethod
def export_cost_schedules(cls, filepath: str, format: str, cost_schedule=None): def export_cost_schedules(
cls, filepath: str, format: str, cost_schedule: Optional[ifcopenshell.entity_instance] = None
) -> Union[str, None]:
import subprocess import subprocess
import os import os
import sys import sys
@@ -624,7 +633,7 @@ class Cost(blenderbim.core.tool.Cost):
return "Could not open file location" return "Could not open file location"
@classmethod @classmethod
def get_units(cls): def get_units(cls) -> dict[int, str]:
units = {} units = {}
for unit in tool.Ifc.get().by_type("IfcNamedUnit"): for unit in tool.Ifc.get().by_type("IfcNamedUnit"):
if unit.get_info().get("UnitType", None) in [ if unit.get_info().get("UnitType", None) in [
@@ -639,7 +648,7 @@ class Cost(blenderbim.core.tool.Cost):
return units return units
@staticmethod @staticmethod
def format_unit(unit): def format_unit(unit: ifcopenshell.entity_instance) -> str:
if unit.is_a("IfcContextDependentUnit"): if unit.is_a("IfcContextDependentUnit"):
return f"{unit.UnitType} / {unit.Name}" return f"{unit.UnitType} / {unit.Name}"
else: else:
@@ -649,7 +658,7 @@ class Cost(blenderbim.core.tool.Cost):
return f"{unit.UnitType} / {name}" return f"{unit.UnitType} / {name}"
@classmethod @classmethod
def get_cost_schedule(cls, cost_item): def get_cost_schedule(cls, cost_item: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
for rel in cost_item.HasAssignments or []: for rel in cost_item.HasAssignments or []:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"): if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
return rel.RelatingControl return rel.RelatingControl
@@ -657,17 +666,17 @@ class Cost(blenderbim.core.tool.Cost):
return cls.get_cost_schedule(rel.RelatingObject) return cls.get_cost_schedule(rel.RelatingObject)
@classmethod @classmethod
def is_cost_schedule_active(cls, cost_schedule): def is_cost_schedule_active(cls, cost_schedule: ifcopenshell.entity_instance) -> bool:
return True if cost_schedule.id() == bpy.context.scene.BIMCostProperties.active_cost_schedule_id else False return True if cost_schedule.id() == bpy.context.scene.BIMCostProperties.active_cost_schedule_id else False
@classmethod @classmethod
def get_active_cost_schedule(cls): def get_active_cost_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
if not bpy.context.scene.BIMCostProperties.active_cost_schedule_id: if not bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
return None return None
return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id) return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
@classmethod @classmethod
def highlight_cost_item(cls, cost_item): def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
def expand_ancestors(cost_item): def expand_ancestors(cost_item):
cls.expand_cost_item(cost_item) cls.expand_cost_item(cost_item)
for rel in cost_item.Nests or []: for rel in cost_item.Nests or []:
@@ -685,11 +694,13 @@ class Cost(blenderbim.core.tool.Cost):
bpy.context.scene.BIMCostProperties.active_cost_item_index = cost_item_index bpy.context.scene.BIMCostProperties.active_cost_item_index = cost_item_index
@classmethod @classmethod
def get_cost_items_for_product(cls, product): def get_cost_items_for_product(cls, product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.cost.get_cost_items_for_product(product) return ifcopenshell.util.cost.get_cost_items_for_product(product)
@classmethod @classmethod
def has_cost_assignments(cls, product, cost_schedule=None): def has_cost_assignments(
cls, product: ifcopenshell.entity_instance, cost_schedule: Optional[ifcopenshell.entity_instance] = None
) -> bool:
cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product) cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product)
if cost_schedule: if cost_schedule:
cost_items = [ cost_items = [
@@ -714,7 +725,7 @@ class Cost(blenderbim.core.tool.Cost):
new.total_cost_quantity = ifcopenshell.util.cost.get_total_quantity(cost_item) new.total_cost_quantity = ifcopenshell.util.cost.get_total_quantity(cost_item)
@classmethod @classmethod
def get_quantity_unit_symbol(cls, quantity): def get_quantity_unit_symbol(cls, quantity: ifcopenshell.entity_instance) -> Union[str, None]:
unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get()) unit = ifcopenshell.util.unit.get_property_unit(quantity, tool.Ifc.get())
if unit: if unit:
return ifcopenshell.util.unit.get_unit_symbol(unit) return ifcopenshell.util.unit.get_unit_symbol(unit)
@@ -722,14 +733,15 @@ class Cost(blenderbim.core.tool.Cost):
return None return None
@classmethod @classmethod
def is_root_cost_item(cls, cost_item): def is_root_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> bool:
if cost_item.HasAssignments: if cost_item.HasAssignments:
for rel in cost_item.HasAssignments: for rel in cost_item.HasAssignments:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"): if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
return True return True
return False
@classmethod @classmethod
def toggle_cost_item_parent_change(cls, cost_item=None): def toggle_cost_item_parent_change(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
if not cost_item: if not cost_item:
return return
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
@@ -740,16 +752,18 @@ class Cost(blenderbim.core.tool.Cost):
cls.disable_editing_cost_item_parent() cls.disable_editing_cost_item_parent()
@classmethod @classmethod
def change_parent_cost_item(cls, cost_item, new_parent): def change_parent_cost_item(
cls, cost_item: ifcopenshell.entity_instance, new_parent: ifcopenshell.entity_instance
) -> None:
ifcopenshell.api.run("nest.change_nest", tool.Ifc.get(), item=cost_item, new_parent=new_parent) ifcopenshell.api.run("nest.change_nest", tool.Ifc.get(), item=cost_item, new_parent=new_parent)
@classmethod @classmethod
def disable_editing_cost_item_parent(cls): def disable_editing_cost_item_parent(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0 bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
@classmethod @classmethod
def load_cost_item_quantities(cls, cost_item=None): def load_cost_item_quantities(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
if not cost_item: if not cost_item:
cost_item = cls.get_highlighted_cost_item() cost_item = cls.get_highlighted_cost_item()
if not cost_item: if not cost_item:
@@ -759,7 +773,9 @@ class Cost(blenderbim.core.tool.Cost):
cls.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE") cls.load_cost_item_quantity_assignments(cost_item, related_object_type="RESOURCE")
@classmethod @classmethod
def update_cost_items(cls, product=None, pset=None): def update_cost_items(
cls, product: Optional[ifcopenshell.entity_instance] = None, pset: Optional[ifcopenshell.entity_instance] = None
) -> None:
cost_items = [] cost_items = []
if product: if product:
cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product) cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product)
@@ -779,11 +795,11 @@ class Cost(blenderbim.core.tool.Cost):
cls.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT") cls.load_cost_item_quantity_assignments(cost_item, related_object_type="PRODUCT")
@classmethod @classmethod
def has_schedules(cls): def has_schedules(cls) -> bool:
return bool(tool.Ifc.get().by_type("IfcCostSchedule")) return bool(tool.Ifc.get().by_type("IfcCostSchedule"))
@classmethod @classmethod
def get_currency_attributes(cls): def get_currency_attributes(cls) -> dict[str, str]:
props = bpy.context.scene.BIMCostProperties props = bpy.context.scene.BIMCostProperties
currency = props.currency currency = props.currency
if currency == "CUSTOM": if currency == "CUSTOM":
+33 -15
View File
@@ -35,30 +35,29 @@ import ifcopenshell.util.classification
import ifcopenshell.util.representation import ifcopenshell.util.representation
from deepdiff import DeepDiff from deepdiff import DeepDiff
from ordered_set import OrderedSet from ordered_set import OrderedSet
from typing import Optional, Union, Literal, Any
__version__ = version = "0.0.0" __version__ = version = "0.0.0"
RELATIONSHIP_TYPE = Literal["geometry", "attributes", "type", "property", "container", "aggregate", "classification"]
class IfcDiff: class IfcDiff:
"""Main IfcDiff application """Main IfcDiff application
If you are using IfcDiff as a library, this is the class you should use. If you are using IfcDiff as a library, this is the class you should use.
:param old: IFC file object for the old model :param old: IFC file object for the old model
:type old: ifcopenshell.file.file
:param new: IFC file object for the new model :param new: IFC file object for the new model
:type new: ifcopenshell.file.file
:param relationships: List of relationships to check. None means that only :param relationships: List of relationships to check. None means that only
geometry is compared. geometry is compared. See RELATIONSHIP_TYPE for available relationships.
:type relationships: list[string]
:param is_shallow: True if you want only the first difference to be listed. :param is_shallow: True if you want only the first difference to be listed.
False if you want all differences to be checked. Choosing False means False if you want all differences to be checked. Choosing False means
that comparisons will take longer. that comparisons will take longer.
:type is_shallow: bool
:param filter_elements: An IFC filter query if you only want to compare a :param filter_elements: An IFC filter query if you only want to compare a
subset of elements. For example: ``IfcWall`` to only compare walls. subset of elements. For example: ``IfcWall`` to only compare walls.
:type filter_elements: string
Example:: Example::
@@ -70,7 +69,19 @@ class IfcDiff:
ifc_diff.export() ifc_diff.export()
""" """
def __init__(self, old, new, relationships=None, is_shallow=True, filter_elements=None): added_elements: set[ifcopenshell.entity_instance]
deleted_elements: set[ifcopenshell.entity_instance]
# GlobalIds to changes dictionary.
change_register: dict[str, dict[str, Any]]
def __init__(
self,
old: ifcopenshell.file,
new: ifcopenshell.file,
relationships: Optional[list[RELATIONSHIP_TYPE]] = None,
is_shallow: bool = True,
filter_elements: Optional[str] = None,
):
self.old = old self.old = old
self.new = new self.new = new
self.change_register = {} self.change_register = {}
@@ -80,7 +91,7 @@ class IfcDiff:
self.is_shallow = is_shallow self.is_shallow = is_shallow
self.filter_elements = filter_elements self.filter_elements = filter_elements
def diff(self): def diff(self) -> None:
logging.disable(logging.CRITICAL) logging.disable(logging.CRITICAL)
self.precision = self.get_precision() self.precision = self.get_precision()
@@ -187,7 +198,9 @@ class IfcDiff:
logging.disable(logging.NOTSET) logging.disable(logging.NOTSET)
def summarise_shapes(self, ifc, elements): def summarise_shapes(
self, ifc: ifcopenshell.file, elements: list[ifcopenshell.entity_instance]
) -> dict[str, dict[str, Any]]:
shapes = {} shapes = {}
iterator = ifcopenshell.geom.iterator( iterator = ifcopenshell.geom.iterator(
self.get_settings(ifc), ifc, multiprocessing.cpu_count(), include=elements self.get_settings(ifc), ifc, multiprocessing.cpu_count(), include=elements
@@ -215,7 +228,7 @@ class IfcDiff:
break break
return shapes return shapes
def get_settings(self, ifc): def get_settings(self, ifc: ifcopenshell.file) -> ifcopenshell.geom.settings:
settings = ifcopenshell.geom.settings() settings = ifcopenshell.geom.settings()
# Are you feeling lucky? # Are you feeling lucky?
settings.set(settings.DISABLE_BOOLEAN_RESULT, True) settings.set(settings.DISABLE_BOOLEAN_RESULT, True)
@@ -246,7 +259,7 @@ class IfcDiff:
return list(obj) return list(obj)
return json.JSONEncoder.default(None, obj) return json.JSONEncoder.default(None, obj)
def export(self, path): def export(self, path: str) -> None:
with open(path, "w", encoding="utf-8") as diff_file: with open(path, "w", encoding="utf-8") as diff_file:
json.dump( json.dump(
{ {
@@ -259,7 +272,7 @@ class IfcDiff:
default=self.json_dump_default, default=self.json_dump_default,
) )
def get_precision(self): def get_precision(self) -> float:
contexts = [c for c in self.new.by_type("IfcGeometricRepresentationContext") if c.ContextType == "Model"] contexts = [c for c in self.new.by_type("IfcGeometricRepresentationContext") if c.ContextType == "Model"]
if contexts: if contexts:
return contexts[0].Precision or 1e-4 return contexts[0].Precision or 1e-4
@@ -356,7 +369,7 @@ class IfcDiff:
# self.representation_ids[new_rep_id] = result # self.representation_ids[new_rep_id] = result
# return result # return result
def diff_representation(self, old_rep_id, new_rep_id): def diff_representation(self, old_rep_id: int, new_rep_id: int) -> bool:
old_rep = self.old.by_id(old_rep_id) old_rep = self.old.by_id(old_rep_id)
new_rep = self.new.by_id(new_rep_id) new_rep = self.new.by_id(new_rep_id)
if len(old_rep.Items) != len(new_rep.Items): if len(old_rep.Items) != len(new_rep.Items):
@@ -365,8 +378,11 @@ class IfcDiff:
result = self.diff_representation_item(old_item, new_rep.Items[i]) result = self.diff_representation_item(old_item, new_rep.Items[i])
if result is True: if result is True:
return True return True
return False
def diff_representation_item(self, old_item, new_item): def diff_representation_item(
self, old_item: ifcopenshell.entity_instance, new_item: ifcopenshell.entity_instance
) -> bool:
if old_item.is_a() != new_item.is_a(): if old_item.is_a() != new_item.is_a():
return True return True
try: try:
@@ -381,8 +397,9 @@ class IfcDiff:
return True return True
if diff: if diff:
return True return True
return False
def get_representation_id(self, element): def get_representation_id(self, element: ifcopenshell.entity_instance) -> Union[int, None]:
if not element.Representation: if not element.Representation:
return return
for representation in element.Representation.Representations: for representation in element.Representation.Representations:
@@ -404,6 +421,7 @@ class DiffTerminator:
def give_up_diffing(self, level, diff_instance) -> bool: def give_up_diffing(self, level, diff_instance) -> bool:
if any(diff_instance.tree.values()): if any(diff_instance.tree.values()):
raise Exception("Terminated") raise Exception("Terminated")
return False
if __name__ == "__main__": if __name__ == "__main__":