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":
attribute.filepath_value.layout_file_select(layout, filter_glob=attribute.filter_glob, text=attribute.name)
elif attribute.name in ("ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"):
propis = bpy.context.scene.BIMWorkScheduleProperties
for item in propis.durations_attributes:
props = tool.Sequence.get_work_schedule_props()
for item in props.durations_attributes:
if item.name == attribute.name:
duration_props = item
layout.label(text=attribute.name)
@@ -145,11 +145,8 @@ class CostClassificationsData(ReferencesData):
@classmethod
def references(cls):
results = []
element = tool.Ifc.get().by_id(
bpy.context.scene.BIMCostProperties.cost_items[
bpy.context.scene.BIMCostProperties.active_cost_item_index
].ifc_definition_id
)
props = tool.Cost.get_cost_props()
element = tool.Ifc.get().by_id(props.cost_items[props.active_cost_item_index].ifc_definition_id)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
@@ -338,7 +338,8 @@ class BIM_PT_cost_classifications(Panel, ReferenceUI):
def poll(cls, context):
if not tool.Ifc.get():
return False
return bool(context.scene.BIMCostProperties.cost_items)
props = tool.Cost.get_cost_props()
return bool(props.cost_items)
def draw(self, context):
if not CostClassificationsData.is_loaded:
+11 -6
View File
@@ -66,8 +66,9 @@ class CostSchedulesData:
@classmethod
def schedules(cls):
results = []
if bpy.context.scene.BIMCostProperties.active_cost_schedule_id:
schedule = tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id)
props = tool.Cost.get_cost_props()
if props.active_cost_schedule_id:
schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
results.append(
{
"id": schedule.id(),
@@ -88,7 +89,8 @@ class CostSchedulesData:
@classmethod
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:
return
return tool.Ifc.get().by_id(ifc_id).PredefinedType == "SCHEDULEOFRATES"
@@ -256,7 +258,8 @@ class CostSchedulesData:
@classmethod
def cost_quantities(cls):
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:
return results
for quantity in tool.Ifc.get().by_id(ifc_id).CostQuantities or []:
@@ -265,7 +268,8 @@ class CostSchedulesData:
@classmethod
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:
return []
return ifcopenshell.util.cost.get_cost_values(tool.Ifc.get().by_id(ifc_id))
@@ -324,7 +328,8 @@ class CostItemQuantitiesData:
@classmethod
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()
total_tasks = len(tprops.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()
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":
predefined_type = self.object_type
core.add_cost_schedule(tool.Ifc, name=self.name, predefined_type=predefined_type)
def draw(self, context):
layout = self.layout
props = tool.Cost.get_cost_props()
layout.prop(self, "name", text="Name")
layout.prop(context.scene.BIMCostProperties, "cost_schedule_predefined_types", text="Type")
if context.scene.BIMCostProperties.cost_schedule_predefined_types == "USERDEFINED":
layout.prop(props, "cost_schedule_predefined_types", text="Type")
if props.cost_schedule_predefined_types == "USERDEFINED":
layout.prop(self, "object_type", text="Object type")
def invoke(self, context, event):
@@ -58,10 +60,11 @@ class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = tool.Cost.get_cost_props()
core.edit_cost_schedule(
tool.Ifc,
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
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")
return False
return True
+81 -4
View File
@@ -33,6 +33,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING, Literal
def get_schedule_of_rates(self, context):
@@ -78,8 +79,8 @@ def update_active_cost_item_index(self, context):
CostClassificationsData.load()
def update_cost_item_identification(self, context):
props = context.scene.BIMCostProperties
def update_cost_item_identification(self: "CostItem", context: bpy.types.Context):
props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.identification == "XXX":
return
self.file = tool.Ifc.get()
@@ -93,8 +94,8 @@ def update_cost_item_identification(self, context):
attribute.string_value = self.identification
def update_cost_item_name(self, context):
props = context.scene.BIMCostProperties
def update_cost_item_name(self: "CostItem", context: bpy.types.Context) -> None:
props = tool.Cost.get_cost_props()
if not props.is_cost_update_enabled or self.name == "Unnamed":
return
self.file = tool.Ifc.get()
@@ -142,6 +143,14 @@ class CostItem(PropertyGroup):
is_expanded: BoolProperty(name="Is Expanded")
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):
name: StringProperty(name="Name")
@@ -150,11 +159,22 @@ class CostItemQuantity(PropertyGroup):
unit_symbol: StringProperty(name="Unit Symbol")
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):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
def update_cost_item_parent(self, context):
cost_item = tool.Cost.get_highlighted_cost_item()
@@ -176,6 +196,9 @@ def update_active_cost_item_resources(self, context):
class ScheduleColumn(PropertyGroup):
schedule_id: IntProperty()
if TYPE_CHECKING:
schedule_id: int
class BIMCostProperties(PropertyGroup):
cost_schedule_predefined_types: EnumProperty(
@@ -249,3 +272,57 @@ class BIMCostProperties(PropertyGroup):
custom_currency: StringProperty(
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
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.bim.helper
import bonsai.bim.module.cost.prop as CostProp
import bonsai.tool as tool
from bpy.types import Panel, UIList
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):
@@ -43,7 +47,7 @@ class BIM_PT_cost_schedules(Panel):
if not CostSchedulesData.is_loaded:
CostSchedulesData.load()
self.props = context.scene.BIMCostProperties
self.props = tool.Cost.get_cost_props()
if not self.props.active_cost_schedule_id:
row = self.layout.row(align=True)
if CostSchedulesData.data["total_cost_schedules"]:
@@ -299,7 +303,7 @@ class BIM_PT_cost_item_types(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -312,7 +316,7 @@ class BIM_PT_cost_item_types(Panel):
return False
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]
grid = self.layout.grid_flow(columns=3, even_columns=True)
@@ -385,7 +389,7 @@ class BIM_PT_cost_item_quantities(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -398,7 +402,7 @@ class BIM_PT_cost_item_quantities(Panel):
return False
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]
@@ -462,7 +466,7 @@ class BIM_PT_cost_item_quantities(Panel):
row2.label(text="Tasks ({})".format(total_cost_item_processes))
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 has_quantity_names:
op = row2.operator("bim.assign_cost_item_quantity", text="", icon="PROPERTIES")
@@ -557,7 +561,7 @@ class BIM_PT_cost_item_rates(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMCostProperties
props = tool.Cost.get_cost_props()
total_cost_items = len(props.cost_items)
if not props.active_cost_schedule_id:
return False
@@ -570,7 +574,7 @@ class BIM_PT_cost_item_rates(Panel):
return False
def draw(self, context):
self.props = context.scene.BIMCostProperties
self.props = tool.Cost.get_cost_props()
row = self.layout.row(align=True)
row.prop(self.props, "schedule_of_rates", text="")
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="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="Total Cost")
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
self.props = context.scene.BIMCostProperties
self.props = tool.Cost.get_cost_props()
cost_item = CostSchedulesData.data["cost_items"][item.ifc_definition_id]
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):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
@@ -737,7 +741,7 @@ class BIM_UL_cost_columns(UIList):
class BIM_UL_cost_item_types(UIList):
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]
if item:
@@ -749,9 +753,17 @@ class BIM_UL_cost_item_types(UIList):
class BIM_UL_cost_item_quantities(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMCostProperties
cost_item = props.cost_items[props.active_cost_item_index]
def draw_item(
self,
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:
row = layout.row(align=True)
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"
def draw(self, context):
self.props = context.scene.BIMCostProperties
props = tool.Cost.get_cost_props()
row = self.layout.row()
row.operator("bim.load_product_cost_items", icon="FILE_REFRESH")
row = self.layout.row()
row.template_list(
"BIM_UL_product_cost_items",
"",
self.props,
props,
"product_cost_items",
self.props,
props,
"active_product_cost_item_index",
)
+1
View File
@@ -61,6 +61,7 @@ class CsvAttribute(PropertyGroup):
formatting: StringProperty(default="{{value}}", name="Formatting")
if TYPE_CHECKING:
name: str
header: str
sort: Literal["NONE", "ASC", "DESC"]
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"}
obj: bpy.props.StringProperty()
if TYPE_CHECKING:
obj: str
def _execute(self, context):
objects = [bpy.data.objects.get(self.obj)] if self.obj else tool.Blender.get_selected_objects()
core.unassign_material(tool.Ifc, tool.Material, objects=objects)
objects = [bpy.data.objects[self.obj]] if self.obj else tool.Blender.get_selected_objects()
core.unassign_material(tool.Ifc, tool.Material, objects=list(objects))
class AddConstituent(bpy.types.Operator, tool.Ifc.Operator):
+3 -2
View File
@@ -227,7 +227,7 @@ class TaskQtosData(Data):
@classmethod
def load(cls):
wprops = bpy.context.scene.BIMWorkScheduleProperties
wprops = tool.Sequence.get_work_schedule_props()
tprops = tool.Sequence.get_task_tree_props()
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)}
@@ -311,7 +311,8 @@ class WorkSchedulePsetsData(Data):
@classmethod
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.is_loaded = True
+3 -4
View File
@@ -494,7 +494,7 @@ class BIM_PT_task_qtos(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties
props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False
tprops = tool.Sequence.get_task_tree_props()
@@ -712,9 +712,8 @@ class BIM_PT_work_schedule_psets(Panel):
@classmethod
def poll(cls, context):
if not context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
return False
return True
props = tool.Sequence.get_work_schedule_props()
return bool(props.active_work_schedule_id)
def draw(self, context):
if not WorkSchedulePsetsData.is_loaded:
@@ -307,20 +307,19 @@ class WorkScheduleData:
cls.is_loaded = True
@classmethod
def can_have_baselines(cls):
if not bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
def can_have_baselines(cls) -> bool:
props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False
return (
tool.Ifc.get().by_id(bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id).PredefinedType
== "PLANNED"
)
return tool.Ifc.get().by_id(props.active_work_schedule_id).PredefinedType == "PLANNED"
@classmethod
def active_work_schedule_baselines(cls):
def active_work_schedule_baselines(cls) -> list[dict[str, Any]]:
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 []
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:
if work_schedule.PredefinedType == "BASELINE":
results.append(
@@ -232,7 +232,7 @@ class AddWorkSchedule(bpy.types.Operator, tool.Ifc.Operator):
def draw(self, context):
layout = self.layout
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")
if self.props.work_schedule_predefined_types == "USERDEFINED":
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"}
def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_work_schedule(
tool.Ifc,
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"}
def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_task_time(
tool.Ifc,
tool.Sequence,
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"}
def _execute(self, context):
core.edit_task(
tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_task_id)
)
props = tool.Sequence.get_work_schedule_props()
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):
@@ -1083,10 +1084,11 @@ class EditSequenceAttributes(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = tool.Sequence.get_work_schedule_props()
core.edit_sequence_attributes(
tool.Ifc,
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
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):
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
def poll(cls, context):
has_start, has_finish = (
bpy.context.scene.BIMWorkScheduleProperties.visualisation_start,
bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish,
)
props = tool.Sequence.get_work_schedule_props()
has_start, has_finish = props.visualisation_start, props.visualisation_finish
return bool(has_start and has_finish) and not "-" in (has_start, has_finish)
def execute(self, context):
@@ -1421,11 +1422,12 @@ class LoadAnimationColorScheme(bpy.types.Operator, tool.Ifc.Operator):
bl_description = "Loads the animation color scheme"
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)
def draw(self, context):
props = context.scene.BIMAnimationProperties
props = tool.Sequence.get_animation_props()
row = self.layout.row()
row.prop(props, "saved_color_schemes", text="")
+213 -50
View File
@@ -19,6 +19,7 @@
import bpy
import isodate
import ifcopenshell.api
import ifcopenshell.api.sequence
import ifcopenshell.util.attribute
import ifcopenshell.util.date
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
import bonsai.bim.module.resource.data
import bonsai.bim.module.pset.data
from mathutils import Color
from bonsai.bim.prop import StrProperty, Attribute
from dateutil import parser
from bpy.types import PropertyGroup
@@ -39,7 +41,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal
def getTaskColumns(self, context):
@@ -93,48 +95,48 @@ def update_active_task_inputs(self, context):
tool.Sequence.load_task_inputs(inputs)
def updateTaskName(self, context):
props = context.scene.BIMWorkScheduleProperties
def updateTaskName(self: "Task", context: bpy.types.Context) -> None:
props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.name == "Unnamed":
return
self.file = tool.Ifc.get()
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}},
ifc_file = tool.Ifc.get()
ifcopenshell.api.sequence.edit_task(
ifc_file,
task=ifc_file.by_id(self.ifc_definition_id),
attributes={"Name": self.name},
)
SequenceData.load()
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
def updateTaskIdentification(self, context):
props = context.scene.BIMWorkScheduleProperties
def updateTaskIdentification(self: "Task", context: bpy.types.Context) -> None:
props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled or self.identification == "XXX":
return
self.file = tool.Ifc.get()
ifcopenshell.api.run(
"sequence.edit_task",
self.file,
**{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}},
ifc_file = tool.Ifc.get()
ifcopenshell.api.sequence.edit_task(
ifc_file,
task=ifc_file.by_id(self.ifc_definition_id),
attributes={"Identification": self.identification},
)
SequenceData.load()
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
def updateTaskTimeStart(self, context):
def updateTaskTimeStart(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "start")
def updateTaskTimeFinish(self, context):
def updateTaskTimeFinish(self: "Task", context: bpy.types.Context) -> None:
updateTaskTimeDateTime(self, context, "finish")
def updateTaskTimeDateTime(self, context, startfinish):
props = context.scene.BIMWorkScheduleProperties
def updateTaskTimeDateTime(self: "Task", context: bpy.types.Context, startfinish: Literal["start", "finish"]) -> None:
props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return
@@ -149,7 +151,7 @@ def updateTaskTimeDateTime(self, context, startfinish):
if startfinish_value == "-":
return
self.file = tool.Ifc.get()
ifc_file = tool.Ifc.get()
try:
startfinish_datetime = parser.isoparse(startfinish_value)
@@ -160,11 +162,11 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, "-")
return
task = self.file.by_id(self.ifc_definition_id)
task = ifc_file.by_id(self.ifc_definition_id)
if task.TaskTime:
task_time = task.TaskTime
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()
startfinish_key = "Schedule" + startfinish.capitalize()
@@ -174,17 +176,17 @@ def updateTaskTimeDateTime(self, context, startfinish):
setattr(self, startfinish, canonical_startfinish_value)
return
ifcopenshell.api.run(
"sequence.edit_task_time",
self.file,
**{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}},
ifcopenshell.api.sequence.edit_task_time(
ifc_file,
task_time=task_time,
attributes={startfinish_key: startfinish_datetime},
)
SequenceData.load()
bpy.ops.bim.load_task_properties()
def updateTaskDuration(self, context):
props = context.scene.BIMWorkScheduleProperties
def updateTaskDuration(self: "Task", context: bpy.types.Context) -> None:
props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return
@@ -196,12 +198,17 @@ def updateTaskDuration(self, context):
self.duration = "-"
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:
task_time = task.TaskTime
else:
task_time = tool.Ifc.run("sequence.add_task_time", task=task)
tool.Ifc.run("sequence.edit_task_time", task_time=task_time, attributes={"ScheduleDuration": duration})
task_time = ifcopenshell.api.sequence.add_task_time(ifc_file, task=task)
ifcopenshell.api.sequence.edit_task_time(
ifc_file,
task_time=task_time,
attributes={"ScheduleDuration": duration},
)
core.load_task_properties(tool.Sequence)
tool.Sequence.refresh_task_resources()
@@ -212,15 +219,19 @@ def get_schedule_predefined_types(self, context):
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")
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")
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):
if not time:
return "-"
@@ -243,34 +254,34 @@ def update_visualisation_start_finish(self, context, startfinish):
def update_color_full(self, context):
material = bpy.data.materials.get("color_full")
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
color = inputs["Base Color"].default_value
color[0] = color_full.r
color[1] = color_full.g
color[2] = color_full.b
color[0] = props.color_full.r
color[1] = props.color_full.g
color[2] = props.color_full.b
def update_color_progress(self, context):
material = bpy.data.materials.get("color_progress")
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
color = inputs["Base Color"].default_value
color[0] = color_progress.r
color[1] = color_progress.g
color[2] = color_progress.b
color[0] = props.color_progress.r
color[1] = props.color_progress.g
color[2] = props.color_progress.b
def update_sort_reversed(self, context):
if context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
def update_sort_reversed(self: "BIMWorkScheduleProperties", context: bpy.types.Context) -> None:
if self.active_work_schedule_id:
core.load_task_tree(
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:
product = tool.Ifc.get_entity(obj)
assert product
@@ -314,8 +325,9 @@ def updateAssignedResourceUsage(self, context):
bonsai.bim.module.pset.data.refresh()
def update_task_bar_list(self, context):
if not context.scene.BIMWorkScheduleProperties.is_task_update_enabled:
def update_task_bar_list(self: "Task", context: bpy.types.Context) -> None:
props = tool.Sequence.get_work_schedule_props()
if not props.is_task_update_enabled:
return
if self.has_bar_visual:
tool.Sequence.add_task_bar(self.ifc_definition_id)
@@ -368,17 +380,30 @@ class WorkPlan(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class TaskResource(PropertyGroup):
name: StringProperty(name="Name", update=updateAssignedResourceName)
ifc_definition_id: IntProperty(name="IFC Definition ID")
schedule_usage: FloatProperty(name="Schedule Usage", update=updateAssignedResourceUsage)
if TYPE_CHECKING:
name: str
ifc_definition_id: int
schedule_usage: float
class TaskProduct(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class BIMWorkPlanProperties(PropertyGroup):
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")
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):
name: StringProperty(name="Name")
@@ -398,16 +431,33 @@ class ISODuration(PropertyGroup):
minutes: IntProperty(name="Minutes", 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):
name: StringProperty(name="Name")
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):
is_enabled: BoolProperty(name="Is Enabled")
statuses: CollectionProperty(name="Statuses", type=IFCStatus)
if TYPE_CHECKING:
is_enabled: bool
statuses: bpy.types.bpy_prop_collection_idprop[IFCStatus]
class BIMWorkScheduleProperties(PropertyGroup):
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
)
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):
# This belongs by itself for performance reasons. https://developer.blender.org/T87737
@@ -507,11 +617,19 @@ class WorkCalendar(PropertyGroup):
name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class RecurrenceComponent(PropertyGroup):
name: StringProperty(name="Name")
is_specified: BoolProperty(name="Is Specified")
if TYPE_CHECKING:
name: str
is_specified: bool
class BIMWorkCalendarProperties(PropertyGroup):
work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute)
@@ -542,6 +660,22 @@ class BIMWorkCalendarProperties(PropertyGroup):
start_time: StringProperty(name="Start 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:
# `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_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):
start_frame: IntProperty(name="Start Frame")
@@ -568,6 +709,12 @@ class BIMDateTextProperties(PropertyGroup):
start: StringProperty(name="Start")
finish: StringProperty(name="Finish")
if TYPE_CHECKING:
start_frame: int
total_frames: int
start: str
finish: str
class BIMTaskTypeColor(PropertyGroup):
name: StringProperty(name="Name")
@@ -580,6 +727,11 @@ class BIMTaskTypeColor(PropertyGroup):
max=1.0,
)
if TYPE_CHECKING:
name: str
animation_type: str
color: tuple[float, float, float]
class BIMAnimationProperties(PropertyGroup):
is_editing: BoolProperty(name="Is Loaded", default=False)
@@ -607,3 +759,14 @@ class BIMAnimationProperties(PropertyGroup):
update=update_color_progress,
)
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,
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):
@@ -154,7 +158,7 @@ class BIM_PT_work_schedules(Panel):
SequenceData.load()
if not WorkScheduleData.is_loaded:
WorkScheduleData.load()
self.props = context.scene.BIMWorkScheduleProperties
self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props()
if not self.props.active_work_schedule_id:
@@ -479,14 +483,14 @@ class BIM_PT_animation_tools(Panel):
@classmethod
def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties
props = tool.Sequence.get_work_schedule_props()
if props.active_work_schedule_id:
return True
return False
def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties
self.animation_props = context.scene.BIMAnimationProperties
self.props = tool.Sequence.get_work_schedule_props()
self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True)
row.alignment = "RIGHT"
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:
AnimationColorSchemeData.load()
self.animation_props = context.scene.BIMAnimationProperties
self.animation_props = tool.Sequence.get_animation_props()
row = self.layout.row(align=True)
row.alignment = "RIGHT"
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
def poll(cls, context):
props = context.scene.BIMWorkScheduleProperties
props = tool.Sequence.get_work_schedule_props()
if not props.active_work_schedule_id:
return False
tprops = tool.Sequence.get_task_tree_props()
@@ -658,7 +662,7 @@ class BIM_PT_task_icom(Panel):
if not TaskICOMData.is_loaded:
TaskICOMData.load()
self.props = context.scene.BIMWorkScheduleProperties
self.props = tool.Sequence.get_work_schedule_props()
self.tprops = tool.Sequence.get_task_tree_props()
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):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
props = context.scene.BIMWorkScheduleProperties
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: "BIMWorkScheduleProperties",
item: "Attribute",
icon,
active_data,
active_propname,
):
props = tool.Sequence.get_work_schedule_props()
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
@@ -820,7 +833,7 @@ class BIM_UL_product_output_tasks(UIList):
class BIM_UL_tasks(UIList):
@classmethod
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)
split1 = row.split(factor=0.1)
@@ -829,9 +842,18 @@ class BIM_UL_tasks(UIList):
split2.label(text="Name")
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:
self.props = context.scene.BIMWorkScheduleProperties
self.props = tool.Sequence.get_work_schedule_props()
task = SequenceData.data["tasks"][item.ifc_definition_id]
row = layout.row(align=True)
@@ -1140,7 +1162,7 @@ class BIM_PT_4D_Tools(Panel):
bl_parent_id = "BIM_PT_tab_sequence"
def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties
self.props = tool.Sequence.get_work_schedule_props()
row = self.layout.row()
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")
+6 -5
View File
@@ -214,11 +214,11 @@ class Blender(bonsai.core.tool.Blender):
return omprops.active_material_set_item_id
elif obj_type == "Task":
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":
return context.scene.BIMCostProperties.cost_items[
context.scene.BIMCostProperties.active_cost_item_index
].ifc_definition_id
cost_props = tool.Cost.get_cost_props()
return cost_props.cost_items[cost_props.active_cost_item_index].ifc_definition_id
elif obj_type == "Resource":
return context.scene.BIMResourceTreeProperties.resources[
context.scene.BIMResourceProperties.active_resource_index
@@ -227,7 +227,8 @@ class Blender(bonsai.core.tool.Blender):
props = tool.Profile.get_profile_props()
return props.profiles[props.active_profile_index].ifc_definition_id
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":
prop = context.scene.BIMGroupProperties
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 bonsai.core.tool
import bonsai.tool as tool
@@ -10,27 +28,35 @@ import ifcopenshell.util.unit
import bonsai.bim.helper
import json
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):
RELATED_OBJECT_TYPE = Literal["PRODUCT", "PROCESS", "RESOURCE"]
@classmethod
def get_cost_props(cls) -> "BIMCostProperties":
return bpy.context.scene.BIMCostProperties
@classmethod
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)
@classmethod
def disable_editing_cost_schedule(cls) -> None:
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()
@classmethod
def load_active_schedule_columns(cls) -> None:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
active_columns = props.columns
storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -53,7 +79,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def store_active_schedule_columns(cls) -> None:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
active_columns = props.columns
storage = props.columns_storage
active_cost_schedule_id = cls.get_active_cost_schedule().id()
@@ -67,7 +93,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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
active_cost_schedule_id = cost_schedule.id()
@@ -79,8 +105,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def enable_editing_cost_schedule_attributes(cls, cost_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMCostProperties.active_cost_schedule_id = cost_schedule.id()
bpy.context.scene.BIMCostProperties.is_editing = "COST_SCHEDULE_ATTRIBUTES"
props = cls.get_cost_props()
props.active_cost_schedule_id = cost_schedule.id()
props.is_editing = "COST_SCHEDULE_ATTRIBUTES"
@classmethod
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()
return True
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
props.cost_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(cost_schedule, props.cost_schedule_attributes, callback=special_import)
@classmethod
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.is_editing = "COST_ITEMS"
@@ -123,7 +150,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def load_cost_schedule_tree(cls) -> None:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
props.is_cost_update_enabled = False
cost_schedule = tool.Ifc.get().by_id(props.active_cost_schedule_id)
props.cost_items.clear()
@@ -137,7 +164,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
if cost_item.id() in cls.contracted_cost_items:
@@ -146,7 +173,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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)
for cost_item in props.cost_items:
if cost_item.ifc_definition_id in cls.contracted_cost_items:
@@ -155,7 +182,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
cls.contracted_cost_items.append(cost_item.id())
@@ -163,7 +190,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def contract_cost_items(cls) -> None:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
if not hasattr(cls, "contracted_cost_items"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
for cost_item in props.cost_items:
@@ -173,7 +200,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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"):
cls.contracted_cost_items = json.loads(props.contracted_cost_items)
if props.active_cost_item_id == cost_item_id:
@@ -185,35 +212,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def enable_editing_cost_item_attributes(cls, cost_item: ifcopenshell.entity_instance):
bpy.context.scene.BIMCostProperties.active_cost_item_id = cost_item.id()
bpy.context.scene.BIMCostProperties.cost_item_editing_type = "ATTRIBUTES"
props = cls.get_cost_props()
props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "ATTRIBUTES"
@classmethod
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()
bonsai.bim.helper.import_attributes2(cost_item, props.cost_item_attributes)
@classmethod
def disable_editing_cost_item(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
props = cls.get_cost_props()
props.active_cost_item_id = 0
props.change_cost_item_parent = False
@classmethod
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)
@classmethod
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:
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
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:
return
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()
if not cost_item:
return
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
props.cost_item_type_products.clear()
# TODO implement process and resource types
# 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
) -> None:
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:
products = cls.get_cost_item_assignments(cost_item, filter_by_type=related_object_type, is_deep=False)
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):
create_list_items(collection, cost_item, is_deep=False)
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
if related_object_type == "PRODUCT":
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)
elif related_object_type == "PROCESS":
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)
elif related_object_type == "RESOURCE":
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)
@classmethod
@@ -321,33 +352,35 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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.cost_item_editing_type = "QUANTITIES"
@classmethod
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
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()
bonsai.bim.helper.import_attributes2(physical_quantity, props.quantity_attributes)
@classmethod
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.cost_item_editing_type = "VALUES"
@classmethod
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
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)
@classmethod
@@ -356,7 +389,8 @@ class Cost(bonsai.core.tool.Cost):
) -> dict[str, Any]:
if cost_type == "FIXED":
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":
category = "*"
attributes = {"Category": category}
@@ -404,7 +438,7 @@ class Cost(bonsai.core.tool.Cost):
break
return True
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
props.cost_value_attributes.clear()
is_rates = cls.is_active_schedule_of_rates()
callback = lambda name, prop, data: import_attributes(
@@ -420,38 +454,37 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def is_active_schedule_of_rates(cls) -> bool:
return (
tool.Ifc.get().by_id(bpy.context.scene.BIMCostProperties.active_cost_schedule_id).PredefinedType
== "SCHEDULEOFRATES"
)
props = cls.get_cost_props()
return tool.Ifc.get().by_id(props.active_cost_schedule_id).PredefinedType == "SCHEDULEOFRATES"
@classmethod
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.cost_value_editing_type = "ATTRIBUTES"
@classmethod
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.cost_value_editing_type = ""
@classmethod
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()
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
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.cost_value_editing_type = "FORMULA"
@classmethod
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
def get_cost_value_attributes(cls) -> dict[str, Any]:
@@ -468,15 +501,14 @@ class Cost(bonsai.core.tool.Cost):
if prop.name == "UnitBasisUnit":
return True
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
callback = lambda attributes, prop: export_attributes(attributes, prop)
return bonsai.bim.helper.export_attributes(props.cost_value_attributes, callback)
@classmethod
def get_cost_value_unit_component(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id(
int(bpy.context.scene.BIMCostProperties.cost_value_attributes.get("UnitBasisUnit").enum_value)
)
props = cls.get_cost_props()
return tool.Ifc.get().by_id(int(props.cost_value_attributes["UnitBasisUnit"].enum_value))
@classmethod
def get_cost_item_assignments(
@@ -491,7 +523,8 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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
def get_cost_item_products(
@@ -543,18 +576,18 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def add_cost_column(cls, name: str) -> None:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
new = props.columns.add()
new.name = name
@classmethod
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))
@classmethod
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")
if schedule_id is None:
return
@@ -562,7 +595,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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.remove(cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -570,7 +603,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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.append(cost_item)
props.contracted_cost_item_rates = json.dumps(contracted_cost_item_rates)
@@ -605,7 +638,7 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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.cost_item_rates.clear()
props.columns.clear()
@@ -689,13 +722,15 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
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
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 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
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)
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]:
expand_ancestors(cost_item)
cost_item_index = [item.ifc_definition_id for item in bpy.context.scene.BIMCostProperties.cost_items].index(
cost_item.id()
) or 0
bpy.context.scene.BIMCostProperties.active_cost_item_index = cost_item_index
cost_item_index = [item.ifc_definition_id for item in cost_props.cost_items].index(cost_item.id()) or 0
cost_props.active_cost_item_index = cost_item_index
@classmethod
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
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.product_cost_items.clear()
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:
if not cost_item:
return
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
if props.change_cost_item_parent:
props.active_cost_item_id = cost_item.id()
props.cost_item_editing_type = "PARENT"
@@ -781,8 +814,9 @@ class Cost(bonsai.core.tool.Cost):
@classmethod
def disable_editing_cost_item_parent(cls) -> None:
bpy.context.scene.BIMCostProperties.active_cost_item_id = 0
bpy.context.scene.BIMCostProperties.change_cost_item_parent = False
props = cls.get_cost_props()
props.active_cost_item_id = 0
props.change_cost_item_parent = False
@classmethod
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
def get_currency_attributes(cls) -> dict[str, str]:
props = bpy.context.scene.BIMCostProperties
props = cls.get_cost_props()
currency = props.currency
if currency == "CUSTOM":
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:
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):
RELATED_OBJECT_TYPE = Literal["RESOURCE", "PRODUCT", "CONTROL"]
@classmethod
def get_animation_props(cls) -> BIMAnimationProperties:
return bpy.context.scene.BIMAnimationProperties
@classmethod
def get_task_tree_props(cls) -> BIMTaskTreeProperties:
return bpy.context.scene.BIMTaskTreeProperties
@classmethod
def get_work_schedule_props(cls) -> BIMWorkScheduleProperties:
return bpy.context.scene.BIMWorkScheduleProperties
@classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]:
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)
return True
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
return bonsai.bim.helper.export_attributes(props.work_schedule_attributes, callback)
@classmethod
@@ -129,23 +137,25 @@ class Sequence(bonsai.core.tool.Sequence):
prop.string_value = "" if prop.is_null else data[name]
return True
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.work_schedule_attributes.clear()
bonsai.bim.helper.import_attributes2(work_schedule, props.work_schedule_attributes, callback)
@classmethod
def enable_editing_work_schedule(cls, work_schedule: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id = work_schedule.id()
bpy.context.scene.BIMWorkScheduleProperties.editing_type = "WORK_SCHEDULE"
props = cls.get_work_schedule_props()
props.active_work_schedule_id = work_schedule.id()
props.editing_type = "WORK_SCHEDULE"
@classmethod
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
def enable_editing_work_schedule_tasks(cls, work_schedule: Union[ifcopenshell.entity_instance, None]) -> None:
if work_schedule:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.active_work_schedule_id = work_schedule.id()
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:
props = cls.get_task_tree_props()
props.tasks.clear()
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
cls.contracted_tasks = json.loads(props.contracted_tasks)
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
def get_sorted_tasks_ids(cls, tasks: list[ifcopenshell.entity_instance]) -> list[int]:
props = cls.get_work_schedule_props()
def get_sort_key(task):
# Sorting only applies to actual tasks, not the WBS
# for rel in task.IsNestedBy:
# for object in rel.RelatedObjects:
# if object.is_a("IfcTask"):
# 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":
return task.get_info(task)[name] or ""
elif column_type == "IfcTaskTime" and task.TaskTime:
@@ -179,12 +191,12 @@ class Sequence(bonsai.core.tool.Sequence):
s = sort_keys[i]
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}
related_object_ids = sorted(sort_keys, key=natural_sort_key)
else:
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()
return related_object_ids
@@ -205,7 +217,7 @@ class Sequence(bonsai.core.tool.Sequence):
# TODO: task argument is never used?
@classmethod
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()
tasks_with_visual_bar = cls.get_task_bar_list()
props.is_task_update_enabled = False
@@ -270,24 +282,26 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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 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
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.remove(task.id())
props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod
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
def contract_all_tasks(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
tprops = cls.get_task_tree_props()
contracted_tasks = json.loads(props.contracted_tasks)
for task_item in tprops.tasks:
@@ -297,23 +311,24 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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.append(task.id())
props.contracted_tasks = json.dumps(contracted_tasks)
@classmethod
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
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 [
task.ifc_definition_id for task in cls.get_task_tree_props().tasks
]: # Task was deleted
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
props.active_task_id = 0
props.active_task_time_id = 0
@classmethod
def get_checked_tasks(cls) -> list[ifcopenshell.entity_instance]:
@@ -323,11 +338,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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
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
def get_active_work_time(cls) -> ifcopenshell.entity_instance:
@@ -339,31 +356,34 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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()
bonsai.bim.helper.import_attributes2(task, props.task_attributes)
@classmethod
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.editing_task_type = "ATTRIBUTES"
@classmethod
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
def load_task_time_attributes(cls, task_time: ifcopenshell.entity_instance) -> None:
import bonsai.bim.module.sequence.helper as helper
props = cls.get_work_schedule_props()
def callback(
name: str, prop: Union[bonsai.bim.prop.Attribute, None], data: dict[str, Any]
) -> Union[bool, None]:
if prop and prop.data_type == "string":
# TODO: Check actual attribute type instead of providing attribute names.
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
if prop.is_null:
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()
return True
props = bpy.context.scene.BIMWorkScheduleProperties
props.task_time_attributes.clear()
props.durations_attributes.clear()
bonsai.bim.helper.import_attributes2(task_time, props.task_time_attributes, callback)
@classmethod
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_time_id = task.TaskTime.id()
props.editing_task_type = "TASKTIME"
@classmethod
def disable_editing_task(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
bpy.context.scene.BIMWorkScheduleProperties.editing_task_type = ""
props = cls.get_work_schedule_props()
props.active_task_id = 0
props.active_task_time_id = 0
props.editing_task_type = ""
@classmethod
def get_task_time_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper
props = cls.get_work_schedule_props()
def callback(attributes, prop):
if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if prop.is_null:
@@ -408,7 +430,6 @@ class Sequence(bonsai.core.tool.Sequence):
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name in ["ScheduleDuration", "ActualDuration", "FreeFloat", "TotalFloat"]:
props = bpy.context.scene.BIMWorkScheduleProperties
if prop.is_null:
attributes[prop.name] = None
for value in props.durations_attributes.values():
@@ -424,13 +445,12 @@ class Sequence(bonsai.core.tool.Sequence):
value = 0
return True
props = bpy.context.scene.BIMWorkScheduleProperties
return bonsai.bim.helper.export_attributes(props.task_time_attributes, callback)
@classmethod
def load_task_resources(cls, task: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
rprops = bpy.context.scene.BIMResourceProperties
props = cls.get_work_schedule_props()
rprops = cls.get_resource_props()
props.task_resources.clear()
rprops.is_resource_update_enabled = False
for resource in cls.get_task_resources(task) or []:
@@ -442,12 +462,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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)
@classmethod
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)
@classmethod
@@ -468,12 +490,13 @@ class Sequence(bonsai.core.tool.Sequence):
) -> Union[list[ifcopenshell.entity_instance], None]:
if not task:
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)
@classmethod
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()
for input in inputs:
new = props.task_inputs.add()
@@ -482,7 +505,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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()
if outputs:
for output in outputs:
@@ -493,10 +516,9 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def get_highlighted_task(cls) -> Union[ifcopenshell.entity_instance, None]:
tasks = cls.get_task_tree_props().tasks
if len(tasks) and len(tasks) > bpy.context.scene.BIMWorkScheduleProperties.active_task_index:
return tool.Ifc.get().by_id(
tasks[bpy.context.scene.BIMWorkScheduleProperties.active_task_index].ifc_definition_id
)
props = cls.get_work_schedule_props()
if len(tasks) and len(tasks) > props.active_task_index:
return tool.Ifc.get().by_id(tasks[props.active_task_index].ifc_definition_id)
@classmethod
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
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.editing_task_type = "CALENDAR"
@classmethod
def enable_editing_task_sequence(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.editing_task_type = "SEQUENCE"
@classmethod
def disable_editing_task_time(cls) -> None:
bpy.context.scene.BIMWorkScheduleProperties.active_task_id = 0
bpy.context.scene.BIMWorkScheduleProperties.active_task_time_id = 0
props = cls.get_work_schedule_props()
props.active_task_id = 0
props.active_task_time_id = 0
@classmethod
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()
bonsai.bim.helper.import_attributes2(rel_sequence, props.sequence_attributes)
@classmethod
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.editing_sequence_type = "ATTRIBUTES"
@classmethod
def load_lag_time_attributes(cls, lag_time: ifcopenshell.entity_instance) -> None:
props = cls.get_work_schedule_props()
def callback(name, prop, data):
if name == "LagValue":
prop = bpy.context.scene.BIMWorkScheduleProperties.lag_time_attributes.add()
prop = props.lag_time_attributes.add()
prop.name = name
prop.is_null = data[name] is None
prop.is_optional = False
@@ -688,27 +713,29 @@ class Sequence(bonsai.core.tool.Sequence):
)
return True
props = bpy.context.scene.BIMWorkScheduleProperties
props.lag_time_attributes.clear()
bonsai.bim.helper.import_attributes2(lag_time, props.lag_time_attributes, callback)
@classmethod
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.editing_sequence_type = "LAG_TIME"
@classmethod
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
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
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
def select_products(cls, products: Iterable[ifcopenshell.entity_instance]) -> None:
@@ -719,14 +746,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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.name = f"{column_type}.{name}"
new.data_type = data_type
@classmethod
def setup_default_task_columns(cls) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.columns.clear()
default_columns = ["ScheduleStart", "ScheduleFinish", "ScheduleDuration"]
for item in default_columns:
@@ -736,14 +763,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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))
if props.sort_column == name:
props.sort_column = ""
@classmethod
def set_task_sort_column(cls, column: str) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.sort_column = column
@classmethod
@@ -772,12 +799,13 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def is_work_schedule_active(cls, work_schedule):
return (
True if work_schedule.id() == bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id else False
)
props = cls.get_work_schedule_props()
return True if work_schedule.id() == props.active_work_schedule_id else False
@classmethod
def go_to_task(cls, task):
props = cls.get_work_schedule_props()
def get_ancestor_ids(task):
ids = []
for rel in task.Nests or []:
@@ -785,11 +813,11 @@ class Sequence(bonsai.core.tool.Sequence):
ids.extend(get_ancestor_ids(rel.RelatingObject))
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):
if ancestor_id in contracted_tasks:
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()
cls.load_task_tree(work_schedule)
@@ -797,7 +825,7 @@ class Sequence(bonsai.core.tool.Sequence):
task_props = cls.get_task_tree_props()
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
@classmethod
@@ -808,7 +836,7 @@ class Sequence(bonsai.core.tool.Sequence):
def update_visualisation_date(cls, start_date, finish_date):
if not (start_date and finish_date):
return
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.visualisation_start = ifcopenshell.util.date.canonicalise_time(start_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):
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
settings = {
"viz_start": (
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
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(
material=material_progress,
vertical_increment=vertical_increment,
@@ -887,7 +916,7 @@ class Sequence(bonsai.core.tool.Sequence):
name=task_data["name"] + "/Progress Bar",
)
color_full = bpy.context.scene.BIMAnimationProperties.color_full
color_full = anim_props.color_full
bar2 = add_bar(
material=material_full,
vertical_increment=vertical_increment,
@@ -1082,7 +1111,7 @@ class Sequence(bonsai.core.tool.Sequence):
"Color": (0.2, 0.2, 0.2),
},
}
props = bpy.context.scene.BIMAnimationProperties
props = cls.get_animation_props()
props.task_output_colors.clear()
props.task_input_colors.clear()
for group, data in groups.items():
@@ -1102,14 +1131,14 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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
@classmethod
def get_finish_date(cls) -> Union[datetime, None]:
finish = parser.parse(
bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish, dayfirst=True, fuzzy=True
)
props = cls.get_work_schedule_props()
finish = parser.parse(props.visualisation_finish, dayfirst=True, fuzzy=True)
return finish or None
@classmethod
@@ -1241,7 +1270,7 @@ class Sequence(bonsai.core.tool.Sequence):
def calculate_using_frames(start, finish, animation_frames, real_duration):
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):
return
@@ -1346,7 +1375,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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
if product_frame["type"] in ["LOGISTIC", "MOVE", "DISPOSAL"]:
cls.animate_destruction(obj, start_frame, product_frame, color, animation_type)
@@ -1355,7 +1384,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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
if product_frame["type"] in ["CONSTRUCTION", "INSTALLATION", "NOTDEFINED"]:
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.total_frames = int(settings["total_frames"])
obj.data.BIMDateTextProperties.start = bpy.context.scene.BIMWorkScheduleProperties.visualisation_start
obj.data.BIMDateTextProperties.finish = bpy.context.scene.BIMWorkScheduleProperties.visualisation_finish
props = cls.get_work_schedule_props()
obj.data.BIMDateTextProperties.start = props.visualisation_start
obj.data.BIMDateTextProperties.finish = props.visualisation_finish
append_handler(animate_text_handler)
@classmethod
@@ -1573,7 +1603,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
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
def get_tasks_for_product(
@@ -1585,7 +1616,7 @@ class Sequence(bonsai.core.tool.Sequence):
def load_product_related_tasks(
cls, task_inputs: list[ifcopenshell.entity_instance], task_ouputs: list[ifcopenshell.entity_instance]
) -> None:
props = bpy.context.scene.BIMWorkScheduleProperties
props = cls.get_work_schedule_props()
props.product_input_tasks.clear()
props.product_output_tasks.clear()
for task in task_inputs or []:
@@ -1617,18 +1648,21 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def is_sorting_enabled(cls):
return bpy.context.scene.BIMWorkScheduleProperties.sort_column
props = cls.get_work_schedule_props()
return props.sort_column
@classmethod
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
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
if predefined_type == "USERDEFINED":
object_type = bpy.context.scene.BIMWorkScheduleProperties.object_type
object_type = props.object_type
return predefined_type, object_type
@classmethod
@@ -1649,7 +1683,7 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod
def save_animation_color_scheme(cls, name: str) -> ifcopenshell.entity_instance:
props = bpy.context.scene.BIMAnimationProperties
props = cls.get_animation_props()
colour_scheme = {
"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},
@@ -1674,7 +1708,7 @@ class Sequence(bonsai.core.tool.Sequence):
if data.get("type") == "BBIM_AnimationColorScheme":
inputs_color_scheme = data.get("colourscheme").get("Inputs")
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_output_colors.clear()
for value, colour in inputs_color_scheme.items():
@@ -1711,26 +1745,30 @@ class Sequence(bonsai.core.tool.Sequence):
return False
@classmethod
def get_task_bar_list(cls):
return json.loads(bpy.context.scene.BIMWorkScheduleProperties.task_bars)
def get_task_bar_list(cls) -> list[int]:
props = cls.get_work_schedule_props()
return json.loads(props.task_bars)
@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.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
def remove_task_bar(cls, task_id):
def remove_task_bar(cls, task_id: int) -> None:
task_bars = cls.get_task_bar_list()
if task_id in task_bars:
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
def get_animation_color_scheme(cls):
if len(bpy.context.scene.BIMAnimationProperties.saved_color_schemes) > 0:
return tool.Ifc.get().by_id(int(bpy.context.scene.BIMAnimationProperties.saved_color_schemes))
props = cls.get_animation_props()
if len(props.saved_color_schemes) > 0:
return tool.Ifc.get().by_id(int(props.saved_color_schemes))
@classmethod
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)
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:
headers = df.columns.tolist()
@@ -65,28 +65,22 @@ def assign_material(
:param products: The list of IfcProducts to assign the material or material set
to.
:type products: list[ifcopenshell.entity_instance]
:param type: Choose from "IfcMaterial", "IfcMaterialConstituentSet",
"IfcMaterialLayerSet", "IfcMaterialLayerSetUsage",
"IfcMaterialProfileSet", "IfcMaterialProfileSetUsage", or
"IfcMaterialList". Note that "Set Usages" may only be assigned to
occurrences, not types. Defaults to "IfcMaterial".
:type type: str
: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
from the element type automatically.
If IfcMaterial is provided as material and type is not IfcMaterial,
provided material will be ignored except for IfcMaterialList
where it will be used as part of the list.
:type material: ifcopenshell.entity_instance, optional
:return: IfcRelAssociatesMaterial entity
or a list of IfcRelAssociatesMaterial entities
(possible if `type` is Usage
and `products` require different Usages)
or `None` if `products` was empty list.
:rtype: Union[
ifcopenshell.entity_instance,
list[ifcopenshell.entity_instance], None]
Example:
@@ -62,6 +62,8 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
set_items = material.MaterialConstituents or []
elif material.is_a("IfcMaterialList"):
set_items = []
else:
raise ValueError(f"Unknown material set type: {material.is_a()}")
for set_item in set_items:
file.remove(set_item)
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.
:param products: The list IfcProducts that may or may not have a material
:type product: list[ifcopenshell.entity_instance]
:return: None
:rtype: None
Example:
@@ -54,18 +52,16 @@ def unassign_material(file: ifcopenshell.file, products: list[ifcopenshell.entit
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"products": products}
return usecase.execute()
return usecase.execute(products)
class Usecase:
file: ifcopenshell.file
settings: dict[str, Any]
def execute(self):
self.products = set(self.settings["products"])
if not self.products:
def execute(self, products: list[ifcopenshell.entity_instance]) -> None:
if not products:
return
self.products = set(products)
self.remove_material_usages_from_types()
self.unassign_materials()