diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 3d3a176149..c588617c4d 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -36,6 +36,9 @@ classes = ( operator.AssignSuccessor, operator.UnassignPredecessor, operator.UnassignSuccessor, + operator.AddTaskTime, + operator.EnableEditingTaskTime, + operator.DisableEditingTaskTime, prop.WorkPlan, prop.BIMWorkPlanProperties, prop.Task, diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 23969b1fc0..091da38c15 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -473,6 +473,49 @@ class RemoveTask(bpy.types.Operator): return {"FINISHED"} +class EnableEditingTaskTime(bpy.types.Operator): + bl_idname = "bim.enable_editing_task_time" + bl_label = "Enable Editing Task" + task: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + self.file = IfcStore.get_file() + while len(props.task_time_attributes) > 0: + props.task_time_attributes.remove(0) + + if self.file.by_id(self.task).TaskTime: + task_time_id = self.file.by_id(self.task).TaskTime.id() + else: + task_time = ifcopenshell.api.run("sequence.add_task_time", self.file) + self.file.by_id(self.task).TaskTime = task_time + task_time_id = task_time.id() + Data.load(self.file) + data = Data.task_times[task_time_id] + + for attribute in IfcStore.get_schema().declaration_by_name("IfcTaskTime").all_attributes(): + data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) + if data_type == "entity": + continue + new = props.task_time_attributes.add() + new.name = attribute.name() + new.is_null = data[attribute.name()] is None + new.is_optional = attribute.optional() + new.data_type = data_type + if data_type == "string": + new.string_value = "" if new.is_null else data[attribute.name()] + elif data_type == "boolean": + new.bool_value = False if new.is_null else data[attribute.name()] + elif data_type == "integer": + new.int_value = 0 if new.is_null else data[attribute.name()] + elif data_type == "enum": + new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute)) + if data[attribute.name()]: + new.enum_value = data[attribute.name()] + props.active_task_time_id = IfcStore.get_file().by_id(self.task).TaskTime.id() + return {"FINISHED"} + + class EnableEditingTask(bpy.types.Operator): bl_idname = "bim.enable_editing_task" bl_label = "Enable Editing Task" @@ -508,6 +551,15 @@ class EnableEditingTask(bpy.types.Operator): return {"FINISHED"} +class DisableEditingTaskTime(bpy.types.Operator): + bl_idname = "bim.disable_editing_task_time" + bl_label = "Disable Editing Task Time" + + def execute(self, context): + context.scene.BIMWorkScheduleProperties.active_task_time_id = 0 + return {"FINISHED"} + + class DisableEditingTask(bpy.types.Operator): bl_idname = "bim.disable_editing_task" bl_label = "Disable Editing Task" @@ -613,8 +665,25 @@ class UnassignSuccessor(bpy.types.Operator): ifcopenshell.api.run( "sequence.unassign_sequence", self.file, - relating_process=IfcStore.get_file().by_id(props.active_task_id), - related_process=IfcStore.get_file().by_id(self.task), + relating_process=self.file.by_id(props.active_task_id), + related_process=self.file.by_id(self.task), + ) + Data.load(self.file) + return {"FINISHED"} + + +class AddTaskTime(bpy.types.Operator): + bl_idname = "bim.add_task_time" + bl_label = "Add Task Time" + task: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkScheduleProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.add_task_time", + self.file, + task = self.file.by_id(self.task), ) Data.load(self.file) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index a4d87d6016..b3bf062ca5 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -47,6 +47,20 @@ def updateTaskIdentification(self, context): attribute = context.scene.BIMWorkScheduleProperties.task_attributes.get("Identification") attribute.string_value = self.identification +def updateTaskTimeScheduleStart(self, context): + if self.schedule_start == "X": + return + self.file = IfcStore.get_file() + props = context.scene.BIMWorkScheduleProperties + ifcopenshell.api.run( + "sequence.edit_task_time", + self.file, + **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"ScheduleStart": self.schedule_start}} + ) + Data.load(IfcStore.get_file()) + if props.active_task_id == self.ifc_definition_id: + attribute = context.scene.BIMWorkScheduleProperties.task_attributes.get("ScheduleStart") + attribute.string_value = self.schedule_start class Task(PropertyGroup): name: StringProperty(name="Name", update=updateTaskName) @@ -55,7 +69,9 @@ class Task(PropertyGroup): has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") level_index: IntProperty(name="Level Index") - + schedule_duration: StringProperty(name="Duration") + schedule_start: StringProperty(name="Schedule Start ", update=updateTaskTimeScheduleStart) + schedule_finish: StringProperty(name="Schedule Finish ") class WorkPlan(PropertyGroup): name: StringProperty(name="Name") @@ -79,6 +95,8 @@ class BIMWorkScheduleProperties(PropertyGroup): active_task_index: IntProperty(name="Active Task Index") active_task_id: IntProperty(name="Active Task Id") task_attributes: CollectionProperty(name="Task Attributes", type=Attribute) + active_task_time_id: IntProperty(name="Active Task Id") + task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute) contracted_tasks: StringProperty(name="Contracted Task Items", default="[]") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 25d90b949f..b3274bd4fc 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -170,6 +170,10 @@ class BIM_UL_tasks(UIList): row.label(text="", icon="DOT") row.prop(item, "identification", emboss=False, text="") row.prop(item, "name", emboss=False, text="") + row.prop(item, "schedule_start", emboss=False, text="Start Time") + row.prop(item, "schedule_finish", emboss=False, text="Finish Time") + row.prop(item, "schedule_duration", emboss=False, text="Duration") + if props.active_task_id == item.ifc_definition_id: row.operator("bim.edit_task", text="", icon="CHECKMARK") row.operator("bim.disable_editing_task", text="", icon="CANCEL") @@ -187,6 +191,7 @@ class BIM_UL_tasks(UIList): row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id else: + row.operator("bim.enable_editing_task_time", text="", icon="TIME").task = item.ifc_definition_id row.operator("bim.add_task", text="", icon="ADD").task = item.ifc_definition_id row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id row.operator("bim.remove_task", text="", icon="X").task = item.ifc_definition_id diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py new file mode 100644 index 0000000000..e37f60ebc4 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_task_time.py @@ -0,0 +1,37 @@ +import ifcopenshell.util.date +from datetime import datetime +from datetime import timedelta + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "task": None, + "name": "Unnamed", + "duration_type": "NOTDEFINED", + "schedule_duration": None, + "schedule_start_time": datetime.now(), + "schedule_finish_time": datetime.now() + timedelta(days=5), + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + task_time = self.file.create_entity("IfcTaskTime", **{"Name": self.settings["name"]}) + task_time.DurationType = self.settings["duration_type"] + task_time.ScheduleStart = ifcopenshell.util.date.datetime2ifc(self.settings["schedule_start_time"], "IfcDateTime") + duration = self.settings["schedule_duration"] + if duration: + task_time.ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration, "IfcTime") + task_time.ScheduleFinish = ifcopenshell.util.date.datetime2ifc( + self.settings["schedule_start"] + duration, + "IfcDateTime" + ) + else: + duration = self.settings["schedule_finish_time"] - self.settings["schedule_start_time"] + # task_time.ScheduleDuration = ifcopenshell.util.date.datetime2ifc(duration.days, "IfcTime") + task_time.ScheduleFinish = ifcopenshell.util.date.datetime2ifc(self.settings["schedule_finish_time"], "IfcDateTime") + task = self.settings["task"] + if task: + task.TaskTime = task_time + return task_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py index 3aa298bafc..07f923b10a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py @@ -6,6 +6,7 @@ class Data: work_plans = {} work_schedules = {} tasks = {} + task_times = {} @classmethod def purge(cls): @@ -24,6 +25,7 @@ class Data: cls.load_work_schedules() cls.load_work_calendars() cls.load_tasks() + cls.load_task_times() cls.is_loaded = True @classmethod @@ -78,8 +80,27 @@ class Data: data["RelatedObjects"] = [] data["IsPredecessorTo"] = [] data["IsSuccessorFrom"] = [] + if task.TaskTime: + data["TaskTime"] = task.TaskTime + data["ScheduleStart"] = task.TaskTime.ScheduleStart + data["ScheduleFinish"] = task.TaskTime.ScheduleFinish + data["ScheduleDuration"] = task.TaskTime.ScheduleDuration for rel in task.IsNestedBy: [data["RelatedObjects"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcTask")] [data["IsPredecessorTo"].append(rel.RelatedProcess.id()) for rel in task.IsPredecessorTo or []] [data["IsSuccessorFrom"].append(rel.RelatingProcess.id()) for rel in task.IsSuccessorFrom or []] cls.tasks[task.id()] = data + + @classmethod + def load_task_times(cls): + cls.task_times = {} + for task_time in cls._file.by_type("IfcTaskTime"): + data = task_time.get_info() + data["ScheduleStart"] = ifcopenshell.util.date.ifc2datetime(data["ScheduleStart"]) + data["ScheduleFinish"] = ifcopenshell.util.date.ifc2datetime(data["ScheduleFinish"]) + data["EarlyStart"] = ifcopenshell.util.date.ifc2datetime(data["EarlyStart"]) + data["EarlyFinish"] = ifcopenshell.util.date.ifc2datetime(data["EarlyFinish"]) + data["LateStart"] = ifcopenshell.util.date.ifc2datetime(data["LateStart"]) + data["LateFinish"] = ifcopenshell.util.date.ifc2datetime(data["LateFinish"]) + data["EarlyFinish"] = ifcopenshell.util.date.ifc2datetime(data["EarlyFinish"]) + cls.task_times[task_time.id()] = data diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py new file mode 100644 index 0000000000..140dff656e --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_task_time.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"task": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + setattr(self.settings["task"].TaskTime, name, value)