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 json
import logging
import ifcdiff
import ifcopenshell
import blenderbim.bim.handler
import blenderbim.bim.import_ifc
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
@@ -175,7 +177,7 @@ class ExecuteIfcDiff(bpy.types.Operator):
blenderbim.bim.handler.refresh_ui_data()
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:
return
+91 -58
View File
@@ -17,25 +17,26 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Union
from typing import TYPE_CHECKING, Optional, Union, Literal
if TYPE_CHECKING:
import bpy
import ifcopenshell
import ifcopenshell.util.cost
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)
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()
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):
def disable_editing_cost_schedule(cost: tool.Cost) -> None:
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)
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.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.load_active_schedule_columns()
cost.load_cost_schedule_tree()
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)
cost.load_cost_schedule_tree()
# 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)
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):
def expand_cost_item(cost: 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):
def expand_cost_items(cost: tool.Cost) -> None:
cost.expand_cost_items()
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.load_cost_schedule_tree()
def contract_cost_items(cost: tool.Cost):
def contract_cost_items(cost: 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):
def remove_cost_item(ifc: tool.Ifc, cost: 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):
def enable_editing_cost_item_attributes(cost: 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):
def disable_editing_cost_item(cost: tool.Cost) -> None:
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()
ifc.run("cost.edit_cost_item", cost_item=cost.get_active_cost_item(), attributes=attributes)
cost.disable_editing_cost_item()
@@ -148,85 +149,104 @@ def unassign_cost_item_type(
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.load_cost_item_types(cost_item)
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)
if products:
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)
def load_cost_item_quantities(cost: tool.Cost):
def load_cost_item_quantities(cost: tool.Cost) -> None:
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.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.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.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)
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)
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)
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)
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)
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)
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)
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.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()
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()
ifc.run("cost.edit_cost_item_quantity", physical_quantity=physical_quantity, attributes=attributes)
cost.disable_editing_cost_item_quantity()
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)
ifc.run(
"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)
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.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()
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.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()
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):
def edit_cost_value(ifc: tool.Ifc, cost: 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()
# 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)
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()
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):
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)
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)
def add_cost_column(cost: tool.Cost, name):
def add_cost_column(cost: tool.Cost, name: str) -> None:
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)
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)
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)
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)
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()
return cost.export_cost_schedules(filepath, format, cost_schedule)
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)
if 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()
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()
products = ifc.get().by_type("IfcElement")
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)
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)
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)
is_cost_schedule_active = cost.is_cost_schedule_active(cost_schedule)
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"
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()
if cost_item and cost.is_root_cost_item(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()
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()
if 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()
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")
attributes = cost.get_currency_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 blenderbim.bim.helper
import json
from typing import Optional, Any, Generator, Union
from typing import Optional, Any, Generator, Union, Literal
class Cost(blenderbim.core.tool.Cost):
RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"]
@classmethod
def get_cost_schedule_attributes(cls):
def get_cost_schedule_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties
return blenderbim.bim.helper.export_attributes(props.cost_schedule_attributes)
@classmethod
def disable_editing_cost_schedule(cls):
def disable_editing_cost_schedule(cls) -> None:
cls.store_active_schedule_columns()
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = 0
cls.disable_editing_cost_item()
@@ -74,12 +77,12 @@ class Cost(blenderbim.core.tool.Cost):
storage.remove(storage_col_i)
@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.is_editing = "COST_SCHEDULE_ATTRIBUTES"
@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):
if name in ["SubmittedOn", "UpdateDate"]:
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"
@classmethod
def play_sound(cls):
def play_sound(cls) -> None:
if tool.Blender.get_addon_preferences().should_play_chaching_sound:
cls.play_chaching_sound() # lol
@classmethod
def play_chaching_sound(cls):
def play_chaching_sound(cls) -> None:
# TODO: make pitch higher as costs rise
try:
import aud
@@ -118,7 +121,7 @@ class Cost(blenderbim.core.tool.Cost):
pass # ah well
@classmethod
def load_cost_schedule_tree(cls):
def load_cost_schedule_tree(cls) -> None:
props = bpy.context.scene.BIMCostProperties
props.is_cost_update_enabled = False
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
@classmethod
def expand_cost_item(cls, cost_item):
def expand_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "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)
@classmethod
def expand_cost_items(cls):
def expand_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties
cls.contracted_cost_items = json.loads(props.contracted_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)
@classmethod
def contract_cost_item(cls, cost_item):
def contract_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "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)
@classmethod
def contract_cost_items(cls):
def contract_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties
if not hasattr(cls, "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)
@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
if not hasattr(cls, "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))
@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.cost_item_editing_type = "ATTRIBUTES"
@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.cost_item_attributes.clear()
blenderbim.bim.helper.import_attributes2(cost_item, props.cost_item_attributes)
@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.change_cost_item_parent = False
@classmethod
def get_cost_item_attributes(cls):
def get_cost_item_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties
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)
@classmethod
def get_highlighted_cost_item(cls):
def get_highlighted_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMCostProperties
if not props.active_cost_schedule_id:
return
@@ -240,7 +243,9 @@ class Cost(blenderbim.core.tool.Cost):
new.name = related_object.Name or "Unnamed"
@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):
products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False)
for product in products:
@@ -291,9 +296,9 @@ class Cost(blenderbim.core.tool.Cost):
return selected_quantitites, unit
@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":
products = tool.Spatial.get_selected_products()
products = list(tool.Spatial.get_selected_products())
elif related_object_type == "PROCESS":
products = [tool.Sequence.get_highlighted_task()]
elif related_object_type == "RESOURCE":
@@ -301,17 +306,17 @@ class Cost(blenderbim.core.tool.Cost):
return products or []
@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.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "QUANTITIES"
@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()
@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.quantity_attributes.clear()
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"
@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
@classmethod
def get_cost_item_quantity_attributes(cls):
def get_cost_item_quantity_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties
return blenderbim.bim.helper.export_attributes(props.quantity_attributes)
@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":
category = None
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"
@classmethod
def get_cost_item_value_formula(cls):
def get_cost_item_value_formula(cls) -> str:
return bpy.context.scene.BIMCostProperties.cost_value_formula
@classmethod
@@ -469,7 +476,7 @@ class Cost(blenderbim.core.tool.Cost):
)
@classmethod
def show_nested_cost_item_elements(cls):
def show_nested_cost_item_elements(cls) -> bool:
return bpy.context.scene.BIMCostProperties.show_nested_elements
@classmethod
@@ -532,7 +539,7 @@ class Cost(blenderbim.core.tool.Cost):
props.columns.remove(props.columns.find(name))
@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
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
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)))
@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
contracted_cost_item_rates = json.loads(props.contracted_cost_item_rates)
contracted_cost_item_rates.append(cost_item)
@@ -571,7 +578,7 @@ class Cost(blenderbim.core.tool.Cost):
]
@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.is_cost_update_enabled = False
props.cost_item_rates.clear()
@@ -585,7 +592,9 @@ class Cost(blenderbim.core.tool.Cost):
props.is_cost_update_enabled = True
@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 os
import sys
@@ -624,7 +633,7 @@ class Cost(blenderbim.core.tool.Cost):
return "Could not open file location"
@classmethod
def get_units(cls):
def get_units(cls) -> dict[int, str]:
units = {}
for unit in tool.Ifc.get().by_type("IfcNamedUnit"):
if unit.get_info().get("UnitType", None) in [
@@ -639,7 +648,7 @@ class Cost(blenderbim.core.tool.Cost):
return units
@staticmethod
def format_unit(unit):
def format_unit(unit: ifcopenshell.entity_instance) -> str:
if unit.is_a("IfcContextDependentUnit"):
return f"{unit.UnitType} / {unit.Name}"
else:
@@ -649,7 +658,7 @@ class Cost(blenderbim.core.tool.Cost):
return f"{unit.UnitType} / {name}"
@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 []:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
return rel.RelatingControl
@@ -657,17 +666,17 @@ class Cost(blenderbim.core.tool.Cost):
return cls.get_cost_schedule(rel.RelatingObject)
@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
@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:
return None
return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
@classmethod
def highlight_cost_item(cls, cost_item):
def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
def expand_ancestors(cost_item):
cls.expand_cost_item(cost_item)
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
@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)
@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)
if cost_schedule:
cost_items = [
@@ -714,7 +725,7 @@ class Cost(blenderbim.core.tool.Cost):
new.total_cost_quantity = ifcopenshell.util.cost.get_total_quantity(cost_item)
@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())
if unit:
return ifcopenshell.util.unit.get_unit_symbol(unit)
@@ -722,14 +733,15 @@ class Cost(blenderbim.core.tool.Cost):
return None
@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:
for rel in cost_item.HasAssignments:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl.is_a("IfcCostSchedule"):
return True
return False
@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:
return
props = bpy.context.scene.BIMCostProperties
@@ -740,16 +752,18 @@ class Cost(blenderbim.core.tool.Cost):
cls.disable_editing_cost_item_parent()
@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)
@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.change_cost_item_parent = False
@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:
cost_item = cls.get_highlighted_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")
@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 = []
if 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")
@classmethod
def has_schedules(cls):
def has_schedules(cls) -> bool:
return bool(tool.Ifc.get().by_type("IfcCostSchedule"))
@classmethod
def get_currency_attributes(cls):
def get_currency_attributes(cls) -> dict[str, str]:
props = bpy.context.scene.BIMCostProperties
currency = props.currency
if currency == "CUSTOM":
+33 -15
View File
@@ -35,30 +35,29 @@ import ifcopenshell.util.classification
import ifcopenshell.util.representation
from deepdiff import DeepDiff
from ordered_set import OrderedSet
from typing import Optional, Union, Literal, Any
__version__ = version = "0.0.0"
RELATIONSHIP_TYPE = Literal["geometry", "attributes", "type", "property", "container", "aggregate", "classification"]
class IfcDiff:
"""Main IfcDiff application
If you are using IfcDiff as a library, this is the class you should use.
:param old: IFC file object for the old model
:type old: ifcopenshell.file.file
: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
geometry is compared.
:type relationships: list[string]
geometry is compared. See RELATIONSHIP_TYPE for available relationships.
: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
that comparisons will take longer.
:type is_shallow: bool
:param filter_elements: An IFC filter query if you only want to compare a
subset of elements. For example: ``IfcWall`` to only compare walls.
:type filter_elements: string
Example::
@@ -70,7 +69,19 @@ class IfcDiff:
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.new = new
self.change_register = {}
@@ -80,7 +91,7 @@ class IfcDiff:
self.is_shallow = is_shallow
self.filter_elements = filter_elements
def diff(self):
def diff(self) -> None:
logging.disable(logging.CRITICAL)
self.precision = self.get_precision()
@@ -187,7 +198,9 @@ class IfcDiff:
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 = {}
iterator = ifcopenshell.geom.iterator(
self.get_settings(ifc), ifc, multiprocessing.cpu_count(), include=elements
@@ -215,7 +228,7 @@ class IfcDiff:
break
return shapes
def get_settings(self, ifc):
def get_settings(self, ifc: ifcopenshell.file) -> ifcopenshell.geom.settings:
settings = ifcopenshell.geom.settings()
# Are you feeling lucky?
settings.set(settings.DISABLE_BOOLEAN_RESULT, True)
@@ -246,7 +259,7 @@ class IfcDiff:
return list(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:
json.dump(
{
@@ -259,7 +272,7 @@ class IfcDiff:
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"]
if contexts:
return contexts[0].Precision or 1e-4
@@ -356,7 +369,7 @@ class IfcDiff:
# self.representation_ids[new_rep_id] = 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)
new_rep = self.new.by_id(new_rep_id)
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])
if result is 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():
return True
try:
@@ -381,8 +397,9 @@ class IfcDiff:
return True
if diff:
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:
return
for representation in element.Representation.Representations:
@@ -404,6 +421,7 @@ class DiffTerminator:
def give_up_diffing(self, level, diff_instance) -> bool:
if any(diff_instance.tree.values()):
raise Exception("Terminated")
return False
if __name__ == "__main__":