This commit is contained in:
Andrej730
2025-03-25 12:05:55 +05:00
parent 244320dd49
commit d35a2dcf5b
22 changed files with 688 additions and 329 deletions
+2 -2
View File
@@ -76,8 +76,8 @@ def draw_attribute(
elif value_name == "filepath_value": elif value_name == "filepath_value":
attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name) attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name)
elif attribute.name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"): elif attribute.name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
propis = bpy.context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
for item in propis.durations_attributes: for item in props.durations_attributes:
if item.name == attribute.name: if item.name == attribute.name:
duration_props = item duration_props = item
layout.label(text=attribute.name) layout.label(text=attribute.name)
@@ -145,11 +145,8 @@ class CostClassificationsData(ReferencesData):
@classmethod @classmethod
def references(cls): def references(cls):
results = [] results = []
element = tool.Ifc.get().by_id( props = tool.Cost.get_cost_props()
bpy.context.scene.BIMCostProperties.cost_items[ element = tool.Ifc.get().by_id(props.cost_items[props.active_cost_item_index].ifc_definition_id)
bpy.context.scene.BIMCostProperties.active_cost_item_index
].ifc_definition_id
)
if element: if element:
for reference in ifcopenshell.util.classification.get_references(element): for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info() data = reference.get_info()
@@ -338,7 +338,8 @@ class BIM_PT_cost_classifications(Panel, ReferenceUI):
def poll(cls, context): def poll(cls, context):
if not tool.Ifc.get(): if not tool.Ifc.get():
return False return False
return bool(context.scene.BIMCostProperties.cost_items) props = tool.Cost.get_cost_props()
return bool(props.cost_items)
def draw(self, context): def draw(self, context):
if not CostClassificationsData.is_loaded: if not CostClassificationsData.is_loaded:
+11 -6
View File
@@ -66,8 +66,9 @@ class CostSchedulesData:
@classmethod @classmethod
def schedules(cls): def schedules(cls):
results = [] results = []
if bpy.context.scene.BIMCostProperties.active_cost_schedule_id: props = tool.Cost.get_cost_props()
schedule = tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id) if props.active_cost_schedule_id:
schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
results.append( results.append(
{ {
"id": schedule.id(), "id": schedule.id(),
@@ -88,7 +89,8 @@ class CostSchedulesData:
@classmethod @classmethod
def is_editing_rates(cls): def is_editing_rates(cls):
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_schedule_id props = tool.Cost.get_cost_props()
ifc_id = props.active_cost_schedule_id
if not ifc_id: if not ifc_id:
return return
return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES" return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES"
@@ -256,7 +258,8 @@ class CostSchedulesData:
@classmethod @classmethod
def cost_quantities(cls): def cost_quantities(cls):
results = [] results = []
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id props = tool.Cost.get_cost_props()
ifc_id = props.active_cost_item_id
if not ifc_id: if not ifc_id:
return results return results
for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []: for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []:
@@ -265,7 +268,8 @@ class CostSchedulesData:
@classmethod @classmethod
def cost_values(cls): def cost_values(cls):
ifc_id = bpy.context.scene.BIMCostProperties.active_cost_item_id props = tool.Cost.get_cost_props()
ifc_id = props.active_cost_item_id
if not ifc_id: if not ifc_id:
return [] return []
return ifcopenshell.util.cost.get_cost_values(tool.Ifc.get().by_id(ifc_id)) return ifcopenshell.util.cost.get_cost_values(tool.Ifc.get().by_id(ifc_id))
@@ -324,7 +328,8 @@ class CostItemQuantitiesData:
@classmethod @classmethod
def process_quantity_names(cls): def process_quantity_names(cls):
active_task_index = bpy.context.scene.BIMWorkScheduleProperties.active_task_index props = tool.Sequence.get_work_schedule_props()
active_task_index = props.active_task_index
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
total_tasks = len(tprops.tasks) total_tasks = len(tprops.tasks)
if not total_tasks or active_task_index >= total_tasks: if not total_tasks or active_task_index >= total_tasks:
@@ -36,16 +36,18 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
object_type: bpy.props.StringProperty() object_type: bpy.props.StringProperty()
def _execute(self, context): def _execute(self, context):
predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types props = tool.Cost.get_cost_props()
predefined_type = props.cost_schedule_predefined_types
if predefined_type == "USERDEFINED": if predefined_type == "USERDEFINED":
predefined_type = self.object_type predefined_type = self.object_type
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type) core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
props = tool.Cost.get_cost_props()
layout.prop(self, "name", text="Name") layout.prop(self, "name", text="Name")
layout.prop(context.scene.BIMCostProperties, "cost_schedule_predefined_types", text="Type") layout.prop(props, "cost_schedule_predefined_types", text="Type")
if context.scene.BIMCostProperties.cost_schedule_predefined_types == "USERDEFINED": if props.cost_schedule_predefined_types == "USERDEFINED":
layout.prop(self, "object_type", text="Object type") layout.prop(self, "object_type", text="Object type")
def invoke(self, context, event): def invoke(self, context, event):
@@ -58,10 +60,11 @@ class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Cost.get_cost_props()
core.edit_cost_schedule( core.edit_cost_schedule(
tool.Ifc, tool.Ifc,
tool.Cost, tool.Cost,
cost_schedule=tool.Ifc.get().by_id(context.scene.BIMCostProperties.active_cost_schedule_id), cost_schedule=tool.Ifc.get().by_id(props.active_cost_schedule_id),
) )
@@ -559,7 +562,8 @@ class AddCostColumn(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.scene.BIMCostProperties.cost_column: props = tool.Cost.get_cost_props()
if not props.cost_column:
cls.poll_message_set("Cost column name is empty") cls.poll_message_set("Cost column name is empty")
return False return False
return True return True
+81 -4
View File
@@ -33,6 +33,7 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
from typing import TYPE_CHECKING, Literal
def get_schedule_of_rates(self, context): def get_schedule_of_rates(self, context):
@@ -78,8 +79,8 @@ def update_active_cost_item_index(self, context):
CostClassificationsData.load() CostClassificationsData.load()
def update_cost_item_identification(self, context): def update_cost_item_identification(self: "CostItem", context: bpy.types.Context):
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.identification == "XXX": if not props.is_cost_update_enabled or self.identification == "XXX":
return return
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
@@ -93,8 +94,8 @@ def update_cost_item_identification(self, context):
attribute.string_value = self.identification attribute.string_value = self.identification
def update_cost_item_name(self, context): def update_cost_item_name(self: "CostItem", context: bpy.types.Context) -> None:
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.name == "Unnamed": if not props.is_cost_update_enabled or self.name == "Unnamed":
return return
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
@@ -142,6 +143,14 @@ class CostItem(PropertyGroup):
is_expanded: BoolProperty(name="Is Expanded") is_expanded: BoolProperty(name="Is Expanded")
level_index: IntProperty(name="Level Index") level_index: IntProperty(name="Level Index")
if TYPE_CHECKING:
name: str
identification: str
ifc_definition_id: int
has_children: bool
is_expanded: bool
level_index: int
class CostItemQuantity(PropertyGroup): class CostItemQuantity(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -150,11 +159,22 @@ class CostItemQuantity(PropertyGroup):
unit_symbol: StringProperty(name="Unit Symbol") unit_symbol: StringProperty(name="Unit Symbol")
total_cost_quantity: FloatProperty(name="Total Quantity") total_cost_quantity: FloatProperty(name="Total Quantity")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
total_quantity: float
unit_symbol: str
total_cost_quantity: float
class CostItemType(PropertyGroup): class CostItemType(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
def update_cost_item_parent(self, context): def update_cost_item_parent(self, context):
cost_item = tool.Cost.get_highlighted_cost_item() cost_item = tool.Cost.get_highlighted_cost_item()
@@ -176,6 +196,9 @@ def update_active_cost_item_resources(self, context):
class ScheduleColumn(PropertyGroup): class ScheduleColumn(PropertyGroup):
schedule_id: IntProperty() schedule_id: IntProperty()
if TYPE_CHECKING:
schedule_id: int
class BIMCostProperties(PropertyGroup): class BIMCostProperties(PropertyGroup):
cost_schedule_predefined_types: EnumProperty( cost_schedule_predefined_types: EnumProperty(
@@ -249,3 +272,57 @@ class BIMCostProperties(PropertyGroup):
custom_currency: StringProperty( custom_currency: StringProperty(
name="Custom Currency", default="USD", description="Custom Currency in ISO 4217 format" name="Custom Currency", default="USD", description="Custom Currency in ISO 4217 format"
) )
if TYPE_CHECKING:
cost_schedule_predefined_types: str
is_cost_update_enabled: bool
cost_schedule_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
is_editing: str
active_cost_schedule_id: int
cost_items: bpy.types.bpy_prop_collection_idprop[CostItem]
active_cost_item_id: int
cost_item_editing_type: str
active_cost_item_index: int
cost_item_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
contracted_cost_items: str
quantity_types: str
product_quantity_names: str
process_quantity_names: str
resource_quantity_names: str
active_cost_item_quantity_id: int
quantity_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
cost_types: Literal["FIXED", "SUM", "CATEGORY"]
cost_category: str
fixed_cost_value: float
active_cost_value_id: int
cost_value_editing_type: str
cost_value_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
cost_value_formula: str
cost_column: str
should_show_column_ui: bool
should_show_currency_ui: bool
columns: bpy.types.bpy_prop_collection_idprop[StrProperty]
columns_storage: bpy.types.bpy_prop_collection_idprop[ScheduleColumn]
active_column_index: int
cost_item_products: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
active_cost_item_product_index: int
cost_item_processes: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
active_cost_item_process_index: int
cost_item_resources: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
active_cost_item_resource_index: int
cost_item_type_products: bpy.types.bpy_prop_collection_idprop[CostItemType]
active_cost_item_type_product_index: int
schedule_of_rates: str
cost_item_rates: bpy.types.bpy_prop_collection_idprop[CostItem]
active_cost_item_rate_index: int
contracted_cost_item_rates: str
product_cost_items: bpy.types.bpy_prop_collection_idprop[CostItemQuantity]
active_product_cost_item_index: int
enable_reorder: bool
show_nested_elements: bool
show_nested_tasks: bool
show_nested_resources: bool
change_cost_item_parent: bool
show_cost_item_operators: bool
currency: str
custom_currency: str
+31 -19
View File
@@ -16,13 +16,17 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import bonsai.bim.helper import bonsai.bim.helper
import bonsai.bim.module.cost.prop as CostProp import bonsai.bim.module.cost.prop as CostProp
import bonsai.tool as tool import bonsai.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from bonsai.bim.module.cost.data import CostSchedulesData from bonsai.bim.module.cost.data import CostSchedulesData
from typing import Any from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
class BIM_PT_cost_schedules(Panel): class BIM_PT_cost_schedules(Panel):
@@ -43,7 +47,7 @@ class BIM_PT_cost_schedules(Panel):
if not CostSchedulesData.is_loaded: if not CostSchedulesData.is_loaded:
CostSchedulesData.load() CostSchedulesData.load()
self.props = context.scene.BIMCostProperties self.props = tool.Cost.get_cost_props()
if not self.props.active_cost_schedule_id: if not self.props.active_cost_schedule_id:
row = self.layout.row(align=True) row = self.layout.row(align=True)
if CostSchedulesData.data["total_cost_schedules"]: if CostSchedulesData.data["total_cost_schedules"]:
@@ -299,7 +303,7 @@ class BIM_PT_cost_item_types(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items) total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id: if not props.active_cost_schedule_id:
return False return False
@@ -312,7 +316,7 @@ class BIM_PT_cost_item_types(Panel):
return False return False
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMCostProperties self.props = tool.Cost.get_cost_props()
cost_item = self.props.cost_items[self.props.active_cost_item_index] cost_item = self.props.cost_items[self.props.active_cost_item_index]
grid = self.layout.grid_flow(columns=3, even_columns=True) grid = self.layout.grid_flow(columns=3, even_columns=True)
@@ -385,7 +389,7 @@ class BIM_PT_cost_item_quantities(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items) total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id: if not props.active_cost_schedule_id:
return False return False
@@ -398,7 +402,7 @@ class BIM_PT_cost_item_quantities(Panel):
return False return False
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMCostProperties self.props = tool.Cost.get_cost_props()
cost_item = self.props.cost_items[self.props.active_cost_item_index] cost_item = self.props.cost_items[self.props.active_cost_item_index]
@@ -462,7 +466,7 @@ class BIM_PT_cost_item_quantities(Panel):
row2.label(text="Tasks ({})".format(total_cost_item_processes)) row2.label(text="Tasks ({})".format(total_cost_item_processes))
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
wprops = context.scene.BIMWorkScheduleProperties wprops = tool.Sequence.get_work_schedule_props()
if tprops.tasks and wprops.active_task_index < len(tprops.tasks): if tprops.tasks and wprops.active_task_index < len(tprops.tasks):
if has_quantity_names: if has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES") op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
@@ -557,7 +561,7 @@ class BIM_PT_cost_item_rates(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items) total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id: if not props.active_cost_schedule_id:
return False return False
@@ -570,7 +574,7 @@ class BIM_PT_cost_item_rates(Panel):
return False return False
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMCostProperties self.props = tool.Cost.get_cost_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="") row.prop(self.props, "schedule_of_rates", text="")
if self.props.active_cost_item_rate_index < len(self.props.cost_item_rates): if self.props.active_cost_item_rate_index < len(self.props.cost_item_rates):
@@ -606,13 +610,14 @@ class BIM_UL_cost_items_trait:
split2.label(text="Quantity") split2.label(text="Quantity")
split2.label(text="Value") split2.label(text="Value")
for column in bpy.context.scene.BIMCostProperties.columns: props = tool.Cost.get_cost_props()
for column in props.columns:
split2.label(text=column.name) split2.label(text=column.name)
split2.label(text="Total Cost") split2.label(text="Total Cost")
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item: if item:
self.props = context.scene.BIMCostProperties self.props = tool.Cost.get_cost_props()
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id] cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
row = layout.row(align=True) row = layout.row(align=True)
@@ -728,7 +733,6 @@ class BIM_UL_cost_item_rates(BIM_UL_cost_items_trait, UIList):
class BIM_UL_cost_columns(UIList): class BIM_UL_cost_columns(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="") row.prop(item, "name", emboss=False, text="")
@@ -737,7 +741,7 @@ class BIM_UL_cost_columns(UIList):
class BIM_UL_cost_item_types(UIList): class BIM_UL_cost_item_types(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
cost_item = props.cost_items[props.active_cost_item_index] cost_item = props.cost_items[props.active_cost_item_index]
if item: if item:
@@ -749,9 +753,17 @@ class BIM_UL_cost_item_types(UIList):
class BIM_UL_cost_item_quantities(UIList): class BIM_UL_cost_item_quantities(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
props = context.scene.BIMCostProperties self,
cost_item = props.cost_items[props.active_cost_item_index] context: bpy.types.Context,
layout: bpy.types.UILayout,
data: BIMCostProperties,
item: CostItemQuantity,
icon,
active_data,
active_propname,
):
cost_item = data.cost_items[data.active_cost_item_index]
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_product", text="", icon="RESTRICT_SELECT_OFF")
@@ -788,15 +800,15 @@ class BIM_PT_Costing_Tools(Panel):
bl_parent_id = "BIM_PT_tab_cost" bl_parent_id = "BIM_PT_tab_cost"
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMCostProperties props = tool.Cost.get_cost_props()
row = self.layout.row() row = self.layout.row()
row.operator("bim.load_product_cost_items", icon="FILE_REFRESH") row.operator("bim.load_product_cost_items", icon="FILE_REFRESH")
row = self.layout.row() row = self.layout.row()
row.template_list( row.template_list(
"BIM_UL_product_cost_items", "BIM_UL_product_cost_items",
"", "",
self.props, props,
"product_cost_items", "product_cost_items",
self.props, props,
"active_product_cost_item_index", "active_product_cost_item_index",
) )
+1
View File
@@ -61,6 +61,7 @@ class CsvAttribute(PropertyGroup):
formatting: StringProperty(default="{{value}}", name="Formatting") formatting: StringProperty(default="{{value}}", name="Formatting")
if TYPE_CHECKING: if TYPE_CHECKING:
name: str
header: str header: str
sort: Literal["NONE", "ASC", "DESC"] sort: Literal["NONE", "ASC", "DESC"]
group: Literal["NONE", "GROUP", "CONCAT", "VARIES", "SUM", "AVERAGE", "MIN", "MAX"] group: Literal["NONE", "GROUP", "CONCAT", "VARIES", "SUM", "AVERAGE", "MIN", "MAX"]
@@ -281,9 +281,12 @@ class UnassignMaterial(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
if TYPE_CHECKING:
obj: str
def _execute(self, context): def _execute(self, context):
objects = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects() objects = [bpy.data.objects[self.obj]] if self.obj else tool.Blender.get_selected_objects()
core.unassign_material(tool.Ifc, tool.Material, objects=objects) core.unassign_material(tool.Ifc, tool.Material, objects=list(objects))
class AddConstituent(bpy.types.Operator, tool.Ifc.Operator): class AddConstituent(bpy.types.Operator, tool.Ifc.Operator):
+3 -2
View File
@@ -227,7 +227,7 @@ class TaskQtosData(Data):
@classmethod @classmethod
def load(cls): def load(cls):
wprops = bpy.context.scene.BIMWorkScheduleProperties wprops = tool.Sequence.get_work_schedule_props()
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id ifc_definition_id = tprops.tasks[wprops.active_task_index].ifc_definition_id
cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)} cls.data = {"qtos": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), qtos_only=True)}
@@ -311,7 +311,8 @@ class WorkSchedulePsetsData(Data):
@classmethod @classmethod
def load(cls): def load(cls):
ifc_definition_id = bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id props = tool.Sequence.get_work_schedule_props()
ifc_definition_id = props.active_work_schedule_id
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)} cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
cls.is_loaded = True cls.is_loaded = True
+3 -4
View File
@@ -494,7 +494,7 @@ class BIM_PT_task_qtos(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id: if not props.active_work_schedule_id:
return False return False
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
@@ -712,9 +712,8 @@ class BIM_PT_work_schedule_psets(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
if not context.scene.BIMWorkScheduleProperties.active_work_schedule_id: props = tool.Sequence.get_work_schedule_props()
return False return bool(props.active_work_schedule_id)
return True
def draw(self, context): def draw(self, context):
if not WorkSchedulePsetsData.is_loaded: if not WorkSchedulePsetsData.is_loaded:
@@ -307,20 +307,19 @@ class WorkScheduleData:
cls.is_loaded = True cls.is_loaded = True
@classmethod @classmethod
def can_have_baselines(cls): def can_have_baselines(cls) -> bool:
if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id: props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False return False
return ( return tool.Ifc.get().by_id(props.active_work_schedule_id).PredefinedType == "PLANNED"
tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id).PredefinedType
== "PLANNED"
)
@classmethod @classmethod
def active_work_schedule_baselines(cls): def active_work_schedule_baselines(cls) -> list[dict[str, Any]]:
results = [] results = []
if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id: props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return [] return []
for rel in tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id).Declares: for rel in tool.Ifc.get().by_id(props.active_work_schedule_id).Declares:
for work_schedule in rel.RelatedObjects: for work_schedule in rel.RelatedObjects:
if work_schedule.PredefinedType == "BASELINE": if work_schedule.PredefinedType == "BASELINE":
results.append( results.append(
@@ -232,7 +232,7 @@ class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.prop(self, "name", text="Name") layout.prop(self, "name", text="Name")
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
layout.prop(self.props, "work_schedule_predefined_types", text="Type") layout.prop(self.props, "work_schedule_predefined_types", text="Type")
if self.props.work_schedule_predefined_types == "USERDEFINED": if self.props.work_schedule_predefined_types == "USERDEFINED":
layout.prop(self.props, "object_type", text="Object type") layout.prop(self.props, "object_type", text="Object type")
@@ -247,10 +247,11 @@ class EditWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_work_schedule( core.edit_work_schedule(
tool.Ifc, tool.Ifc,
tool.Sequence, tool.Sequence,
work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id), work_schedule=tool.Ifc.get().by_id(props.active_work_schedule_id),
) )
@@ -376,11 +377,12 @@ class EditTaskTime(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_task_time( core.edit_task_time(
tool.Ifc, tool.Ifc,
tool.Sequence, tool.Sequence,
tool.Resource, tool.Resource,
task_time=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_time_id), task_time=tool.Ifc.get().by_id(props.active_task_time_id),
) )
@@ -411,9 +413,8 @@ class EditTask(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
core.edit_task( props = tool.Sequence.get_work_schedule_props()
tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_id) core.edit_task(tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(props.active_task_id))
)
class CopyTaskAttribute(bpy.types.Operator, tool.Ifc.Operator): class CopyTaskAttribute(bpy.types.Operator, tool.Ifc.Operator):
@@ -1083,10 +1084,11 @@ class EditSequenceAttributes(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_sequence_attributes( core.edit_sequence_attributes(
tool.Ifc, tool.Ifc,
tool.Sequence, tool.Sequence,
rel_sequence=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_sequence_id), rel_sequence=tool.Ifc.get().by_id(props.active_sequence_id),
) )
@@ -1138,7 +1140,8 @@ class VisualiseWorkScheduleDate(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return bool(bpy.context.scene.BIMWorkScheduleProperties.visualisation_start) props = tool.Sequence.get_work_schedule_props()
return bool(props.visualisation_start)
def execute(self, context): def execute(self, context):
core.visualise_work_schedule_date(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule)) core.visualise_work_schedule_date(tool.Sequence, work_schedule=tool.Ifc.get().by_id(self.work_schedule))
@@ -1163,10 +1166,8 @@ class VisualiseWorkScheduleDateRange(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
has_start, has_finish = ( props = tool.Sequence.get_work_schedule_props()
bpy.context.scene.BIMWorkScheduleProperties.visualisation_start, has_start, has_finish = props.visualisation_start, props.visualisation_finish
bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish,
)
return bool(has_start and has_finish) and not "-" in (has_start, has_finish) return bool(has_start and has_finish) and not "-" in (has_start, has_finish)
def execute(self, context): def execute(self, context):
@@ -1421,11 +1422,12 @@ class LoadAnimationColorScheme(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Loads the animation color scheme" bl_description = "Loads the animation color scheme"
def _execute(self, context): def _execute(self, context):
group = tool.Ifc.get().by_id(int(context.scene.BIMAnimationProperties.saved_color_schemes)) props = tool.Sequence.get_animation_props()
group = tool.Ifc.get().by_id(int(props.saved_color_schemes))
core.load_animation_color_scheme(tool.Sequence, scheme=group) core.load_animation_color_scheme(tool.Sequence, scheme=group)
def draw(self, context): def draw(self, context):
props = context.scene.BIMAnimationProperties props = tool.Sequence.get_animation_props()
row = self.layout.row() row = self.layout.row()
row.prop(props, "saved_color_schemes", text="") row.prop(props, "saved_color_schemes", text="")
+213 -50
View File
@@ -19,6 +19,7 @@
import bpy import bpy
import isodate import isodate
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.sequence
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
import ifcopenshell.util.date import ifcopenshell.util.date
import bonsai.tool as tool import bonsai.tool as tool
@@ -26,6 +27,7 @@ import bonsai.core.sequence as core
from bonsai.bim.module.sequence.data import SequenceData, AnimationColorSchemeData, refresh as refresh_sequence_data from bonsai.bim.module.sequence.data import SequenceData, AnimationColorSchemeData, refresh as refresh_sequence_data
import bonsai.bim.module.resource.data import bonsai.bim.module.resource.data
import bonsai.bim.module.pset.data import bonsai.bim.module.pset.data
from mathutils import Color
from bonsai.bim.prop import StrProperty, Attribute from bonsai.bim.prop import StrProperty, Attribute
from dateutil import parser from dateutil import parser
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
@@ -39,7 +41,7 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Literal
def getTaskColumns(self, context): def getTaskColumns(self, context):
@@ -93,48 +95,48 @@ def update_active_task_inputs(self, context):
tool.Sequence.load_task_inputs(inputs) tool.Sequence.load_task_inputs(inputs)
def updateTaskName(self, context): def updateTaskName(self: "Task", context: bpy.types.Context) -> None:
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.name == "Unnamed": if not props.is_task_update_enabled or self.name == "Unnamed":
return return
self.file = tool.Ifc.get() ifc_file = tool.Ifc.get()
ifcopenshell.api.run( ifcopenshell.api.sequence.edit_task(
"sequence.edit_task", ifc_file,
self.file, task=ifc_file.by_id(self.ifc_definition_id),
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}}, attributes={"Name": self.name},
) )
SequenceData.load() SequenceData.load()
if props.active_task_id == self.ifc_definition_id: if props.active_task_id == self.ifc_definition_id:
attribute = props.task_attributes.get("Name") attribute = props.task_attributes["Name"]
attribute.string_value = self.name attribute.string_value = self.name
def updateTaskIdentification(self, context): def updateTaskIdentification(self: "Task", context: bpy.types.Context) -> None:
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.identification == "XXX": if not props.is_task_update_enabled or self.identification == "XXX":
return return
self.file = tool.Ifc.get() ifc_file = tool.Ifc.get()
ifcopenshell.api.run( ifcopenshell.api.sequence.edit_task(
"sequence.edit_task", ifc_file,
self.file, task=ifc_file.by_id(self.ifc_definition_id),
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}}, attributes={"Identification": self.identification},
) )
SequenceData.load() SequenceData.load()
if props.active_task_id == self.ifc_definition_id: if props.active_task_id == self.ifc_definition_id:
attribute = props.task_attributes.get("Identification") attribute = props.task_attributes["Identification"]
attribute.string_value = self.identification attribute.string_value = self.identification
def updateTaskTimeStart(self, context): def updateTaskTimeStart(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "start") updateTaskTimeDateTime(self, context, "start")
def updateTaskTimeFinish(self, context): def updateTaskTimeFinish(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "finish") updateTaskTimeDateTime(self, context, "finish")
def updateTaskTimeDateTime(self, context, startfinish): def updateTaskTimeDateTime(self: "Task", context: bpy.types.Context, startfinish: Literal["start", "finish"]) -> None:
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled: if not props.is_task_update_enabled:
return return
@@ -149,7 +151,7 @@ def updateTaskTimeDateTime(self, context, startfinish):
if startfinish_value == "-": if startfinish_value == "-":
return return
self.file = tool.Ifc.get() ifc_file = tool.Ifc.get()
try: try:
startfinish_datetime = parser.isoparse(startfinish_value) startfinish_datetime = parser.isoparse(startfinish_value)
@@ -160,11 +162,11 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, "-") setattr(self, startfinish, "-")
return return
task = self.file.by_id(self.ifc_definition_id) task = ifc_file.by_id(self.ifc_definition_id)
if task.TaskTime: if task.TaskTime:
task_time = task.TaskTime task_time = task.TaskTime
else: else:
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=task) task_time = ifcopenshell.api.sequence.add_task_time(ifc_file, task=task)
SequenceData.load() SequenceData.load()
startfinish_key = "Schedule" + startfinish.capitalize() startfinish_key = "Schedule" + startfinish.capitalize()
@@ -174,17 +176,17 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, canonical_startfinish_value) setattr(self, startfinish, canonical_startfinish_value)
return return
ifcopenshell.api.run( ifcopenshell.api.sequence.edit_task_time(
"sequence.edit_task_time", ifc_file,
self.file, task_time=task_time,
**{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}}, attributes={startfinish_key: startfinish_datetime},
) )
SequenceData.load() SequenceData.load()
bpy.ops.bim.load_task_properties() bpy.ops.bim.load_task_properties()
def updateTaskDuration(self, context): def updateTaskDuration(self: "Task", context: bpy.types.Context) -> None:
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled: if not props.is_task_update_enabled:
return return
@@ -196,12 +198,17 @@ def updateTaskDuration(self, context):
self.duration = "-" self.duration = "-"
return return
task = tool.Ifc.get().by_id(self.ifc_definition_id) ifc_file = tool.Ifc.get()
task = ifc_file.by_id(self.ifc_definition_id)
if task.TaskTime: if task.TaskTime:
task_time = task.TaskTime task_time = task.TaskTime
else: else:
task_time = tool.Ifc.run("sequence.add_task_time", task=task) task_time = ifcopenshell.api.sequence.add_task_time(ifc_file, task=task)
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration}) ifcopenshell.api.sequence.edit_task_time(
ifc_file,
task_time=task_time,
attributes={"ScheduleDuration": duration},
)
core.load_task_properties(tool.Sequence) core.load_task_properties(tool.Sequence)
tool.Sequence.refresh_task_resources() tool.Sequence.refresh_task_resources()
@@ -212,15 +219,19 @@ def get_schedule_predefined_types(self, context):
return SequenceData.data["schedule_predefined_types_enum"] return SequenceData.data["schedule_predefined_types_enum"]
def update_visualisation_start(self, context): def update_visualisation_start(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
update_visualisation_start_finish(self, context, "visualisation_start") update_visualisation_start_finish(self, context, "visualisation_start")
def update_visualisation_finish(self, context): def update_visualisation_finish(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
update_visualisation_start_finish(self, context, "visualisation_finish") update_visualisation_start_finish(self, context, "visualisation_finish")
def update_visualisation_start_finish(self, context, startfinish): def update_visualisation_start_finish(
self: "BIMWorkScheduleProperties",
context: bpy.types.Context,
startfinish: Literal["visualisation_start", "visualisation_finish"],
) -> None:
def canonicalise_time(time): def canonicalise_time(time):
if not time: if not time:
return "-" return "-"
@@ -243,34 +254,34 @@ def update_visualisation_start_finish(self, context, startfinish):
def update_color_full(self, context): def update_color_full(self, context):
material = bpy.data.materials.get("color_full") material = bpy.data.materials.get("color_full")
if material: if material:
color_full = bpy.context.scene.BIMAnimationProperties.color_full props = tool.Sequence.get_animation_props()
inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs
color = inputs["Base Color"].default_value color = inputs["Base Color"].default_value
color[0] = color_full.r color[0] = props.color_full.r
color[1] = color_full.g color[1] = props.color_full.g
color[2] = color_full.b color[2] = props.color_full.b
def update_color_progress(self, context): def update_color_progress(self, context):
material = bpy.data.materials.get("color_progress") material = bpy.data.materials.get("color_progress")
if material: if material:
color_progress = bpy.context.scene.BIMAnimationProperties.color_progress props = tool.Sequence.get_animation_props()
inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs inputs = tool.Blender.get_material_node(material, "BSDF_PRINCIPLED").inputs
color = inputs["Base Color"].default_value color = inputs["Base Color"].default_value
color[0] = color_progress.r color[0] = props.color_progress.r
color[1] = color_progress.g color[1] = props.color_progress.g
color[2] = color_progress.b color[2] = props.color_progress.b
def update_sort_reversed(self, context): def update_sort_reversed(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
if context.scene.BIMWorkScheduleProperties.active_work_schedule_id: if self.active_work_schedule_id:
core.load_task_tree( core.load_task_tree(
tool.Sequence, tool.Sequence,
work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id), work_schedule=tool.Ifc.get().by_id(self.active_work_schedule_id),
) )
def update_filter_by_active_schedule(self, context): def update_filter_by_active_schedule(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
if obj := context.active_object: if obj := context.active_object:
product = tool.Ifc.get_entity(obj) product = tool.Ifc.get_entity(obj)
assert product assert product
@@ -314,8 +325,9 @@ def updateAssignedResourceUsage(self, context):
bonsai.bim.module.pset.data.refresh() bonsai.bim.module.pset.data.refresh()
def update_task_bar_list(self, context): def update_task_bar_list(self: "Task", context: bpy.types.Context) -> None:
if not context.scene.BIMWorkScheduleProperties.is_task_update_enabled: props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return return
if self.has_bar_visual: if self.has_bar_visual:
tool.Sequence.add_task_bar(self.ifc_definition_id) tool.Sequence.add_task_bar(self.ifc_definition_id)
@@ -368,17 +380,30 @@ class WorkPlan(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class TaskResource(PropertyGroup): class TaskResource(PropertyGroup):
name: StringProperty(name="Name", update=updateAssignedResourceName) name: StringProperty(name="Name", update=updateAssignedResourceName)
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
schedule_usage: FloatProperty(name="Schedule Usage", update=updateAssignedResourceUsage) schedule_usage: FloatProperty(name="Schedule Usage", update=updateAssignedResourceUsage)
if TYPE_CHECKING:
name: str
ifc_definition_id: int
schedule_usage: float
class TaskProduct(PropertyGroup): class TaskProduct(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class BIMWorkPlanProperties(PropertyGroup): class BIMWorkPlanProperties(PropertyGroup):
work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute) work_plan_attributes: CollectionProperty(name="Work Plan Attributes", type=Attribute)
@@ -388,6 +413,14 @@ class BIMWorkPlanProperties(PropertyGroup):
active_work_plan_id: IntProperty(name="Active Work Plan Id") active_work_plan_id: IntProperty(name="Active Work Plan Id")
work_schedules: EnumProperty(items=getWorkSchedules, name="Work Schedules") work_schedules: EnumProperty(items=getWorkSchedules, name="Work Schedules")
if TYPE_CHECKING:
work_plan_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
editing_type: str
work_plans: bpy.types.bpy_prop_collection_idprop[WorkPlan]
active_work_plan_index: int
active_work_plan_id: int
work_schedules: str
class ISODuration(PropertyGroup): class ISODuration(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -398,16 +431,33 @@ class ISODuration(PropertyGroup):
minutes: IntProperty(name="Minutes", default=0) minutes: IntProperty(name="Minutes", default=0)
seconds: IntProperty(name="Seconds", default=0) seconds: IntProperty(name="Seconds", default=0)
if TYPE_CHECKING:
name: str
years: int
months: int
days: int
hours: int
minutes: int
seconds: int
class IFCStatus(PropertyGroup): class IFCStatus(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
is_visible: BoolProperty(name="Is Visible", default=True, update=lambda x, y: bpy.ops.bim.activate_status_filters()) is_visible: BoolProperty(name="Is Visible", default=True, update=lambda x, y: bpy.ops.bim.activate_status_filters())
if TYPE_CHECKING:
name: str
is_visible: bool
class BIMStatusProperties(PropertyGroup): class BIMStatusProperties(PropertyGroup):
is_enabled: BoolProperty(name="Is Enabled") is_enabled: BoolProperty(name="Is Enabled")
statuses: CollectionProperty(name="Statuses", type=IFCStatus) statuses: CollectionProperty(name="Statuses", type=IFCStatus)
if TYPE_CHECKING:
is_enabled: bool
statuses: bpy.types.bpy_prop_collection_idprop[IFCStatus]
class BIMWorkScheduleProperties(PropertyGroup): class BIMWorkScheduleProperties(PropertyGroup):
work_schedule_predefined_types: EnumProperty( work_schedule_predefined_types: EnumProperty(
@@ -493,6 +543,66 @@ class BIMWorkScheduleProperties(PropertyGroup):
name="Filter By Active Schedule", default=False, update=update_filter_by_active_schedule name="Filter By Active Schedule", default=False, update=update_filter_by_active_schedule
) )
if TYPE_CHECKING:
work_schedule_predefined_types: str
object_type: str
durations_attributes: bpy.types.bpy_prop_collection_idprop[ISODuration]
work_calendars: str
work_schedule_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
editing_type: str
editing_task_type: str
active_work_schedule_index: int
active_work_schedule_id: int
active_task_index: int
active_task_id: int
highlighted_task_id: int
task_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
should_show_visualisation_ui: bool
should_show_task_bar_selection: bool
should_show_snapshot_ui: bool
should_show_column_ui: bool
columns: bpy.types.bpy_prop_collection_idprop[Attribute]
active_column_index: int
sort_column: str
is_sort_reversed: bool
column_types: str
task_columns: str
task_time_columns: str
other_columns: str
active_task_time_id: int
task_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
contracted_tasks: str
task_bars: str
is_task_update_enabled: bool
editing_sequence_type: str
active_sequence_id: int
sequence_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
lag_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
visualisation_start: str
visualisation_finish: str
speed_multiplier: float
speed_animation_duration: str
speed_animation_frames: int
speed_real_duration: str
speed_types: str
task_resources: bpy.types.bpy_prop_collection_idprop[TaskResource]
active_task_resource_index: int
task_inputs: bpy.types.bpy_prop_collection_idprop[TaskProduct]
active_task_input_index: int
task_outputs: bpy.types.bpy_prop_collection_idprop[TaskProduct]
active_task_output_index: int
show_nested_outputs: bool
show_nested_resources: bool
show_nested_inputs: bool
product_input_tasks: bpy.types.bpy_prop_collection_idprop[TaskProduct]
product_output_tasks: bpy.types.bpy_prop_collection_idprop[TaskProduct]
active_product_output_task_index: int
active_product_input_task_index: int
enable_reorder: bool
show_task_operators: bool
should_show_schedule_baseline_ui: bool
filter_by_active_schedule: bool
class BIMTaskTreeProperties(PropertyGroup): class BIMTaskTreeProperties(PropertyGroup):
# This belongs by itself for performance reasons. https://developer.blender.org/T87737 # This belongs by itself for performance reasons. https://developer.blender.org/T87737
@@ -507,11 +617,19 @@ class WorkCalendar(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class RecurrenceComponent(PropertyGroup): class RecurrenceComponent(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
is_specified: BoolProperty(name="Is Specified") is_specified: BoolProperty(name="Is Specified")
if TYPE_CHECKING:
name: str
is_specified: bool
class BIMWorkCalendarProperties(PropertyGroup): class BIMWorkCalendarProperties(PropertyGroup):
work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute) work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute)
@@ -542,6 +660,22 @@ class BIMWorkCalendarProperties(PropertyGroup):
start_time: StringProperty(name="Start Time") start_time: StringProperty(name="Start Time")
end_time: StringProperty(name="End Time") end_time: StringProperty(name="End Time")
if TYPE_CHECKING:
work_calendar_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
work_time_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
editing_type: str
active_work_calendar_id: int
active_work_time_id: int
day_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
weekday_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
month_components: bpy.types.bpy_prop_collection_idprop[RecurrenceComponent]
position: int
interval: int
occurrences: int
recurrence_types: str
start_time: str
end_time: str
def update_selected_date(self: "DatePickerProperties", context: bpy.types.Context) -> None: def update_selected_date(self: "DatePickerProperties", context: bpy.types.Context) -> None:
# `include_time` is `True`, otherwise time props are not displayed in UI. # `include_time` is `True`, otherwise time props are not displayed in UI.
@@ -561,6 +695,13 @@ class DatePickerProperties(PropertyGroup):
selected_min: IntProperty(min=0, max=59, update=update_selected_date) selected_min: IntProperty(min=0, max=59, update=update_selected_date)
selected_sec: IntProperty(min=0, max=59, update=update_selected_date) selected_sec: IntProperty(min=0, max=59, update=update_selected_date)
if TYPE_CHECKING:
display_date: str
selected_date: str
selected_hour: int
selected_min: int
selected_sec: int
class BIMDateTextProperties(PropertyGroup): class BIMDateTextProperties(PropertyGroup):
start_frame: IntProperty(name="Start Frame") start_frame: IntProperty(name="Start Frame")
@@ -568,6 +709,12 @@ class BIMDateTextProperties(PropertyGroup):
start: StringProperty(name="Start") start: StringProperty(name="Start")
finish: StringProperty(name="Finish") finish: StringProperty(name="Finish")
if TYPE_CHECKING:
start_frame: int
total_frames: int
start: str
finish: str
class BIMTaskTypeColor(PropertyGroup): class BIMTaskTypeColor(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
@@ -580,6 +727,11 @@ class BIMTaskTypeColor(PropertyGroup):
max=1.0, max=1.0,
) )
if TYPE_CHECKING:
name: str
animation_type: str
color: tuple[float, float, float]
class BIMAnimationProperties(PropertyGroup): class BIMAnimationProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Loaded", default=False) is_editing: BoolProperty(name="Is Loaded", default=False)
@@ -607,3 +759,14 @@ class BIMAnimationProperties(PropertyGroup):
update=update_color_progress, update=update_color_progress,
) )
should_show_task_bar_options: BoolProperty(name="Show Task Bar Options", default=False) should_show_task_bar_options: BoolProperty(name="Show Task Bar Options", default=False)
if TYPE_CHECKING:
is_editing: bool
saved_color_schemes: str
active_color_component_outputs_index: int
active_color_component_inputs_index: int
task_input_colors: bpy.types.bpy_prop_collection_idprop[BIMTaskTypeColor]
task_output_colors: bpy.types.bpy_prop_collection_idprop[BIMTaskTypeColor]
color_full: Color
color_progress: Color
should_show_task_bar_options: bool
+36 -14
View File
@@ -30,7 +30,11 @@ from bonsai.bim.module.sequence.data import (
TaskICOMData, TaskICOMData,
AnimationColorSchemeData, AnimationColorSchemeData,
) )
from typing import Any, Optional from typing import Any, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.prop import Attribute
from bonsai.bim.module.sequence.prop import BIMWorkScheduleProperties, BIMTaskTreeProperties, Task
class BIM_PT_status(Panel): class BIM_PT_status(Panel):
@@ -154,7 +158,7 @@ class BIM_PT_work_schedules(Panel):
SequenceData.load() SequenceData.load()
if not WorkScheduleData.is_loaded: if not WorkScheduleData.is_loaded:
WorkScheduleData.load() WorkScheduleData.load()
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props() self.tprops = tool.Sequence.get_task_tree_props()
if not self.props.active_work_schedule_id: if not self.props.active_work_schedule_id:
@@ -479,14 +483,14 @@ class BIM_PT_animation_tools(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if props.active_work_schedule_id: if props.active_work_schedule_id:
return True return True
return False return False
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
self.animation_props = context.scene.BIMAnimationProperties self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.alignment = "RIGHT" row.alignment = "RIGHT"
row.prop(self.props, "should_show_visualisation_ui", text="Animation Settings", icon="SETTINGS") row.prop(self.props, "should_show_visualisation_ui", text="Animation Settings", icon="SETTINGS")
@@ -598,7 +602,7 @@ class BIM_PT_animation_Color_Scheme(Panel):
if not AnimationColorSchemeData.is_loaded: if not AnimationColorSchemeData.is_loaded:
AnimationColorSchemeData.load() AnimationColorSchemeData.load()
self.animation_props = context.scene.BIMAnimationProperties self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.alignment = "RIGHT" row.alignment = "RIGHT"
row.operator("bim.load_default_animation_color_scheme", text="Load default", icon="SEQUENCE_COLOR_04") row.operator("bim.load_default_animation_color_scheme", text="Load default", icon="SEQUENCE_COLOR_04")
@@ -645,7 +649,7 @@ class BIM_PT_task_icom(Panel):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id: if not props.active_work_schedule_id:
return False return False
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
@@ -658,7 +662,7 @@ class BIM_PT_task_icom(Panel):
if not TaskICOMData.is_loaded: if not TaskICOMData.is_loaded:
TaskICOMData.load() TaskICOMData.load()
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props() self.tprops = tool.Sequence.get_task_tree_props()
task = self.tprops.tasks[self.props.active_task_index] task = self.tprops.tasks[self.props.active_task_index]
@@ -751,8 +755,17 @@ class BIM_PT_task_icom(Panel):
class BIM_UL_task_columns(UIList): class BIM_UL_task_columns(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
props = context.scene.BIMWorkScheduleProperties self,
context,
layout: bpy.types.UILayout,
data: "BIMWorkScheduleProperties",
item: "Attribute",
icon,
active_data,
active_propname,
):
props = tool.Sequence.get_work_schedule_props()
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="") row.prop(item, "name", emboss=False, text="")
@@ -820,7 +833,7 @@ class BIM_UL_product_output_tasks(UIList):
class BIM_UL_tasks(UIList): class BIM_UL_tasks(UIList):
@classmethod @classmethod
def draw_header(cls, layout: bpy.types.UILayout): def draw_header(cls, layout: bpy.types.UILayout):
props = bpy.context.scene.BIMWorkScheduleProperties props = tool.Sequence.get_work_schedule_props()
row = layout.row(align=True) row = layout.row(align=True)
split1 = row.split(factor=0.1) split1 = row.split(factor=0.1)
@@ -829,9 +842,18 @@ class BIM_UL_tasks(UIList):
split2.label(text="Name") split2.label(text="Name")
cls.draw_custom_columns(props, split2, header=True) cls.draw_custom_columns(props, split2, header=True)
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: "BIMTaskTreeProperties",
item: "Task",
icon,
active_data,
active_propname,
):
if item: if item:
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
task = SequenceData.data["tasks"][item.ifc_definition_id] task = SequenceData.data["tasks"][item.ifc_definition_id]
row = layout.row(align=True) row = layout.row(align=True)
@@ -1140,7 +1162,7 @@ class BIM_PT_4D_Tools(Panel):
bl_parent_id = "BIM_PT_tab_sequence" bl_parent_id = "BIM_PT_tab_sequence"
def draw(self, context): def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties self.props = tool.Sequence.get_work_schedule_props()
row = self.layout.row() row = self.layout.row()
row.operator("bim.load_product_related_tasks", text="Load Tasks", icon="FILE_REFRESH") row.operator("bim.load_product_related_tasks", text="Load Tasks", icon="FILE_REFRESH")
row.prop(self.props, "filter_by_active_schedule", text="Filter by Active Schedule") row.prop(self.props, "filter_by_active_schedule", text="Filter by Active Schedule")
+6 -5
View File
@@ -214,11 +214,11 @@ class Blender(bonsai.core.tool.Blender):
return omprops.active_material_set_item_id return omprops.active_material_set_item_id
elif obj_type == "Task": elif obj_type == "Task":
tprops = tool.Sequence.get_task_tree_props() tprops = tool.Sequence.get_task_tree_props()
return tprops.tasks[context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id wsprops = tool.Sequence.get_work_schedule_props()
return tprops.tasks[wsprops.active_task_index].ifc_definition_id
elif obj_type == "Cost": elif obj_type == "Cost":
return context.scene.BIMCostProperties.cost_items[ cost_props = tool.Cost.get_cost_props()
context.scene.BIMCostProperties.active_cost_item_index return cost_props.cost_items[cost_props.active_cost_item_index].ifc_definition_id
].ifc_definition_id
elif obj_type == "Resource": elif obj_type == "Resource":
return context.scene.BIMResourceTreeProperties.resources[ return context.scene.BIMResourceTreeProperties.resources[
context.scene.BIMResourceProperties.active_resource_index context.scene.BIMResourceProperties.active_resource_index
@@ -227,7 +227,8 @@ class Blender(bonsai.core.tool.Blender):
props = tool.Profile.get_profile_props() props = tool.Profile.get_profile_props()
return props.profiles[props.active_profile_index].ifc_definition_id return props.profiles[props.active_profile_index].ifc_definition_id
elif obj_type == "WorkSchedule": elif obj_type == "WorkSchedule":
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id wsprops = tool.Sequence.get_work_schedule_props()
return wsprops.active_work_schedule_id
elif obj_type == "Group": elif obj_type == "Group":
prop = context.scene.BIMGroupProperties prop = context.scene.BIMGroupProperties
return prop.groups[prop.active_group_index].ifc_definition_id return prop.groups[prop.active_group_index].ifc_definition_id
+108 -74
View File
@@ -1,4 +1,22 @@
import os # Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>, 2022 Yassine Oualid <yassine@sigmadimensions.com>
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import bonsai.core.tool import bonsai.core.tool
import bonsai.tool as tool import bonsai.tool as tool
@@ -10,27 +28,35 @@ import ifcopenshell.util.unit
import bonsai.bim.helper import bonsai.bim.helper
import json import json
from pathlib import Path from pathlib import Path
from typing import Optional, Any, Generator, Union, Literal from typing import Optional, Any, Generator, Union, Literal, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
class Cost(bonsai.core.tool.Cost): class Cost(bonsai.core.tool.Cost):
RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"] RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"]
@classmethod
def get_cost_props(cls) -> "BIMCostProperties":
return bpy.context.scene.BIMCostProperties
@classmethod @classmethod
def get_cost_schedule_attributes(cls) -> dict[str, Any]: def get_cost_schedule_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.cost_schedule_attributes) return bonsai.bim.helper.export_attributes(props.cost_schedule_attributes)
@classmethod @classmethod
def disable_editing_cost_schedule(cls) -> None: 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 props = cls.get_cost_props()
props.active_cost_schedule_id = 0
cls.disable_editing_cost_item() cls.disable_editing_cost_item()
@classmethod @classmethod
def load_active_schedule_columns(cls) -> None: def load_active_schedule_columns(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
active_columns = props.columns active_columns = props.columns
storage = props.columns_storage storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id() active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -53,7 +79,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def store_active_schedule_columns(cls) -> None: def store_active_schedule_columns(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
active_columns = props.columns active_columns = props.columns
storage = props.columns_storage storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id() active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -67,7 +93,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def remove_stored_schedule_columns(cls, cost_schedule: ifcopenshell.entity_instance) -> None: def remove_stored_schedule_columns(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
storage = props.columns_storage storage = props.columns_storage
active_cost_schedule_id = cost_schedule.id() active_cost_schedule_id = cost_schedule.id()
@@ -79,8 +105,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def enable_editing_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None: def enable_editing_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id() props = cls.get_cost_props()
bpy.context.scene.BIMCostProperties.is_editing = "COST_SCHEDULE_ATTRIBUTES" props.active_cost_schedule_id = cost_schedule.id()
props.is_editing = "COST_SCHEDULE_ATTRIBUTES"
@classmethod @classmethod
def load_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None: def load_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
@@ -89,13 +116,13 @@ class Cost(bonsai.core.tool.Cost):
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()
return True return True
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.cost_schedule_attributes.clear() props.cost_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(cost_schedule, props.cost_schedule_attributes, callback=special_import) bonsai.bim.helper.import_attributes2(cost_schedule, props.cost_schedule_attributes, callback=special_import)
@classmethod @classmethod
def enable_editing_cost_items(cls, cost_schedule: ifcopenshell.entity_instance) -> None: def enable_editing_cost_items(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.active_cost_schedule_id = cost_schedule.id() props.active_cost_schedule_id = cost_schedule.id()
props.is_editing = "COST_ITEMS" props.is_editing = "COST_ITEMS"
@@ -123,7 +150,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def load_cost_schedule_tree(cls) -> None: def load_cost_schedule_tree(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
props.cost_items.clear() props.cost_items.clear()
@@ -137,7 +164,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def expand_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None: def expand_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
if cost_item.id() in cls.contracted_cost_items: if cost_item.id() in cls.contracted_cost_items:
@@ -146,7 +173,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def expand_cost_items(cls) -> None: def expand_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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:
if cost_item.ifc_definition_id in cls.contracted_cost_items: if cost_item.ifc_definition_id in cls.contracted_cost_items:
@@ -155,7 +182,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def contract_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None: def contract_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
cls.contracted_cost_items.append(cost_item.id()) cls.contracted_cost_items.append(cost_item.id())
@@ -163,7 +190,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def contract_cost_items(cls) -> None: def contract_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
for cost_item in props.cost_items: for cost_item in props.cost_items:
@@ -173,7 +200,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def clean_up_cost_item_tree(cls, cost_item_id: int) -> None: def clean_up_cost_item_tree(cls, cost_item_id: int) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
if props.active_cost_item_id == cost_item_id: if props.active_cost_item_id == cost_item_id:
@@ -185,35 +212,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def enable_editing_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance): def enable_editing_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance):
bpy.context.scene.BIMCostProperties.active_cost_item_id = cost_item.id() props = cls.get_cost_props()
bpy.context.scene.BIMCostProperties.cost_item_editing_type = "ATTRIBUTES" props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "ATTRIBUTES"
@classmethod @classmethod
def load_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance) -> None: def load_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.cost_item_attributes.clear() props.cost_item_attributes.clear()
bonsai.bim.helper.import_attributes2(cost_item, props.cost_item_attributes) bonsai.bim.helper.import_attributes2(cost_item, props.cost_item_attributes)
@classmethod @classmethod
def disable_editing_cost_item(cls) -> None: def disable_editing_cost_item(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0 props = cls.get_cost_props()
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False props.active_cost_item_id = 0
props.change_cost_item_parent = False
@classmethod @classmethod
def get_cost_item_attributes(cls) -> dict[str, Any]: def get_cost_item_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.cost_item_attributes) return bonsai.bim.helper.export_attributes(props.cost_item_attributes)
@classmethod @classmethod
def get_active_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]: def get_active_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
if not props.active_cost_item_id: if not props.active_cost_item_id:
return None return None
return tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_item_id) return tool.Ifc.get().by_id(props.active_cost_item_id)
@classmethod @classmethod
def get_highlighted_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]: def get_highlighted_cost_item(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
if not props.active_cost_schedule_id: if not props.active_cost_schedule_id:
return return
if props.active_cost_item_index < len(props.cost_items): if props.active_cost_item_index < len(props.cost_items):
@@ -226,7 +255,7 @@ class Cost(bonsai.core.tool.Cost):
cost_item = cls.get_highlighted_cost_item() cost_item = cls.get_highlighted_cost_item()
if not cost_item: if not cost_item:
return return
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.cost_item_type_products.clear() props.cost_item_type_products.clear()
# TODO implement process and resource types # TODO implement process and resource types
# props.cost_item_processes.clear() # props.cost_item_processes.clear()
@@ -248,7 +277,9 @@ class Cost(bonsai.core.tool.Cost):
cls, cost_item: ifcopenshell.entity_instance, related_object_type: RELATED_OBJECT_TYPE cls, cost_item: ifcopenshell.entity_instance, related_object_type: RELATED_OBJECT_TYPE
) -> None: ) -> None:
def create_list_items( def create_list_items(
collection: bpy.types.CollectionProperty, cost_item: ifcopenshell.entity_instance, is_deep: bool collection: bpy.types.bpy_prop_collection_idprop[CostItemQuantity],
cost_item: ifcopenshell.entity_instance,
is_deep: bool,
) -> None: ) -> None:
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:
@@ -262,18 +293,18 @@ class Cost(bonsai.core.tool.Cost):
for cost_item in ifcopenshell.util.cost.get_nested_cost_items(cost_item, is_deep): for cost_item in ifcopenshell.util.cost.get_nested_cost_items(cost_item, is_deep):
create_list_items(collection, cost_item, is_deep=False) create_list_items(collection, cost_item, is_deep=False)
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
if related_object_type == "PRODUCT": if related_object_type == "PRODUCT":
props.cost_item_products.clear() props.cost_item_products.clear()
is_deep = bpy.context.scene.BIMCostProperties.show_nested_elements is_deep = props.show_nested_elements
create_list_items(props.cost_item_products, cost_item, is_deep) create_list_items(props.cost_item_products, cost_item, is_deep)
elif related_object_type == "PROCESS": elif related_object_type == "PROCESS":
props.cost_item_processes.clear() props.cost_item_processes.clear()
is_deep = bpy.context.scene.BIMCostProperties.show_nested_tasks is_deep = props.show_nested_tasks
create_list_items(props.cost_item_processes, cost_item, is_deep) create_list_items(props.cost_item_processes, cost_item, is_deep)
elif related_object_type == "RESOURCE": elif related_object_type == "RESOURCE":
props.cost_item_resources.clear() props.cost_item_resources.clear()
is_deep = bpy.context.scene.BIMCostProperties.show_nested_resources is_deep = props.show_nested_resources
create_list_items(props.cost_item_resources, cost_item, is_deep) create_list_items(props.cost_item_resources, cost_item, is_deep)
@classmethod @classmethod
@@ -321,33 +352,35 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def enable_editing_cost_item_quantities(cls, cost_item: ifcopenshell.entity_instance) -> None: def enable_editing_cost_item_quantities(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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: ifcopenshell.entity_instance) -> 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() props = cls.get_cost_props()
props.active_cost_item_quantity_id = physical_quantity.id()
@classmethod @classmethod
def load_cost_item_quantity_attributes(cls, physical_quantity: ifcopenshell.entity_instance) -> None: def load_cost_item_quantity_attributes(cls, physical_quantity: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.quantity_attributes.clear() props.quantity_attributes.clear()
bonsai.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes) bonsai.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes)
@classmethod @classmethod
def enable_editing_cost_item_values(cls, cost_item: ifcopenshell.entity_instance) -> None: def enable_editing_cost_item_values(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.active_cost_item_id = cost_item.id() props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "VALUES" props.cost_item_editing_type = "VALUES"
@classmethod @classmethod
def disable_editing_cost_item_quantity(cls) -> None: def disable_editing_cost_item_quantity(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_quantity_id = 0 props = cls.get_cost_props()
props.active_cost_item_quantity_id = 0
@classmethod @classmethod
def get_cost_item_quantity_attributes(cls) -> dict[str, Any]: def get_cost_item_quantity_attributes(cls) -> dict[str, Any]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
return bonsai.bim.helper.export_attributes(props.quantity_attributes) return bonsai.bim.helper.export_attributes(props.quantity_attributes)
@classmethod @classmethod
@@ -356,7 +389,8 @@ class Cost(bonsai.core.tool.Cost):
) -> dict[str, Any]: ) -> dict[str, Any]:
if cost_type == "FIXED": if cost_type == "FIXED":
category = None category = None
attributes = {"AppliedValue": bpy.context.scene.BIMCostProperties.fixed_cost_value} props = cls.get_cost_props()
attributes = {"AppliedValue": props.fixed_cost_value}
elif cost_type == "SUM": elif cost_type == "SUM":
category = "*" category = "*"
attributes = {"Category": category} attributes = {"Category": category}
@@ -404,7 +438,7 @@ class Cost(bonsai.core.tool.Cost):
break break
return True return True
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.cost_value_attributes.clear() props.cost_value_attributes.clear()
is_rates = cls.is_active_schedule_of_rates() is_rates = cls.is_active_schedule_of_rates()
callback = lambda name, prop, data: import_attributes( callback = lambda name, prop, data: import_attributes(
@@ -420,38 +454,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def is_active_schedule_of_rates(cls) -> bool: def is_active_schedule_of_rates(cls) -> bool:
return ( props = cls.get_cost_props()
tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id).PredefinedType return tool.Ifc.get().by_id(props.active_cost_schedule_id).PredefinedType == "SCHEDULEOFRATES"
== "SCHEDULEOFRATES"
)
@classmethod @classmethod
def enable_editing_cost_item_value(cls, cost_value: ifcopenshell.entity_instance) -> None: def enable_editing_cost_item_value(cls, cost_value: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.active_cost_value_id = cost_value.id() props.active_cost_value_id = cost_value.id()
props.cost_value_editing_type = "ATTRIBUTES" props.cost_value_editing_type = "ATTRIBUTES"
@classmethod @classmethod
def disable_editing_cost_item_value(cls) -> None: def disable_editing_cost_item_value(cls) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.active_cost_value_id = 0 props.active_cost_value_id = 0
props.cost_value_editing_type = "" props.cost_value_editing_type = ""
@classmethod @classmethod
def load_cost_item_value_formula_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None: def load_cost_item_value_formula_attributes(cls, cost_value: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.cost_value_attributes.clear() props.cost_value_attributes.clear()
bpy.context.scene.BIMCostProperties.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value) props.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value)
@classmethod @classmethod
def enable_editing_cost_item_value_formula(cls, cost_value: ifcopenshell.entity_instance) -> None: def enable_editing_cost_item_value_formula(cls, cost_value: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.active_cost_value_id = cost_value.id() props.active_cost_value_id = cost_value.id()
props.cost_value_editing_type = "FORMULA" props.cost_value_editing_type = "FORMULA"
@classmethod @classmethod
def get_cost_item_value_formula(cls) -> str: def get_cost_item_value_formula(cls) -> str:
return bpy.context.scene.BIMCostProperties.cost_value_formula props = cls.get_cost_props()
return props.cost_value_formula
@classmethod @classmethod
def get_cost_value_attributes(cls) -> dict[str, Any]: def get_cost_value_attributes(cls) -> dict[str, Any]:
@@ -468,15 +501,14 @@ class Cost(bonsai.core.tool.Cost):
if prop.name == "UnitBasisUnit": if prop.name == "UnitBasisUnit":
return True return True
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
callback = lambda attributes, prop: export_attributes(attributes, prop) callback = lambda attributes, prop: export_attributes(attributes, prop)
return bonsai.bim.helper.export_attributes(props.cost_value_attributes, callback) return bonsai.bim.helper.export_attributes(props.cost_value_attributes, callback)
@classmethod @classmethod
def get_cost_value_unit_component(cls) -> ifcopenshell.entity_instance: def get_cost_value_unit_component(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id( props = cls.get_cost_props()
int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value) return tool.Ifc.get().by_id(int(props.cost_value_attributes["UnitBasisUnit"].enum_value))
)
@classmethod @classmethod
def get_cost_item_assignments( def get_cost_item_assignments(
@@ -491,7 +523,8 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def show_nested_cost_item_elements(cls) -> bool: def show_nested_cost_item_elements(cls) -> bool:
return bpy.context.scene.BIMCostProperties.show_nested_elements props = cls.get_cost_props()
return props.show_nested_elements
@classmethod @classmethod
def get_cost_item_products( def get_cost_item_products(
@@ -543,18 +576,18 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def add_cost_column(cls, name: str) -> None: def add_cost_column(cls, name: str) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
new = props.columns.add() new = props.columns.add()
new.name = name new.name = name
@classmethod @classmethod
def remove_cost_column(cls, name: str) -> None: def remove_cost_column(cls, name: str) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.columns.remove(props.columns.find(name)) props.columns.remove(props.columns.find(name))
@classmethod @classmethod
def get_active_schedule_of_rates(cls) -> Union[ifcopenshell.entity_instance, None]: def get_active_schedule_of_rates(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
schedule_id = tool.Blender.get_enum_safe(props, "schedule_of_rates") schedule_id = tool.Blender.get_enum_safe(props, "schedule_of_rates")
if schedule_id is None: if schedule_id is None:
return return
@@ -562,7 +595,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def expand_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None: def expand_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -570,7 +603,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def contract_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None: def contract_cost_item_rate(cls, cost_item: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
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)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates) props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -605,7 +638,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def load_schedule_of_rates_tree(cls, schedule_of_rates: ifcopenshell.entity_instance) -> None: def load_schedule_of_rates_tree(cls, schedule_of_rates: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.is_cost_update_enabled = False props.is_cost_update_enabled = False
props.cost_item_rates.clear() props.cost_item_rates.clear()
props.columns.clear() props.columns.clear()
@@ -689,13 +722,15 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def is_cost_schedule_active(cls, cost_schedule: ifcopenshell.entity_instance) -> bool: 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 props = cls.get_cost_props()
return True if cost_schedule.id() == props.active_cost_schedule_id else False
@classmethod @classmethod
def get_active_cost_schedule(cls) -> Union[ifcopenshell.entity_instance, None]: def get_active_cost_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
if not bpy.context.scene.BIMCostProperties.active_cost_schedule_id: props = cls.get_cost_props()
if not props.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(props.active_cost_schedule_id)
@classmethod @classmethod
def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None: def highlight_cost_item(cls, cost_item: ifcopenshell.entity_instance) -> None:
@@ -707,13 +742,11 @@ class Cost(bonsai.core.tool.Cost):
expand_ancestors(parent_cost) expand_ancestors(parent_cost)
cls.load_cost_schedule_tree() cls.load_cost_schedule_tree()
cost_props = bpy.context.scene.BIMCostProperties cost_props = cls.get_cost_props()
if not cost_item.id() in [item.ifc_definition_id for item in cost_props.cost_items]: if not cost_item.id() in [item.ifc_definition_id for item in cost_props.cost_items]:
expand_ancestors(cost_item) expand_ancestors(cost_item)
cost_item_index = [item.ifc_definition_id for item in bpy.context.scene.BIMCostProperties.cost_items].index( cost_item_index = [item.ifc_definition_id for item in cost_props.cost_items].index(cost_item.id()) or 0
cost_item.id() cost_props.active_cost_item_index = cost_item_index
) or 0
bpy.context.scene.BIMCostProperties.active_cost_item_index = cost_item_index
@classmethod @classmethod
def get_cost_items_for_product(cls, product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_cost_items_for_product(cls, product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -732,7 +765,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def load_product_cost_items(cls, product: ifcopenshell.entity_instance) -> None: def load_product_cost_items(cls, product: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
props.is_cost_update_enabled = False props.is_cost_update_enabled = False
props.product_cost_items.clear() props.product_cost_items.clear()
cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product) cost_items = ifcopenshell.util.cost.get_cost_items_for_product(product)
@@ -766,7 +799,7 @@ class Cost(bonsai.core.tool.Cost):
def toggle_cost_item_parent_change(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> 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 = cls.get_cost_props()
if props.change_cost_item_parent: if props.change_cost_item_parent:
props.active_cost_item_id = cost_item.id() props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "PARENT" props.cost_item_editing_type = "PARENT"
@@ -781,8 +814,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def disable_editing_cost_item_parent(cls) -> None: def disable_editing_cost_item_parent(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0 props = cls.get_cost_props()
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False props.active_cost_item_id = 0
props.change_cost_item_parent = False
@classmethod @classmethod
def load_cost_item_quantities(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None: def load_cost_item_quantities(cls, cost_item: Optional[ifcopenshell.entity_instance] = None) -> None:
@@ -822,7 +856,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod @classmethod
def get_currency_attributes(cls) -> dict[str, str]: def get_currency_attributes(cls) -> dict[str, str]:
props = bpy.context.scene.BIMCostProperties props = cls.get_cost_props()
currency = props.currency currency = props.currency
if currency == "CUSTOM": if currency == "CUSTOM":
currency = props.custom_currency currency = props.custom_currency
+136 -98
View File
@@ -42,17 +42,25 @@ from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable
if TYPE_CHECKING: if TYPE_CHECKING:
import bonsai.bim.prop import bonsai.bim.prop
from bonsai.bim.module.sequence.prop import BIMTaskTreeProperties from bonsai.bim.module.sequence.prop import BIMTaskTreeProperties, BIMWorkScheduleProperties, BIMAnimationProperties
class Sequence(bonsai.core.tool.Sequence): class Sequence(bonsai.core.tool.Sequence):
RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"] RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"]
@classmethod
def get_animation_props(cls) -> BIMAnimationProperties:
return bpy.context.scene.BIMAnimationProperties
@classmethod @classmethod
def get_task_tree_props(cls) -> BIMTaskTreeProperties: def get_task_tree_props(cls) -> BIMTaskTreeProperties:
return bpy.context.scene.BIMTaskTreeProperties return bpy.context.scene.BIMTaskTreeProperties
@classmethod
def get_work_schedule_props(cls) -> BIMWorkScheduleProperties:
return bpy.context.scene.BIMWorkScheduleProperties
@classmethod @classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]: def get_work_plan_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper import bonsai.bim.module.sequence.helper as helper
@@ -119,7 +127,7 @@ class Sequence(bonsai.core.tool.Sequence):
attributes[prop.name] = helper.parse_duration(prop.string_value) attributes[prop.name] = helper.parse_duration(prop.string_value)
return True return True
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.work_schedule_attributes, callback) return bonsai.bim.helper.export_attributes(props.work_schedule_attributes, callback)
@classmethod @classmethod
@@ -129,23 +137,25 @@ class Sequence(bonsai.core.tool.Sequence):
prop.string_value = "" if prop.is_null else data[name] prop.string_value = "" if prop.is_null else data[name]
return True return True
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.work_schedule_attributes.clear() props.work_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback) bonsai.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback)
@classmethod @classmethod
def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None: def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = work_schedule.id() props = cls.get_work_schedule_props()
bpy.context.scene.BIMWorkScheduleProperties.editing_type = "WORK_SCHEDULE" props.active_work_schedule_id = work_schedule.id()
props.editing_type = "WORK_SCHEDULE"
@classmethod @classmethod
def disable_editing_work_schedule(cls) -> None: def disable_editing_work_schedule(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0 props = cls.get_work_schedule_props()
props.active_work_schedule_id = 0
@classmethod @classmethod
def enable_editing_work_schedule_tasks(cls, work_schedule: Union[ifcopenshell.entity_instance, None]) -> None: def enable_editing_work_schedule_tasks(cls, work_schedule: Union[ifcopenshell.entity_instance, None]) -> None:
if work_schedule: if work_schedule:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_work_schedule_id = work_schedule.id() props.active_work_schedule_id = work_schedule.id()
props.editing_type = "TASKS" props.editing_type = "TASKS"
@@ -153,7 +163,7 @@ class Sequence(bonsai.core.tool.Sequence):
def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None: def load_task_tree(cls, work_schedule: ifcopenshell.entity_instance) -> None:
props = cls.get_task_tree_props() props = cls.get_task_tree_props()
props.tasks.clear() props.tasks.clear()
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
cls.contracted_tasks = json.loads(props.contracted_tasks) cls.contracted_tasks = json.loads(props.contracted_tasks)
related_objects_ids = cls.get_sorted_tasks_ids(ifcopenshell.util.sequence.get_root_tasks(work_schedule)) related_objects_ids = cls.get_sorted_tasks_ids(ifcopenshell.util.sequence.get_root_tasks(work_schedule))
@@ -162,13 +172,15 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_sorted_tasks_ids(cls, tasks: list[ifcopenshell.entity_instance]) -> list[int]: def get_sorted_tasks_ids(cls, tasks: list[ifcopenshell.entity_instance]) -> list[int]:
props = cls.get_work_schedule_props()
def get_sort_key(task): def get_sort_key(task):
# Sorting only applies to actual tasks, not the WBS # Sorting only applies to actual tasks, not the WBS
# for rel in task.IsNestedBy: # for rel in task.IsNestedBy:
# for object in rel.RelatedObjects: # for object in rel.RelatedObjects:
# if object.is_a("IfcTask"): # if object.is_a("IfcTask"):
# return "0000000000" + (task.Identification or "") # return "0000000000" + (task.Identification or "")
column_type, name = bpy.context.scene.BIMWorkScheduleProperties.sort_column.split(".") column_type, name = props.sort_column.split(".")
if column_type == "IfcTask": if column_type == "IfcTask":
return task.get_info(task)[name] or "" return task.get_info(task)[name] or ""
elif column_type == "IfcTaskTime" and task.TaskTime: elif column_type == "IfcTaskTime" and task.TaskTime:
@@ -179,12 +191,12 @@ class Sequence(bonsai.core.tool.Sequence):
s = sort_keys[i] s = sort_keys[i]
return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)] return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
if bpy.context.scene.BIMWorkScheduleProperties.sort_column: if props.sort_column:
sort_keys = {task.id(): get_sort_key(task) for task in tasks} sort_keys = {task.id(): get_sort_key(task) for task in tasks}
related_object_ids = sorted(sort_keys, key=natural_sort_key) related_object_ids = sorted(sort_keys, key=natural_sort_key)
else: else:
related_object_ids = [task.id() for task in tasks] related_object_ids = [task.id() for task in tasks]
if bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed: if props.is_sort_reversed:
related_object_ids.reverse() related_object_ids.reverse()
return related_object_ids return related_object_ids
@@ -205,7 +217,7 @@ class Sequence(bonsai.core.tool.Sequence):
# TODO: task argument is never used? # TODO: task argument is never used?
@classmethod @classmethod
def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None: def load_task_properties(cls, task: Optional[ifcopenshell.entity_instance] = None) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
task_props = cls.get_task_tree_props() task_props = cls.get_task_tree_props()
tasks_with_visual_bar = cls.get_task_bar_list() tasks_with_visual_bar = cls.get_task_bar_list()
props.is_task_update_enabled = False props.is_task_update_enabled = False
@@ -270,24 +282,26 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_active_work_schedule(cls) -> Union[ifcopenshell.entity_instance, None]: def get_active_work_schedule(cls) -> Union[ifcopenshell.entity_instance, None]:
if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id: props = cls.get_work_schedule_props()
if not props.active_work_schedule_id:
return None return None
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id) return tool.Ifc.get().by_id(props.active_work_schedule_id)
@classmethod @classmethod
def expand_task(cls, task: ifcopenshell.entity_instance) -> None: def expand_task(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
contracted_tasks = json.loads(props.contracted_tasks) contracted_tasks = json.loads(props.contracted_tasks)
contracted_tasks.remove(task.id()) contracted_tasks.remove(task.id())
props.contracted_tasks = json.dumps(contracted_tasks) props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod @classmethod
def expand_all_tasks(cls) -> None: def expand_all_tasks(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps([]) props = cls.get_work_schedule_props()
props.contracted_tasks = json.dumps([])
@classmethod @classmethod
def contract_all_tasks(cls) -> None: def contract_all_tasks(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
tprops = cls.get_task_tree_props() tprops = cls.get_task_tree_props()
contracted_tasks = json.loads(props.contracted_tasks) contracted_tasks = json.loads(props.contracted_tasks)
for task_item in tprops.tasks: for task_item in tprops.tasks:
@@ -297,23 +311,24 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def contract_task(cls, task: ifcopenshell.entity_instance) -> None: def contract_task(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
contracted_tasks = json.loads(props.contracted_tasks) contracted_tasks = json.loads(props.contracted_tasks)
contracted_tasks.append(task.id()) contracted_tasks.append(task.id())
props.contracted_tasks = json.dumps(contracted_tasks) props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod @classmethod
def disable_work_schedule(cls) -> None: def disable_work_schedule(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0 props = cls.get_work_schedule_props()
props.active_work_schedule_id = 0
@classmethod @classmethod
def disable_selecting_deleted_task(cls) -> None: def disable_selecting_deleted_task(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
if props.active_task_id not in [ if props.active_task_id not in [
task.ifc_definition_id for task in cls.get_task_tree_props().tasks task.ifc_definition_id for task in cls.get_task_tree_props().tasks
]: # Task was deleted ]: # Task was deleted
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0 props.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 props.active_task_time_id = 0
@classmethod @classmethod
def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]: def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]:
@@ -323,11 +338,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_task_attribute_value(cls, attribute_name: str) -> Any: def get_task_attribute_value(cls, attribute_name: str) -> Any:
return bpy.context.scene.BIMWorkScheduleProperties.task_attributes.get(attribute_name).get_value() props = cls.get_work_schedule_props()
return props.task_attributes.get(attribute_name).get_value()
@classmethod @classmethod
def get_active_task(cls) -> ifcopenshell.entity_instance: def get_active_task(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_task_id) props = cls.get_work_schedule_props()
return tool.Ifc.get().by_id(props.active_task_id)
@classmethod @classmethod
def get_active_work_time(cls) -> ifcopenshell.entity_instance: def get_active_work_time(cls) -> ifcopenshell.entity_instance:
@@ -339,31 +356,34 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def load_task_attributes(cls, task: ifcopenshell.entity_instance) -> None: def load_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.task_attributes.clear() props.task_attributes.clear()
bonsai.bim.helper.import_attributes2(task, props.task_attributes) bonsai.bim.helper.import_attributes2(task, props.task_attributes)
@classmethod @classmethod
def enable_editing_task_attributes(cls, task: ifcopenshell.entity_instance) -> None: def enable_editing_task_attributes(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_task_id = task.id() props.active_task_id = task.id()
props.editing_task_type = "ATTRIBUTES" props.editing_task_type = "ATTRIBUTES"
@classmethod @classmethod
def get_task_attributes(cls) -> dict[str, Any]: def get_task_attributes(cls) -> dict[str, Any]:
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.task_attributes) props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.task_attributes)
@classmethod @classmethod
def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None: def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None:
import bonsai.bim.module.sequence.helper as helper import bonsai.bim.module.sequence.helper as helper
props = cls.get_work_schedule_props()
def callback( def callback(
name: str, prop: Union[bonsai.bim.prop.Attribute, None], data: dict[str, Any] name: str, prop: Union[bonsai.bim.prop.Attribute, None], data: dict[str, Any]
) -> Union[bool, None]: ) -> Union[bool, None]:
if prop and prop.data_type == "string": if prop and prop.data_type == "string":
# TODO: Check actual attribute type instead of providing attribute names. # TODO: Check actual attribute type instead of providing attribute names.
if name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"): if name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
duration_props = bpy.context.scene.BIMWorkScheduleProperties.durations_attributes.add() duration_props = props.durations_attributes.add()
duration_props.name = name duration_props.name = name
if prop.is_null: if prop.is_null:
for key in duration_props.keys(): for key in duration_props.keys():
@@ -378,28 +398,30 @@ class Sequence(bonsai.core.tool.Sequence):
prop.string_value = "" if prop.is_null else data[name].isoformat() prop.string_value = "" if prop.is_null else data[name].isoformat()
return True return True
props = bpy.context.scene.BIMWorkScheduleProperties
props.task_time_attributes.clear() props.task_time_attributes.clear()
props.durations_attributes.clear() props.durations_attributes.clear()
bonsai.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback) bonsai.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback)
@classmethod @classmethod
def enable_editing_task_time(cls, task: ifcopenshell.entity_instance) -> None: def enable_editing_task_time(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_task_id = task.id() props.active_task_id = task.id()
props.active_task_time_id = task.TaskTime.id() props.active_task_time_id = task.TaskTime.id()
props.editing_task_type = "TASKTIME" props.editing_task_type = "TASKTIME"
@classmethod @classmethod
def disable_editing_task(cls) -> None: def disable_editing_task(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0 props = cls.get_work_schedule_props()
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 props.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.editing_task_type = "" props.active_task_time_id = 0
props.editing_task_type = ""
@classmethod @classmethod
def get_task_time_attributes(cls) -> dict[str, Any]: def get_task_time_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper import bonsai.bim.module.sequence.helper as helper
props = cls.get_work_schedule_props()
def callback(attributes, prop): def callback(attributes, prop):
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime": if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if prop.is_null: if prop.is_null:
@@ -408,7 +430,6 @@ class Sequence(bonsai.core.tool.Sequence):
attributes[prop.name] = helper.parse_datetime(prop.string_value) attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True return True
elif prop.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]: elif prop.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]:
props = bpy.context.scene.BIMWorkScheduleProperties
if prop.is_null: if prop.is_null:
attributes[prop.name] = None attributes[prop.name] = None
for value in props.durations_attributes.values(): for value in props.durations_attributes.values():
@@ -424,13 +445,12 @@ class Sequence(bonsai.core.tool.Sequence):
value = 0 value = 0
return True return True
props = bpy.context.scene.BIMWorkScheduleProperties
return bonsai.bim.helper.export_attributes(props.task_time_attributes, callback) return bonsai.bim.helper.export_attributes(props.task_time_attributes, callback)
@classmethod @classmethod
def load_task_resources(cls, task: ifcopenshell.entity_instance) -> None: def load_task_resources(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
rprops = bpy.context.scene.BIMResourceProperties rprops = cls.get_resource_props()
props.task_resources.clear() props.task_resources.clear()
rprops.is_resource_update_enabled = False rprops.is_resource_update_enabled = False
for resource in cls.get_task_resources(task) or []: for resource in cls.get_task_resources(task) or []:
@@ -442,12 +462,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_task_inputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_task_inputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_inputs props = cls.get_work_schedule_props()
is_deep = props.show_nested_inputs
return ifcopenshell.util.sequence.get_task_inputs(task, is_deep) return ifcopenshell.util.sequence.get_task_inputs(task, is_deep)
@classmethod @classmethod
def get_task_outputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_task_outputs(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_outputs props = cls.get_work_schedule_props()
is_deep = props.show_nested_outputs
return ifcopenshell.util.sequence.get_task_outputs(task, is_deep) return ifcopenshell.util.sequence.get_task_outputs(task, is_deep)
@classmethod @classmethod
@@ -468,12 +490,13 @@ class Sequence(bonsai.core.tool.Sequence):
) -> Union[list[ifcopenshell.entity_instance], None]: ) -> Union[list[ifcopenshell.entity_instance], None]:
if not task: if not task:
return return
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_resources props = cls.get_work_schedule_props()
is_deep = props.show_nested_resources
return ifcopenshell.util.sequence.get_task_resources(task, is_deep) return ifcopenshell.util.sequence.get_task_resources(task, is_deep)
@classmethod @classmethod
def load_task_inputs(cls, inputs: list[ifcopenshell.entity_instance]) -> None: def load_task_inputs(cls, inputs: list[ifcopenshell.entity_instance]) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.task_inputs.clear() props.task_inputs.clear()
for input in inputs: for input in inputs:
new = props.task_inputs.add() new = props.task_inputs.add()
@@ -482,7 +505,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def load_task_outputs(cls, outputs: list[ifcopenshell.entity_instance]) -> None: def load_task_outputs(cls, outputs: list[ifcopenshell.entity_instance]) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.task_outputs.clear() props.task_outputs.clear()
if outputs: if outputs:
for output in outputs: for output in outputs:
@@ -493,10 +516,9 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]: def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]:
tasks = cls.get_task_tree_props().tasks tasks = cls.get_task_tree_props().tasks
if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index: props = cls.get_work_schedule_props()
return tool.Ifc.get().by_id( if len(tasks) and len(tasks) > props.active_task_index:
tasks[bpy.context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id return tool.Ifc.get().by_id(tasks[props.active_task_index].ifc_definition_id)
)
@classmethod @classmethod
def get_direct_nested_tasks(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]: def get_direct_nested_tasks(cls, task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
@@ -648,37 +670,40 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None: def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_task_id = task.id() props.active_task_id = task.id()
props.editing_task_type = "CALENDAR" props.editing_task_type = "CALENDAR"
@classmethod @classmethod
def enable_editing_task_sequence(cls) -> None: def enable_editing_task_sequence(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.editing_task_type = "SEQUENCE" props.editing_task_type = "SEQUENCE"
@classmethod @classmethod
def disable_editing_task_time(cls) -> None: def disable_editing_task_time(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0 props = cls.get_work_schedule_props()
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 props.active_task_id = 0
props.active_task_time_id = 0
@classmethod @classmethod
def load_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None: def load_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.sequence_attributes.clear() props.sequence_attributes.clear()
bonsai.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes) bonsai.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes)
@classmethod @classmethod
def enable_editing_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None: def enable_editing_rel_sequence_attributes(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_sequence_id = rel_sequence.id() props.active_sequence_id = rel_sequence.id()
props.editing_sequence_type = "ATTRIBUTES" props.editing_sequence_type = "ATTRIBUTES"
@classmethod @classmethod
def load_lag_time_attributes(cls, lag_time: ifcopenshell.entity_instance) -> None: def load_lag_time_attributes(cls, lag_time: ifcopenshell.entity_instance) -> None:
props = cls.get_work_schedule_props()
def callback(name, prop, data): def callback(name, prop, data):
if name == "LagValue": if name == "LagValue":
prop = bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes.add() prop = props.lag_time_attributes.add()
prop.name = name prop.name = name
prop.is_null = data[name] is None prop.is_null = data[name] is None
prop.is_optional = False prop.is_optional = False
@@ -688,27 +713,29 @@ class Sequence(bonsai.core.tool.Sequence):
) )
return True return True
props = bpy.context.scene.BIMWorkScheduleProperties
props.lag_time_attributes.clear() props.lag_time_attributes.clear()
bonsai.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback) bonsai.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback)
@classmethod @classmethod
def enable_editing_sequence_lag_time(cls, rel_sequence: ifcopenshell.entity_instance) -> None: def enable_editing_sequence_lag_time(cls, rel_sequence: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.active_sequence_id = rel_sequence.id() props.active_sequence_id = rel_sequence.id()
props.editing_sequence_type = "LAG_TIME" props.editing_sequence_type = "LAG_TIME"
@classmethod @classmethod
def get_rel_sequence_attributes(cls) -> dict[str, Any]: def get_rel_sequence_attributes(cls) -> dict[str, Any]:
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.sequence_attributes) props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.sequence_attributes)
@classmethod @classmethod
def disable_editing_rel_sequence(cls) -> None: def disable_editing_rel_sequence(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_sequence_id = 0 props = cls.get_work_schedule_props()
props.active_sequence_id = 0
@classmethod @classmethod
def get_lag_time_attributes(cls) -> dict[str, Any]: def get_lag_time_attributes(cls) -> dict[str, Any]:
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes) props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.lag_time_attributes)
@classmethod @classmethod
def select_products(cls, products: Iterable[ifcopenshell.entity_instance]) -> None: def select_products(cls, products: Iterable[ifcopenshell.entity_instance]) -> None:
@@ -719,14 +746,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def add_task_column(cls, column_type: str, name: str, data_type: str) -> None: def add_task_column(cls, column_type: str, name: str, data_type: str) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
new = props.columns.add() new = props.columns.add()
new.name = f"{column_type}.{name}" new.name = f"{column_type}.{name}"
new.data_type = data_type new.data_type = data_type
@classmethod @classmethod
def setup_default_task_columns(cls) -> None: def setup_default_task_columns(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.columns.clear() props.columns.clear()
default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"] default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]
for item in default_columns: for item in default_columns:
@@ -736,14 +763,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def remove_task_column(cls, name: str) -> None: def remove_task_column(cls, name: str) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.columns.remove(props.columns.find(name)) props.columns.remove(props.columns.find(name))
if props.sort_column == name: if props.sort_column == name:
props.sort_column = "" props.sort_column = ""
@classmethod @classmethod
def set_task_sort_column(cls, column: str) -> None: def set_task_sort_column(cls, column: str) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.sort_column = column props.sort_column = column
@classmethod @classmethod
@@ -772,12 +799,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def is_work_schedule_active(cls, work_schedule): def is_work_schedule_active(cls, work_schedule):
return ( props = cls.get_work_schedule_props()
True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False return True if work_schedule.id() == props.active_work_schedule_id else False
)
@classmethod @classmethod
def go_to_task(cls, task): def go_to_task(cls, task):
props = cls.get_work_schedule_props()
def get_ancestor_ids(task): def get_ancestor_ids(task):
ids = [] ids = []
for rel in task.Nests or []: for rel in task.Nests or []:
@@ -785,11 +813,11 @@ class Sequence(bonsai.core.tool.Sequence):
ids.extend(get_ancestor_ids(rel.RelatingObject)) ids.extend(get_ancestor_ids(rel.RelatingObject))
return ids return ids
contracted_tasks = json.loads(bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks) contracted_tasks = json.loads(props.contracted_tasks)
for ancestor_id in get_ancestor_ids(task): for ancestor_id in get_ancestor_ids(task):
if ancestor_id in contracted_tasks: if ancestor_id in contracted_tasks:
contracted_tasks.remove(ancestor_id) contracted_tasks.remove(ancestor_id)
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps(contracted_tasks) props.contracted_tasks = json.dumps(contracted_tasks)
work_schedule = cls.get_active_work_schedule() work_schedule = cls.get_active_work_schedule()
cls.load_task_tree(work_schedule) cls.load_task_tree(work_schedule)
@@ -797,7 +825,7 @@ class Sequence(bonsai.core.tool.Sequence):
task_props = cls.get_task_tree_props() task_props = cls.get_task_tree_props()
expanded_tasks = [item.ifc_definition_id for item in task_props.tasks] expanded_tasks = [item.ifc_definition_id for item in task_props.tasks]
bpy.context.scene.BIMWorkScheduleProperties.active_task_index = expanded_tasks.index(task.id()) or 0 props.active_task_index = expanded_tasks.index(task.id()) or 0
# TODO: proper typing # TODO: proper typing
@classmethod @classmethod
@@ -808,7 +836,7 @@ class Sequence(bonsai.core.tool.Sequence):
def update_visualisation_date(cls, start_date, finish_date): def update_visualisation_date(cls, start_date, finish_date):
if not (start_date and finish_date): if not (start_date and finish_date):
return return
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.visualisation_start = ifcopenshell.util.date.canonicalise_time(start_date) props.visualisation_start = ifcopenshell.util.date.canonicalise_time(start_date)
props.visualisation_finish = ifcopenshell.util.date.canonicalise_time(finish_date) props.visualisation_finish = ifcopenshell.util.date.canonicalise_time(finish_date)
@@ -849,7 +877,7 @@ class Sequence(bonsai.core.tool.Sequence):
} }
def create_task_bar_data(tasks, vertical_increment, collection): def create_task_bar_data(tasks, vertical_increment, collection):
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
settings = { settings = {
"viz_start": ( "viz_start": (
parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True) parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
@@ -874,7 +902,8 @@ class Sequence(bonsai.core.tool.Sequence):
position_shift = task_data["start_frame"] * size_to_duration_ratio position_shift = task_data["start_frame"] * size_to_duration_ratio
bar_size = (task_data["finish_frame"] - task_data["start_frame"]) * size_to_duration_ratio bar_size = (task_data["finish_frame"] - task_data["start_frame"]) * size_to_duration_ratio
color_progress = bpy.context.scene.BIMAnimationProperties.color_progress anim_props = cls.get_animation_props()
color_progress = anim_props.color_progress
bar = add_bar( bar = add_bar(
material=material_progress, material=material_progress,
vertical_increment=vertical_increment, vertical_increment=vertical_increment,
@@ -887,7 +916,7 @@ class Sequence(bonsai.core.tool.Sequence):
name=task_data["name"] + "/Progress Bar", name=task_data["name"] + "/Progress Bar",
) )
color_full = bpy.context.scene.BIMAnimationProperties.color_full color_full = anim_props.color_full
bar2 = add_bar( bar2 = add_bar(
material=material_full, material=material_full,
vertical_increment=vertical_increment, vertical_increment=vertical_increment,
@@ -1082,7 +1111,7 @@ class Sequence(bonsai.core.tool.Sequence):
"Color": (0.2, 0.2, 0.2), "Color": (0.2, 0.2, 0.2),
}, },
} }
props = bpy.context.scene.BIMAnimationProperties props = cls.get_animation_props()
props.task_output_colors.clear() props.task_output_colors.clear()
props.task_input_colors.clear() props.task_input_colors.clear()
for group, data in groups.items(): for group, data in groups.items():
@@ -1102,14 +1131,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_start_date(cls) -> Union[datetime, None]: def get_start_date(cls) -> Union[datetime, None]:
start = parser.parse(bpy.context.scene.BIMWorkScheduleProperties.visualisation_start, dayfirst=True, fuzzy=True) props = cls.get_work_schedule_props()
start = parser.parse(props.visualisation_start, dayfirst=True, fuzzy=True)
return start or None return start or None
@classmethod @classmethod
def get_finish_date(cls) -> Union[datetime, None]: def get_finish_date(cls) -> Union[datetime, None]:
finish = parser.parse( props = cls.get_work_schedule_props()
bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish, dayfirst=True, fuzzy=True finish = parser.parse(props.visualisation_finish, dayfirst=True, fuzzy=True)
)
return finish or None return finish or None
@classmethod @classmethod
@@ -1241,7 +1270,7 @@ class Sequence(bonsai.core.tool.Sequence):
def calculate_using_frames(start, finish, animation_frames, real_duration): def calculate_using_frames(start, finish, animation_frames, real_duration):
return ((finish - start) / real_duration) * animation_frames return ((finish - start) / real_duration) * animation_frames
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
if not (props.visualisation_start and props.visualisation_finish): if not (props.visualisation_start and props.visualisation_finish):
return return
@@ -1346,7 +1375,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def animate_input(cls, obj, start_frame, product_frame, animation_type): def animate_input(cls, obj, start_frame, product_frame, animation_type):
props = bpy.context.scene.BIMAnimationProperties props = cls.get_animation_props()
color = props.task_input_colors[product_frame["type"]].color color = props.task_input_colors[product_frame["type"]].color
if product_frame["type"] in ["LOGISTIC", "MOVE", "DISPOSAL"]: if product_frame["type"] in ["LOGISTIC", "MOVE", "DISPOSAL"]:
cls.animate_destruction(obj, start_frame, product_frame, color, animation_type) cls.animate_destruction(obj, start_frame, product_frame, color, animation_type)
@@ -1355,7 +1384,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def animate_output(cls, obj, start_frame, product_frame, animation_type): def animate_output(cls, obj, start_frame, product_frame, animation_type):
props = bpy.context.scene.BIMAnimationProperties props = cls.get_animation_props()
color = props.task_output_colors[product_frame["type"]].color color = props.task_output_colors[product_frame["type"]].color
if product_frame["type"] in ["CONSTRUCTION", "INSTALLATION", "NOTDEFINED"]: if product_frame["type"] in ["CONSTRUCTION", "INSTALLATION", "NOTDEFINED"]:
cls.animate_creation(obj, start_frame, product_frame, color) cls.animate_creation(obj, start_frame, product_frame, color)
@@ -1466,8 +1495,9 @@ class Sequence(bonsai.core.tool.Sequence):
obj.data.BIMDateTextProperties.start_frame = settings["start_frame"] obj.data.BIMDateTextProperties.start_frame = settings["start_frame"]
obj.data.BIMDateTextProperties.total_frames = int(settings["total_frames"]) obj.data.BIMDateTextProperties.total_frames = int(settings["total_frames"])
obj.data.BIMDateTextProperties.start = bpy.context.scene.BIMWorkScheduleProperties.visualisation_start props = cls.get_work_schedule_props()
obj.data.BIMDateTextProperties.finish = bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish obj.data.BIMDateTextProperties.start = props.visualisation_start
obj.data.BIMDateTextProperties.finish = props.visualisation_finish
append_handler(animate_text_handler) append_handler(animate_text_handler)
@classmethod @classmethod
@@ -1573,7 +1603,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def is_filter_by_active_schedule(cls) -> bool: def is_filter_by_active_schedule(cls) -> bool:
return bpy.context.scene.BIMWorkScheduleProperties.filter_by_active_schedule props = cls.get_work_schedule_props()
return props.filter_by_active_schedule
@classmethod @classmethod
def get_tasks_for_product( def get_tasks_for_product(
@@ -1585,7 +1616,7 @@ class Sequence(bonsai.core.tool.Sequence):
def load_product_related_tasks( def load_product_related_tasks(
cls, task_inputs: list[ifcopenshell.entity_instance], task_ouputs: list[ifcopenshell.entity_instance] cls, task_inputs: list[ifcopenshell.entity_instance], task_ouputs: list[ifcopenshell.entity_instance]
) -> None: ) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties props = cls.get_work_schedule_props()
props.product_input_tasks.clear() props.product_input_tasks.clear()
props.product_output_tasks.clear() props.product_output_tasks.clear()
for task in task_inputs or []: for task in task_inputs or []:
@@ -1617,18 +1648,21 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def is_sorting_enabled(cls): def is_sorting_enabled(cls):
return bpy.context.scene.BIMWorkScheduleProperties.sort_column props = cls.get_work_schedule_props()
return props.sort_column
@classmethod @classmethod
def is_sort_reversed(cls): def is_sort_reversed(cls):
return bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed props = cls.get_work_schedule_props()
return props.is_sort_reversed
@classmethod @classmethod
def get_user_predefined_type(cls): def get_user_predefined_type(cls):
predefined_type = bpy.context.scene.BIMWorkScheduleProperties.work_schedule_predefined_types props = cls.get_work_schedule_props()
predefined_type = props.work_schedule_predefined_types
object_type = None object_type = None
if predefined_type == "USERDEFINED": if predefined_type == "USERDEFINED":
object_type = bpy.context.scene.BIMWorkScheduleProperties.object_type object_type = props.object_type
return predefined_type, object_type return predefined_type, object_type
@classmethod @classmethod
@@ -1649,7 +1683,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def save_animation_color_scheme(cls, name: str) -> ifcopenshell.entity_instance: def save_animation_color_scheme(cls, name: str) -> ifcopenshell.entity_instance:
props = bpy.context.scene.BIMAnimationProperties props = cls.get_animation_props()
colour_scheme = { colour_scheme = {
"Inputs": {cs.name: cs.color[0:3] for cs in props.task_input_colors}, "Inputs": {cs.name: cs.color[0:3] for cs in props.task_input_colors},
"Outputs": {cs.name: cs.color[0:3] for cs in props.task_output_colors}, "Outputs": {cs.name: cs.color[0:3] for cs in props.task_output_colors},
@@ -1674,7 +1708,7 @@ class Sequence(bonsai.core.tool.Sequence):
if data.get("type") == "BBIM_AnimationColorScheme": if data.get("type") == "BBIM_AnimationColorScheme":
inputs_color_scheme = data.get("colourscheme").get("Inputs") inputs_color_scheme = data.get("colourscheme").get("Inputs")
outputs_color_scheme = data.get("colourscheme").get("Outputs") outputs_color_scheme = data.get("colourscheme").get("Outputs")
props = bpy.context.scene.BIMAnimationProperties props = cls.get_animation_props()
props.task_input_colors.clear() props.task_input_colors.clear()
props.task_output_colors.clear() props.task_output_colors.clear()
for value, colour in inputs_color_scheme.items(): for value, colour in inputs_color_scheme.items():
@@ -1711,26 +1745,30 @@ class Sequence(bonsai.core.tool.Sequence):
return False return False
@classmethod @classmethod
def get_task_bar_list(cls): def get_task_bar_list(cls) -> list[int]:
return json.loads(bpy.context.scene.BIMWorkScheduleProperties.task_bars) props = cls.get_work_schedule_props()
return json.loads(props.task_bars)
@classmethod @classmethod
def add_task_bar(cls, task_id): def add_task_bar(cls, task_id: int) -> None:
task_bars = cls.get_task_bar_list() task_bars = cls.get_task_bar_list()
task_bars.append(task_id) task_bars.append(task_id)
bpy.context.scene.BIMWorkScheduleProperties.task_bars = json.dumps(task_bars) props = cls.get_work_schedule_props()
props.task_bars = json.dumps(task_bars)
@classmethod @classmethod
def remove_task_bar(cls, task_id): def remove_task_bar(cls, task_id: int) -> None:
task_bars = cls.get_task_bar_list() task_bars = cls.get_task_bar_list()
if task_id in task_bars: if task_id in task_bars:
task_bars.remove(task_id) task_bars.remove(task_id)
bpy.context.scene.BIMWorkScheduleProperties.task_bars = json.dumps(task_bars) props = cls.get_work_schedule_props()
props.task_bars = json.dumps(task_bars)
@classmethod @classmethod
def get_animation_color_scheme(cls): def get_animation_color_scheme(cls):
if len(bpy.context.scene.BIMAnimationProperties.saved_color_schemes) > 0: props = cls.get_animation_props()
return tool.Ifc.get().by_id(int(bpy.context.scene.BIMAnimationProperties.saved_color_schemes)) if len(props.saved_color_schemes) > 0:
return tool.Ifc.get().by_id(int(props.saved_color_schemes))
@classmethod @classmethod
def parse_isodate_datetime(cls, datetime_str: str, include_time: bool) -> datetime: def parse_isodate_datetime(cls, datetime_str: str, include_time: bool) -> datetime:
+9 -1
View File
@@ -456,7 +456,15 @@ class IfcCsv:
self.import_pd(ifc_file, df, attributes, null, empty, bool_true, bool_false) self.import_pd(ifc_file, df, attributes, null, empty, bool_true, bool_false)
def import_pd( def import_pd(
self, ifc_file, df, attributes=None, null="-", empty="", bool_true="YES", bool_false="NO", concat=", " self,
ifc_file: ifcopenshell.file,
df: "pd.DataFrame",
attributes: Optional[list[Union[str, None]]] = None,
null: str = "-",
empty: str = "",
bool_true: str = "YES",
bool_false: str = "NO",
concat: str = ", ",
) -> None: ) -> None:
headers = df.columns.tolist() headers = df.columns.tolist()
@@ -65,28 +65,22 @@ def assign_material(
:param products: The list of IfcProducts to assign the material or material set :param products: The list of IfcProducts to assign the material or material set
to. to.
:type products: list[ifcopenshell.entity_instance]
:param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet", :param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
"IfcMaterialLayerSet", "IfcMaterialLayerSetUsage", "IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
"IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or "IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
"IfcMaterialList". Note that "Set Usages" may only be assigned to "IfcMaterialList". Note that "Set Usages" may only be assigned to
occurrences, not types. Defaults to "IfcMaterial". occurrences, not types. Defaults to "IfcMaterial".
:type type: str
:param material: The IfcMaterial or material set you are assigning here. :param material: The IfcMaterial or material set you are assigning here.
If type is Usage then no need to provide `material`, it will be deduced If type is Usage then no need to provide `material`, it will be deduced
from the element type automatically. from the element type automatically.
If IfcMaterial is provided as material and type is not IfcMaterial, If IfcMaterial is provided as material and type is not IfcMaterial,
provided material will be ignored except for IfcMaterialList provided material will be ignored except for IfcMaterialList
where it will be used as part of the list. where it will be used as part of the list.
:type material: ifcopenshell.entity_instance, optional
:return: IfcRelAssociatesMaterial entity :return: IfcRelAssociatesMaterial entity
or a list of IfcRelAssociatesMaterial entities or a list of IfcRelAssociatesMaterial entities
(possible if `type` is Usage (possible if `type` is Usage
and `products` require different Usages) and `products` require different Usages)
or `None` if `products` was empty list. or `None` if `products` was empty list.
:rtype: Union[
ifcopenshell.entity_instance,
list[ifcopenshell.entity_instance], None]
Example: Example:
@@ -62,6 +62,8 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
set_items = material.MaterialConstituents or [] set_items = material.MaterialConstituents or []
elif material.is_a("IfcMaterialList"): elif material.is_a("IfcMaterialList"):
set_items = [] set_items = []
else:
raise ValueError(f"Unknown material set type: {material.is_a()}")
for set_item in set_items: for set_item in set_items:
file.remove(set_item) file.remove(set_item)
file.remove(material) file.remove(material)
@@ -32,9 +32,7 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
If the product does not have a material, nothing happens. If the product does not have a material, nothing happens.
:param products: The list IfcProducts that may or may not have a material :param products: The list IfcProducts that may or may not have a material
:type product: list[ifcopenshell.entity_instance]
:return: None :return: None
:rtype: None
Example: Example:
@@ -54,18 +52,16 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
""" """
usecase = Usecase() usecase = Usecase()
usecase.file = file usecase.file = file
usecase.settings = {"products": products} return usecase.execute(products)
return usecase.execute()
class Usecase: class Usecase:
file: ifcopenshell.file file: ifcopenshell.file
settings: dict[str, Any]
def execute(self): def execute(self, products: list[ifcopenshell.entity_instance]) -> None:
self.products = set(self.settings["products"]) if not products:
if not self.products:
return return
self.products = set(products)
self.remove_material_usages_from_types() self.remove_material_usages_from_types()
self.unassign_materials() self.unassign_materials()