BBIM 4D features & fixes:

- feature to re-order tasks
- fixes for task sorting
- further workSchedule UI/UX improvements
- forgotten bits and bobs
This commit is contained in:
Sigma Dimensions
2023-04-30 15:44:13 +01:00
parent 2aa5e9d808
commit 55250aba6d
12 changed files with 403 additions and 104 deletions
@@ -97,6 +97,7 @@ classes = (
operator.RemoveWorkPlan,
operator.RemoveWorkSchedule,
operator.RemoveWorkTime,
operator.ReorderTask,
operator.SelectTaskRelatedProducts,
operator.SelectTaskRelatedInputs,
operator.SelectWorkScheduleProducts,
@@ -223,6 +223,9 @@ class SequenceData:
if rel.is_a("IfcRelAssignsToControl") and rel.RelatingControl:
if rel.RelatingControl.is_a("IfcWorkCalendar"):
data["HasAssignmentsWorkCalendar"].append(rel.RelatingControl.id())
data["NestingIndex"] = None
for rel in task.Nests or []:
data["NestingIndex"] = rel.RelatedObjects.index(task)
cls.data["tasks"][task.id()] = data
@classmethod
@@ -275,13 +275,13 @@ class EditTaskTime(bpy.types.Operator, tool.Ifc.Operator):
class EnableEditingTask(bpy.types.Operator):
bl_idname = "bim.enable_editing_task"
bl_label = "Enable Editing Task"
bl_idname = "bim.enable_editing_task_attributes"
bl_label = "Enable Editing Task Attributes"
bl_options = {"REGISTER", "UNDO"}
task: bpy.props.IntProperty()
def execute(self, context):
core.enable_editing_task(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
core.enable_editing_task_attributes(tool.Sequence, task=tool.Ifc.get().by_id(self.task))
return {"FINISHED"}
@@ -1295,7 +1295,7 @@ class CopyTask(bpy.types.Operator, tool.Ifc.Operator):
class LoadProductTasks(bpy.types.Operator):
bl_idname = "bim.load_product_tasks"
bl_idname = "bim.load_product_related_tasks"
bl_label = "Load Product Tasks"
bl_options = {"REGISTER", "UNDO"}
@@ -1310,7 +1310,7 @@ class LoadProductTasks(bpy.types.Operator):
return True
def execute(self, context):
core.load_product_tasks(
core.load_product_related_tasks(
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
)
return {"FINISHED"}
@@ -1355,3 +1355,18 @@ class SelectUnassignedWorkScheduleProducts(bpy.types.Operator):
if isinstance(r, str):
self.report({"WARNING"}, r)
return {"FINISHED"}
class ReorderTask(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.reorder_task_nesting"
bl_label = "Reorder Nesting"
bl_options = {"REGISTER", "UNDO"}
new_index: bpy.props.IntProperty()
task: bpy.props.IntProperty()
def _execute(self, context):
r = core.reorder_task_nesting(
tool.Ifc, tool.Sequence, task=tool.Ifc.get().by_id(self.task), new_index=self.new_index
)
if isinstance(r, str):
self.report({"WARNING"}, r)
@@ -284,9 +284,14 @@ def update_color_progress(self, context):
color[1] = color_progress.g
color[2] = color_progress.b
def update_sort_reversed(self, context):
if context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
core.create_task_tree(tool.Sequence, work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id))
core.load_task_tree(
tool.Sequence,
work_schedule=tool.Ifc.get().by_id(context.scene.BIMWorkScheduleProperties.active_work_schedule_id),
)
class Task(PropertyGroup):
name: StringProperty(name="Name", update=updateTaskName)
@@ -359,7 +364,8 @@ class BIMWorkScheduleProperties(PropertyGroup):
active_task_id: IntProperty(name="Active Task Id")
task_attributes: CollectionProperty(name="Task Attributes", type=Attribute)
should_show_visualisation_ui: BoolProperty(name="Should Show Visualisation UI", default=False)
should_show_bar_visual_option: BoolProperty(name="Add to task bar", default=False)
should_show_task_bar_selection: BoolProperty(name="Add to task bar", default=False)
should_show_snapshot_ui: BoolProperty(name="Should Show Snapshot UI", default=False)
should_show_column_ui: BoolProperty(name="Should Show Column UI", default=False)
columns: CollectionProperty(name="Columns", type=Attribute)
active_column_index: IntProperty(name="Active Column Index")
@@ -417,6 +423,8 @@ class BIMWorkScheduleProperties(PropertyGroup):
product_output_tasks: CollectionProperty(name="Product Task Outputs", type=TaskProduct)
active_product_output_task_index: IntProperty(name="Active Product Output Task Index")
active_product_input_task_index: IntProperty(name="Active Product Input Task Index")
enable_reorder: BoolProperty(name="Enable Reorder", default=False)
show_task_operators: BoolProperty(name="Show Task Options", default=True)
class BIMTaskTreeProperties(PropertyGroup):
@@ -170,7 +170,9 @@ class BIM_PT_work_schedules(Panel):
row1 = col.row(align=True)
row1.alignment = "RIGHT"
row1.prop(self.props, "should_show_column_ui", text="Schedule Columns", icon="SHORTDISPLAY")
row1.prop(self.props, "should_show_visualisation_ui", text="Animation Options", icon="CAMERA_STEREO")
row2 = col.row(align=True)
row2.prop(self.props, "should_show_visualisation_ui", text="Animation Options", icon="CAMERA_STEREO")
row2.prop(self.props, "should_show_snapshot_ui", text="Snapshot Options", icon="CAMERA_STEREO")
row.operator("bim.disable_editing_work_schedule", text="Disable editing", icon="CANCEL")
else:
row.operator(
@@ -189,6 +191,8 @@ class BIM_PT_work_schedules(Panel):
self.draw_column_ui()
if self.props.should_show_visualisation_ui:
self.draw_visualisation_ui()
if self.props.should_show_snapshot_ui:
self.draw_snapshot_ui()
self.draw_editable_task_ui(work_schedule_id)
def draw_task_operators(self):
@@ -206,14 +210,23 @@ class BIM_PT_work_schedules(Panel):
row.operator("bim.edit_task", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_task", text="", icon="CANCEL")
else:
row.prop(self.props, "should_show_bar_visual_option", text="", icon="NLA_PUSHDOWN")
row.operator("bim.enable_editing_task_sequence", text="", icon="TRACKING").task = ifc_definition_id
row.operator("bim.enable_editing_task_time", text="", icon="TIME").task = ifc_definition_id
row.operator("bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO").task = ifc_definition_id
row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = ifc_definition_id
row.operator("bim.add_task", text="", icon="ADD").task = ifc_definition_id
row.operator("bim.duplicate_task", text="", icon="DUPLICATE").task = ifc_definition_id
row.operator("bim.remove_task", text="", icon="X").task = ifc_definition_id
row.prop(self.props, "show_task_operators", text="Edit", icon="GREASEPENCIL")
if self.props.show_task_operators:
row2 = self.layout.row(align=True)
row2.alignment = "RIGHT"
row2.prop(self.props, "enable_reorder", text="", icon="SORTALPHA")
row2.operator("bim.enable_editing_task_sequence", text="", icon="TRACKING").task = ifc_definition_id
row2.operator("bim.enable_editing_task_time", text="", icon="TIME").task = ifc_definition_id
row2.operator(
"bim.enable_editing_task_calendar", text="", icon="VIEW_ORTHO"
).task = ifc_definition_id
row2.operator(
"bim.enable_editing_task_attributes", text="", icon="GREASEPENCIL"
).task = ifc_definition_id
row.operator("bim.add_task", text="Add", icon="ADD").task = ifc_definition_id
row.operator("bim.duplicate_task", text="Copy", icon="DUPLICATE").task = ifc_definition_id
row.operator("bim.remove_task", text="Delete", icon="X").task = ifc_definition_id
def draw_column_ui(self):
row = self.layout.row()
@@ -281,26 +294,35 @@ class BIM_PT_work_schedules(Panel):
row.prop(self.animation_props, "should_show_task_bar_options", text="Task Bar", icon="NLA_PUSHDOWN")
if self.animation_props.should_show_task_bar_options:
row = self.layout.row()
row.operator("bim.add_task_bars", text="Add Bar Visual", icon="NLA_PUSHDOWN")
row.label(text="Task Bar Options", icon="NLA_PUSHDOWN")
row.alignment = "LEFT"
row = self.layout.row(align=True)
row.prop(self.props, "should_show_task_bar_selection", text="Enable Selection", icon="NLA_PUSHDOWN")
row.operator("bim.add_task_bars", text="Generate bars", icon="NLA_PUSHDOWN")
grid = self.layout.grid_flow(columns=2, even_columns=True)
# Column1
col = grid.column()
row1 = col.row(align=True)
row1.label(text="Bar Colors", icon="NLA_PUSHDOWN")
row2 = col.row(align=True)
row2.prop(self.animation_props, "color_progress")
row = col.row(align=True)
row.prop(self.animation_props, "color_progress")
row3 = col.row(align=True)
row3.prop(self.animation_props, "color_full")
row = col.row(align=True)
row.prop(self.animation_props, "color_full")
if self.animation_props.is_editing:
self.draw_visualisation_settings_ui()
row = self.layout.row()
row = self.layout.row(align=True)
op = row.operator("bim.visualise_work_schedule_date_range", text="Create Animation", icon="OUTLINER_OB_CAMERA")
op.work_schedule = self.props.active_work_schedule_id
def draw_snapshot_ui(self):
row = self.layout.row(align=True)
row.label(text="Create Construction Snapshot:")
row = self.layout.row(align=True)
op = row.operator("bim.datepicker", text=self.props.visualisation_start or "Date", icon="REW")
op.target_prop = "BIMWorkScheduleProperties.visualisation_start"
op = row.operator("bim.visualise_work_schedule_date", text="Create SnapShot", icon="RESTRICT_RENDER_OFF")
op.work_schedule = self.props.active_work_schedule_id
@@ -572,7 +594,7 @@ class BIM_UL_task_resources(UIList):
if item:
row = layout.row(align=True)
row.prop(item, "name", emboss=False, text="")
row.prop(item, "schedule_usage", emboss=False, text="")
row.label(text=str(item.schedule_usage))
class BIM_UL_animation_colors(UIList):
@@ -636,7 +658,7 @@ class BIM_UL_tasks(UIList):
text="",
emboss=False,
)
if self.props.should_show_bar_visual_option:
if self.props.should_show_task_bar_selection:
row.prop(
item,
"has_bar_visual",
@@ -644,6 +666,8 @@ class BIM_UL_tasks(UIList):
text="",
emboss=False,
)
if self.props.enable_reorder:
self.draw_order_operator(row, item.ifc_definition_id)
if self.props.active_task_id:
if self.props.editing_task_type == "SEQUENCE" and self.props.active_task_id != item.ifc_definition_id:
if item.is_predecessor:
@@ -658,6 +682,18 @@ class BIM_UL_tasks(UIList):
op = row.operator("bim.assign_successor", text="", icon="TRACKING_FORWARDS", emboss=False)
op.task = item.ifc_definition_id
def draw_order_operator(self, row, ifc_definition_id):
task = SequenceData.data["tasks"][ifc_definition_id]
if task["NestingIndex"] is not None:
if task["NestingIndex"] == 0:
op = row.operator("bim.reorder_task_nesting", icon="TRIA_DOWN", text="")
op.task = ifc_definition_id
op.new_index = task["NestingIndex"] + 1
elif task["NestingIndex"] > 0:
op = row.operator("bim.reorder_task_nesting", icon="TRIA_UP", text="")
op.task = ifc_definition_id
op.new_index = task["NestingIndex"] - 1
def draw_hierarchy(self, row, item):
for i in range(0, item.level_index):
row.label(text="", icon="BLANK1")
@@ -878,7 +914,7 @@ class BIM_PT_4D_Tools(Panel):
def draw(self, context):
self.props = context.scene.BIMWorkScheduleProperties
row = self.layout.row()
row.operator("bim.load_product_tasks", text="Load Tasks", icon="FILE_REFRESH")
row.operator("bim.load_product_related_tasks", text="Load Tasks", icon="FILE_REFRESH")
grid = self.layout.grid_flow(columns=2, even_columns=True)
col1 = grid.column()
@@ -170,3 +170,15 @@ def unassign_resource(ifc, spatial, resource=None, products=None):
products = spatial.get_selected_products()
for product in products:
ifc.run("resource.unassign_resource", relating_resource=resource, related_object=product)
def edit_productivity_pset(ifc, resource_tool):
resource = resource_tool.get_highlighted_resource()
if resource is None:
return
productivity = resource_tool.get_productivity(resource)
if productivity:
pset = ifc.get().by_id(productivity["id"])
else:
pset = ifc.run("pset.add_pset", product=resource, name="EPset_Productivity")
ifc.run("pset.edit_pset", pset=pset, properties=resource_tool.get_productivity_attributes())
+32 -15
View File
@@ -74,47 +74,47 @@ def enable_editing_work_schedule(sequence, work_schedule=None):
def enable_editing_work_schedule_tasks(sequence, work_schedule=None):
sequence.enable_editing_work_schedule_tasks(work_schedule)
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def create_task_tree(sequence, work_schedule):
sequence.create_task_tree(work_schedule)
def load_task_tree(sequence, work_schedule):
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def expand_task(sequence, task=None):
sequence.expand_task(task)
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def expand_all_tasks(sequence):
sequence.expand_all_tasks()
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def contract_task(sequence, task=None):
sequence.contract_task(task)
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def contract_all_tasks(sequence):
sequence.contract_all_tasks()
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def remove_task(ifc, sequence, task=None):
ifc.run("sequence.remove_task", task=task)
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
sequence.disable_selecting_deleted_task()
@@ -129,21 +129,24 @@ def disable_editing_work_schedule(sequence):
def add_summary_task(ifc, sequence, work_schedule=None):
ifc.run("sequence.add_task", work_schedule=work_schedule)
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def add_task(ifc, sequence, parent_task=None):
ifc.run("sequence.add_task", parent_task=parent_task)
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
def enable_editing_task(sequence, task=None):
sequence.load_task_attributes(task)
sequence.enable_editing_task(task)
def enable_editing_task_attributes(sequence, task=None):
sequence.load_task_attributes(task)
sequence.enable_editing_task_attributes(task)
def edit_task(ifc, sequence, task=None):
attributes = sequence.get_task_attributes()
@@ -165,7 +168,7 @@ def copy_task_attribute(ifc, sequence, attribute_name=None):
def duplicate_task(ifc, sequence, task=None):
ifc.run("sequence.duplicate_task", task=task)
work_schedule = sequence.get_active_work_schedule()
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
@@ -464,7 +467,7 @@ def calculate_task_duration(ifc, sequence, task=None):
ifc.run("sequence.calculate_task_duration", task=task)
work_schedule = sequence.get_active_work_schedule()
if work_schedule:
sequence.create_task_tree(work_schedule)
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
@@ -473,6 +476,8 @@ def highlight_task(sequence, task=None):
is_work_schedule_active = sequence.is_work_schedule_active(work_schedule)
if is_work_schedule_active:
sequence.highlight_task(task)
else:
return "Work schedule is not active"
def highlight_product_related_task(sequence, spatial, product_type=None):
@@ -537,5 +542,17 @@ def generate_gantt_chart(sequence, work_schedule):
sequence.generate_gantt_browser_chart(json)
def load_product_tasks(sequence, product=None):
sequence.load_product_tasks(product)
def load_product_related_tasks(sequence, product=None):
sequence.load_product_related_tasks(product)
def reorder_task_nesting(ifc, sequence, task, new_index):
is_sorting_enabled = sequence.is_sorting_enabled()
is_sort_reversed = sequence.is_sort_reversed()
if is_sorting_enabled or is_sort_reversed:
return "Remove manual sorting"
else:
ifc.run("nest.reorder_nesting", item= task, new_index= new_index)
work_schedule= sequence.get_active_work_schedule()
sequence.load_task_tree(work_schedule)
sequence.load_task_properties()
+27 -22
View File
@@ -562,32 +562,37 @@ class Qto:
@interface
class Resource:
def load_resources(cls): pass
def load_resource_properties(cls): pass
def clear_productivity_data(cls, props): pass
def contract_resource(cls, resource): pass
def disable_editing_resource_cost_value(cls): pass
def disable_editing_resource_quantity(cls): pass
def disable_editing_resource(cls): pass
def disable_resource_editing_ui(cls): pass
def load_resource_attributes(cls, resource): pass
def enable_editing_resource(cls, resource): pass
def get_resource_attributes(cls): pass
def enable_editing_resource_time(cls, resource): pass
def get_resource_time(cls, resource): pass
def load_resource_time_attributes(cls, resource_time): pass
def get_resource_time_attributes(cls): pass
def enable_editing_resource_costs(cls, resource): pass
def disable_editing_resource_cost_value(cls): pass
def enable_editing_resource_cost_value_formula(cls, cost_value): pass
def load_cost_value_attributes(cls, cost_value): pass
def edit_productivity_pset(cls, resource, attributes): pass
def enable_editing_cost_value_attributes(cls, cost_value): pass
def get_resource_cost_value_formula(cls): pass
def get_resource_cost_value_attributes(cls): pass
def enable_editing_resource_base_quantity(cls, resource): pass
def enable_editing_resource_cost_value_formula(cls, cost_value): pass
def enable_editing_resource_costs(cls, resource): pass
def enable_editing_resource_quantity(cls, resource_quantity): pass
def disable_editing_resource_quantity(cls): pass
def get_resource_quantity_attributes(cls): pass
def enable_editing_resource_time(cls, resource): pass
def enable_editing_resource(cls, resource): pass
def expand_resource(cls, resource): pass
def contract_resource(cls, resource): pass
def get_highlighted_resource(cls): pass
def get_productivity_attributes(cls): pass
def get_productivity(cls, resource, should_inherit): pass
def get_resource_attributes(cls): pass
def get_resource_cost_value_attributes(cls): pass
def get_resource_cost_value_formula(cls): pass
def get_resource_quantity_attributes(cls): pass
def get_resource_time_attributes(cls): pass
def get_resource_time(cls, resource): pass
def import_resources(cls, file_path): pass
def load_cost_value_attributes(cls, cost_value): pass
def load_productivity_data(cls): pass
def load_resource_attributes(cls, resource): pass
def load_resource_properties(cls): pass
def load_resource_time_attributes(cls, resource_time): pass
def load_resources(cls): pass
@interface
class Root:
@@ -635,7 +640,7 @@ class Sequence:
def contract_task(cls, task): pass
def create_bars(cls, tasks): pass
def create_new_task_json(cls, task, json, type_map=None): pass
def create_task_tree(cls, work_schedule): pass
def load_task_tree(cls, work_schedule): pass
def create_tasks_json(cls, work_schedule=None): pass
def disable_editing_rel_sequence(cls): pass
def disable_editing_task_animation_colors(cls): pass
@@ -653,7 +658,7 @@ class Sequence:
def enable_editing_task_calendar(cls, task): pass
def enable_editing_task_sequence(cls, task): pass
def enable_editing_task_time(cls, task): pass
def enable_editing_task(cls, task): pass
def enable_editing_task_attributes(cls, task): pass
def enable_editing_work_calendar_times(cls, work_calendar): pass
def enable_editing_work_calendar(cls, work_calendar): pass
def enable_editing_work_plan_schedules(cls, work_plan): pass
@@ -700,7 +705,7 @@ class Sequence:
def highlight_task(cls, task): pass
def is_work_schedule_active(cls, work_schedule): pass
def load_lag_time_attributes(cls, lag_time): pass
def load_product_tasks(cls, product): pass
def load_product_related_tasks(cls, product): pass
def load_rel_sequence_attributes(cls, rel_sequence): pass
def load_resources(cls): pass
def load_task_animation_colors(cls): pass
+82 -4
View File
@@ -30,6 +30,8 @@ from datetime import datetime
from dateutil import parser
import ifcopenshell.util.date as ifcdateutils
import ifcopenshell.util.cost
import ifcopenshell.util.resource
import blenderbim.bim.schema
class Resource(blenderbim.core.tool.Resource):
@@ -58,6 +60,7 @@ class Resource(blenderbim.core.tool.Resource):
if not resource.HasContext:
continue
create_new_resource_li(resource, 0)
cls.load_productivity_data()
props.is_editing = True
@classmethod
@@ -299,8 +302,83 @@ class Resource(blenderbim.core.tool.Resource):
@classmethod
def get_highlighted_resource(cls):
return tool.Ifc.get().by_id(
bpy.context.scene.BIMResourceTreeProperties.resources[
bpy.context.scene.BIMResourceProperties.active_resource_index
resources = len(bpy.context.scene.BIMResourceTreeProperties.resources)
if resources and resources > bpy.context.scene.BIMResourceProperties.active_resource_index:
return tool.Ifc.get().by_id(
bpy.context.scene.BIMResourceTreeProperties.resources[
bpy.context.scene.BIMResourceProperties.active_resource_index
].ifc_definition_id
)
)
@classmethod
def clear_productivity_data(cls, props):
for duration_prop in props.quantity_consumed or []:
if duration_prop.name == "BaseQuantityConsumed":
duration_prop.years = 0
duration_prop.months = 0
duration_prop.days = 0
duration_prop.hours = 0
duration_prop.minutes = 0
duration_prop.seconds = 0
props.quantity_produced = 0
props.quantity_produced_name = ""
@classmethod
def load_productivity_data(cls):
duration_props = None
for collection_prop in bpy.context.scene.BIMResourceProductivity.quantity_consumed:
duration_props = collection_prop if collection_prop.name == "BaseQuantityConsumed" else None
break
if not duration_props:
duration_props = bpy.context.scene.BIMResourceProductivity.quantity_consumed.add()
duration_props.name = "BaseQuantityConsumed"
cls.clear_productivity_data(bpy.context.scene.BIMResourceProductivity)
current_resource = tool.Resource.get_highlighted_resource()
if current_resource:
productivity = cls.get_productivity(current_resource)
print("Resource:", current_resource, "productivity", productivity)
if productivity:
bpy.context.scene.BIMResourceProductivity.quantity_produced = (
ifcopenshell.util.resource.get_quantity_produced(productivity)
)
bpy.context.scene.BIMResourceProductivity.quantity_produced_name = (
ifcopenshell.util.resource.get_quantity_produced_name(productivity)
)
time_consumed = ifcopenshell.util.resource.get_unit_consumed(productivity)
if time_consumed:
durations_attributes = helper.parse_duration_as_blender_props(time_consumed)
duration_props.years = durations_attributes["years"]
duration_props.months = durations_attributes["months"]
duration_props.days = durations_attributes["days"]
duration_props.hours = durations_attributes["hours"]
duration_props.minutes = durations_attributes["minutes"]
duration_props.seconds = durations_attributes["seconds"]
@classmethod
def get_productivity_attributes(cls):
props = bpy.context.scene.BIMResourceProductivity
productivity = {}
if props.quantity_consumed:
productivity["BaseQuantityConsumed"] = helper.simplify_duration(
props.quantity_consumed, "ELAPSEDTIME", "BaseQuantityConsumed"
)
productivity["BaseQuantityProducedValue"] = props.quantity_produced
productivity["BaseQuantityProducedName"] = props.quantity_produced_name
return productivity
@classmethod
def get_productivity(cls, resource, should_inherit=False):
return ifcopenshell.util.resource.get_productivity(resource, should_inherit=should_inherit)
@classmethod
def edit_productivity_pset(cls, resource, attributes):
productivity = cls.get_productivity(resource)
if productivity:
pset = tool.Ifc.get().by_id(productivity["id"])
else:
pset = tool.Ifc.run("pset.add_pset", product=resource, name="EPset_Productivity")
tool.Ifc.run(
"pset.edit_pset",
pset=pset,
properties=attributes,
)
+45 -23
View File
@@ -27,6 +27,7 @@ import ifcopenshell.util.element
import ifcopenshell.util.unit
import json
import blenderbim.core.tool
import blenderbim.core
import blenderbim.tool as tool
import blenderbim.bim.helper
import blenderbim.bim.module.sequence.helper as helper
@@ -132,7 +133,7 @@ class Sequence(blenderbim.core.tool.Sequence):
props.editing_type = "TASKS"
@classmethod
def create_task_tree(cls, work_schedule):
def load_task_tree(cls, work_schedule):
bpy.context.scene.BIMTaskTreeProperties.tasks.clear()
props = bpy.context.scene.BIMWorkScheduleProperties
cls.contracted_tasks = json.loads(props.contracted_tasks)
@@ -145,25 +146,26 @@ class Sequence(blenderbim.core.tool.Sequence):
def get_sorted_tasks_ids(cls, tasks):
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 "")
if not bpy.context.scene.BIMWorkScheduleProperties.sort_column:
return task.Identification or ""
# 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(".")
if column_type == "IfcTask":
return task.Name or ""
return task.get_info(task)[name] or ""
elif column_type == "IfcTaskTime" and task.TaskTime:
return task.TaskTime.Name or ""
return task.TaskTime.get_info(task)[name]
return task.Identification or ""
def natural_sort_key(i, _nsre=re.compile("([0-9]+)")):
s = sort_keys[i]
return [int(text) if text.isdigit() else text.lower() for text in _nsre.split(s)]
sort_keys = {task.id(): get_sort_key(task) for task in tasks}
related_object_ids = sorted(sort_keys, key=natural_sort_key)
if bpy.context.scene.BIMWorkScheduleProperties.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:
related_object_ids.reverse()
return related_object_ids
@@ -328,7 +330,7 @@ class Sequence(blenderbim.core.tool.Sequence):
blenderbim.bim.helper.import_attributes2(task, props.task_attributes)
@classmethod
def enable_editing_task(cls, task):
def enable_editing_task_attributes(cls, task):
props = bpy.context.scene.BIMWorkScheduleProperties
props.active_task_id = task.id()
props.editing_task_type = "ATTRIBUTES"
@@ -407,16 +409,15 @@ class Sequence(blenderbim.core.tool.Sequence):
def load_task_resources(cls, resources):
props = bpy.context.scene.BIMWorkScheduleProperties
props.task_resources.clear()
if resources:
for resource in resources:
new = props.task_resources.add()
new.ifc_definition_id = resource.id()
new.name = resource.Name or "Unnamed"
new.schedule_usage = resource.Usage.ScheduleUsage or 1 if resource.Usage else 0
for resource in resources or []:
new = props.task_resources.add()
new.ifc_definition_id = resource.id()
new.name = resource.Name or "Unnamed"
new.schedule_usage = resource.Usage.ScheduleUsage or 1 if resource.Usage else 0
@classmethod
def load_resources(cls):
bpy.ops.bim.load_resources() # remove and refactor
blenderbim.core.resource.load_resources(tool.Resource)
@classmethod
def get_task_inputs(cls, task):
@@ -428,10 +429,22 @@ class Sequence(blenderbim.core.tool.Sequence):
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_outputs
return ifcopenshell.util.sequence.get_task_outputs(task, is_deep)
@classmethod
def are_entities_same_class(cls, entities):
if not entities:
return False
if len(entities) == 1:
return True
first = entities[0]
for entity in entities:
if entity.is_a() != first.is_a():
return False
return True
@classmethod
def get_task_resources(cls, task):
is_deep = bpy.context.scene.BIMWorkScheduleProperties.show_nested_resources
return ifcopenshell.util.sequence.get_task_resource(task, is_deep)
return ifcopenshell.util.sequence.get_task_resources(task, is_deep)
@classmethod
def load_task_inputs(cls, inputs):
@@ -732,6 +745,8 @@ class Sequence(blenderbim.core.tool.Sequence):
def remove_task_column(cls, name):
props = bpy.context.scene.BIMWorkScheduleProperties
props.columns.remove(props.columns.find(name))
if props.sort_column == name:
props.sort_column = ""
@classmethod
def set_task_sort_column(cls, column):
@@ -779,7 +794,7 @@ class Sequence(blenderbim.core.tool.Sequence):
bpy.context.scene.BIMWorkScheduleProperties.contracted_tasks = json.dumps(contracted_tasks)
expand_ancestors(parent_task)
work_schedule = cls.get_active_work_schedule()
cls.create_task_tree(work_schedule)
cls.load_task_tree(work_schedule)
cls.load_task_properties()
task_props = bpy.context.scene.BIMTaskTreeProperties
@@ -1510,9 +1525,8 @@ class Sequence(blenderbim.core.tool.Sequence):
webbrowser.open("file://" + os.path.join(bpy.context.scene.BIMProperties.data_dir, "gantt", "index.html"))
@classmethod
def load_product_tasks(cls, product):
def load_product_related_tasks(cls, product):
props = bpy.context.scene.BIMWorkScheduleProperties
props.is_task_update_enabled = False
props.product_input_tasks.clear()
props.product_output_tasks.clear()
task_inputs, task_ouputs = ifcopenshell.util.sequence.get_tasks_for_product(product)
@@ -1540,3 +1554,11 @@ class Sequence(blenderbim.core.tool.Sequence):
task_inputs = [task for task in task_inputs or [] if cls.get_work_schedule(task) == work_schedule]
task_ouputs = [task for task in task_ouputs or [] if cls.get_work_schedule(task) == work_schedule]
return bool(task_inputs or task_ouputs)
@classmethod
def is_sorting_enabled(cls):
return bpy.context.scene.BIMWorkScheduleProperties.sort_column
@classmethod
def is_sort_reversed(cls):
return bpy.context.scene.BIMWorkScheduleProperties.is_sort_reversed
@@ -225,7 +225,7 @@ Scenario: Enable editing task
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
When I press "bim.enable_editing_task(task={task})"
When I press "bim.enable_editing_task_attributes(task={task})"
Then nothing happens
Scenario: Copy task attribute
@@ -236,7 +236,7 @@ Scenario: Copy task attribute
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes[2].string_value" to "Foo"
When I set "scene.BIMTaskTreeProperties.tasks[1].is_selected" to "True"
And I press "bim.copy_task_attribute(name='Description')"
@@ -253,7 +253,7 @@ Scenario: Unassign task Successor
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
And I press "bim.assign_successor(task={nested_task_two})"
When I press "bim.unassign_successor(task={nested_task_two})"
Then nothing happens
@@ -269,7 +269,7 @@ Scenario: Edit time Lag
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
When I press "bim.assign_successor(task={nested_task_two})"
And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
When I press "bim.assign_lag_time(sequence={rel_sequence})"
@@ -290,7 +290,7 @@ Scenario: Unassign time Lag
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
When I press "bim.assign_successor(task={nested_task_two})"
And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
When I press "bim.assign_lag_time(sequence={rel_sequence})"
@@ -309,7 +309,7 @@ Scenario: Edit Sequence Relationship
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
When I press "bim.assign_successor(task={nested_task_two})"
And the variable "rel_sequence" is "IfcStore.get_file().by_type('IfcRelSequence')[0].id()"
And I press "bim.enable_editing_sequence_attributes(sequence={rel_sequence})"
@@ -340,7 +340,7 @@ Scenario: Animate the construction of a wall
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={task})"
@@ -376,7 +376,7 @@ Scenario: Animate the demolition of a wall
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "DEMOLITION"
And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={task})"
@@ -415,7 +415,7 @@ Scenario: Animate the operation of a wall
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "OPERATION"
And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={task})"
@@ -448,7 +448,7 @@ Scenario: Animate the movement of a wall
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "MOVE"
And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={task})"
@@ -539,7 +539,7 @@ Scenario: Generate Gantt Chart
And I press "bim.enable_editing_work_schedule_tasks(work_schedule={work_schedule})"
And I press "bim.add_summary_task(work_schedule={work_schedule})"
And the variable "task" is "IfcStore.get_file().by_type('IfcTask')[0].id()"
And I press "bim.enable_editing_task(task={task})"
And I press "bim.enable_editing_task_attributes(task={task})"
And I set "scene.BIMWorkScheduleProperties.task_attributes.get('PredefinedType').enum_value" to "CONSTRUCTION"
And I press "bim.edit_task"
And I press "bim.enable_editing_task_time(task={task})"
@@ -788,7 +788,7 @@ Scenario: Duplicate Task
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
And I press "bim.assign_successor(task={nested_task_two})"
When I press "bim.duplicate_task(task={task})"
Then nothing happens
@@ -804,7 +804,7 @@ Scenario: Duplicate Task and edit sequence Relationship
And the variable "nested_task_one" is "IfcStore.get_file().by_type('IfcTask')[1].id()"
When I press "bim.add_task(task={task})"
And the variable "nested_task_two" is "IfcStore.get_file().by_type('IfcTask')[2].id()"
And I press "bim.enable_editing_task(task={nested_task_one})"
And I press "bim.enable_editing_task_attributes(task={nested_task_one})"
And I press "bim.assign_successor(task={nested_task_two})"
And I press "bim.duplicate_task(task={task})"
When I press "bim.enable_editing_task_sequence(task={nested_task_one})"
@@ -0,0 +1,102 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of IfcOpenShell.
#
# IfcOpenShell is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# IfcOpenShell 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.element
import ifcopenshell.util.date
def get_productivity(resource, should_inherit=True):
productivity = ifcopenshell.util.element.get_psets(
resource
).get("EPset_Productivity", None)
if should_inherit and not productivity:
# Proposal for Schema - If instance doesn't have any productivity, inherit it's parent's productivity
if not resource.Nests:
return None
else:
parent_resource = resource.Nests[0].RelatingObject
productivity = ifcopenshell.util.element.get_psets(
parent_resource
).get("EPset_Productivity", None)
return productivity
def get_unit_consumed(productivity):
duration = productivity.get("BaseQuantityConsumed", None)
if not duration:
return
return ifcopenshell.util.date.ifc2datetime(duration)
def get_quantity_produced(productivity):
if not productivity:
return 0
return productivity.get("BaseQuantityProducedValue", 0)
def get_quantity_produced_name(productivity):
if not productivity:
return ""
return productivity.get("BaseQuantityProducedName", "")
def get_total_quantity_produced(resource, quantity_name_in_process):
def get_product_quantity(product, quantity_name):
psets = ifcopenshell.util.element.get_psets(product)
for pset in psets.values():
for name, value in pset.items():
if name == quantity_name:
return float(value)
total = 0
products = get_parametric_resource_products(resource)
if quantity_name_in_process == "Count":
total = len(products)
else:
for product in products:
total += get_product_quantity(product, quantity_name_in_process)
return total
def get_parametric_resource_products(resource):
products = []
for rel in resource.HasAssignments or []:
if not rel.is_a("IfcRelAssignsToProcess"):
continue
for rel2 in rel.RelatingProcess.HasAssignments or []:
if not rel2.is_a("IfcRelAssignsToProduct"):
continue
products.append(rel2.RelatingProduct)
return products
def get_resource_required_work(resource):
productivity = get_productivity(resource)
if productivity:
quantity_produced = get_quantity_produced(productivity)
time_consumed = get_unit_consumed(productivity)
quantity_name_in_process = get_quantity_produced_name(productivity)
total_quantity_to_produce = get_total_quantity_produced(resource, quantity_name_in_process)
if not time_consumed or not quantity_produced or not total_quantity_to_produce:
return
iso_string = ""
if "T" in productivity.get("BaseQuantityConsumed", ""):
seconds = (time_consumed.days * 24 * 60 * 60) + time_consumed.seconds
productivity_ratio = seconds / quantity_produced
required_work = total_quantity_to_produce * productivity_ratio
iso_string = f"PT{required_work / 60 / 60}H"
else:
days = time_consumed.days + (time_consumed.seconds / (24 * 60 * 60))
productivity_ratio = days / quantity_produced
required_work = total_quantity_to_produce * productivity_ratio
iso_string = f"P{required_work}D"
return iso_string