diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 8fbc13bdb3..45f1f6b15b 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -17,13 +17,21 @@ classes = ( operator.EnableEditingTasks, operator.DisableEditingWorkSchedule, operator.DisableTaskEditingUI, - operator.LoadWorkCalendars, - operator.DisableWorkCalendarEditingUI, operator.AddWorkCalendar, operator.EditWorkCalendar, + operator.EditWorkTime, operator.RemoveWorkCalendar, + operator.RemoveWorkTime, + operator.UnassignRecurrencePattern, + operator.RemoveTimePeriod, operator.EnableEditingWorkCalendar, + operator.EnableEditingWorkTime, + operator.EnableEditingWorkCalendarTimes, operator.DisableEditingWorkCalendar, + operator.DisableEditingWorkTime, + operator.AddWorkTime, + operator.AssignRecurrencePattern, + operator.AddTimePeriod, operator.AddTask, operator.AddSummaryTask, operator.ExpandTask, @@ -54,7 +62,6 @@ classes = ( ui.BIM_PT_work_plans, ui.BIM_PT_work_schedules, ui.BIM_PT_work_calendars, - ui.BIM_UL_work_calendars, ui.BIM_UL_tasks, ) diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 283621494c..6cb69430d0 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -747,32 +747,6 @@ class GenerateGanttChart(bpy.types.Operator): self.create_new_task_json(task_id) -class LoadWorkCalendars(bpy.types.Operator): - bl_idname = "bim.load_work_calendars" - bl_label = "Load Work Calendars" - - def execute(self, context): - props = context.scene.BIMWorkCalendarProperties - while len(props.work_calendars) > 0: - props.work_calendars.remove(0) - for ifc_definition_id, work_calendar in Data.work_calendars.items(): - new = props.work_calendars.add() - new.ifc_definition_id = ifc_definition_id - new.name = work_calendar["Name"] or "Unnamed" - props.is_editing = True - bpy.ops.bim.disable_editing_work_calendar() - return {"FINISHED"} - - -class DisableWorkCalendarEditingUI(bpy.types.Operator): - bl_idname = "bim.disable_work_calendar_editing_ui" - bl_label = "Disable WorkCalendar Editing UI" - - def execute(self, context): - context.scene.BIMWorkCalendarProperties.is_editing = False - return {"FINISHED"} - - class AddWorkCalendar(bpy.types.Operator): bl_idname = "bim.add_work_calendar" bl_label = "Add Work Calendar" @@ -780,7 +754,6 @@ class AddWorkCalendar(bpy.types.Operator): def execute(self, context): ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file()) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_work_calendars() return {"FINISHED"} @@ -806,7 +779,7 @@ class EditWorkCalendar(bpy.types.Operator): **{"work_calendar": self.file.by_id(props.active_work_calendar_id), "attributes": attributes}, ) Data.load(IfcStore.get_file()) - bpy.ops.bim.load_work_calendars() + bpy.ops.bim.disable_editing_work_calendar() return {"FINISHED"} @@ -821,13 +794,12 @@ class RemoveWorkCalendar(bpy.types.Operator): "sequence.remove_work_calendar", self.file, **{"work_calendar": self.file.by_id(self.work_calendar)} ) Data.load(self.file) - bpy.ops.bim.load_work_calendars() return {"FINISHED"} class EnableEditingWorkCalendar(bpy.types.Operator): bl_idname = "bim.enable_editing_work_calendar" - bl_label = "Enable Editing Work Plan" + bl_label = "Enable Editing Work Calendar" work_calendar: bpy.props.IntProperty() def execute(self, context): @@ -853,6 +825,7 @@ class EnableEditingWorkCalendar(bpy.types.Operator): if data[attribute.name()]: new.enum_value = data[attribute.name()] props.active_work_calendar_id = self.work_calendar + props.is_editing = "ATTRIBUTES" return {"FINISHED"} @@ -884,3 +857,182 @@ class ImportP6(bpy.types.Operator, ImportHelper): Data.load(IfcStore.get_file()) print("Import finished in {:.2f} seconds".format(time.time() - start)) return {"FINISHED"} + + +class EnableEditingWorkCalendarTimes(bpy.types.Operator): + bl_idname = "bim.enable_editing_work_calendar_times" + bl_label = "Enable Editing Work Calendar Times" + work_calendar: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkCalendarProperties + props.active_work_calendar_id = self.work_calendar + props.is_editing = "WORKTIMES" + return {"FINISHED"} + + +class AddWorkTime(bpy.types.Operator): + bl_idname = "bim.add_work_time" + bl_label = "Add Work Time" + work_calendar: bpy.props.IntProperty() + time_type: bpy.props.StringProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.add_work_time", + self.file, + **{"work_calendar": self.file.by_id(self.work_calendar), "time_type": self.time_type}, + ) + Data.load(IfcStore.get_file()) + return {"FINISHED"} + + +class EnableEditingWorkTime(bpy.types.Operator): + bl_idname = "bim.enable_editing_work_time" + bl_label = "Enable Editing Work Time" + work_time: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkCalendarProperties + while len(props.work_time_attributes) > 0: + props.work_time_attributes.remove(0) + + data = Data.work_times[self.work_time] + + for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkTime").all_attributes(): + data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) + if data_type == "entity": + continue + new = props.work_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 attribute.name() in ["Start", "Finish"]: + new.string_value = "" if new.is_null else data[attribute.name()].isoformat() + elif data_type == "string": + new.string_value = "" 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_work_time_id = self.work_time + return {"FINISHED"} + + +class DisableEditingWorkTime(bpy.types.Operator): + bl_idname = "bim.disable_editing_work_time" + bl_label = "Disable Editing Work Time" + + def execute(self, context): + context.scene.BIMWorkCalendarProperties.active_work_time_id = 0 + return {"FINISHED"} + + +class EditWorkTime(bpy.types.Operator): + bl_idname = "bim.edit_work_time" + bl_label = "Edit Work Time" + + def execute(self, context): + props = context.scene.BIMWorkCalendarProperties + attributes = {} + for attribute in props.work_time_attributes: + if attribute.is_null: + attributes[attribute.name] = None + else: + if attribute.data_type == "string": + attributes[attribute.name] = attribute.string_value + elif attribute.data_type == "enum": + attributes[attribute.name] = attribute.enum_value + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.edit_work_time", + self.file, + **{"work_time": self.file.by_id(props.active_work_time_id), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_work_time() + return {"FINISHED"} + + +class RemoveWorkTime(bpy.types.Operator): + bl_idname = "bim.remove_work_time" + bl_label = "Remove Work Plan" + work_time: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run("sequence.remove_work_time", self.file, **{"work_time": self.file.by_id(self.work_time)}) + Data.load(self.file) + return {"FINISHED"} + + +class AssignRecurrencePattern(bpy.types.Operator): + bl_idname = "bim.assign_recurrence_pattern" + bl_label = "Assign Recurrence Pattern" + work_time: bpy.props.IntProperty() + recurrence_type: bpy.props.StringProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.assign_recurrence_pattern", + self.file, + **{"parent": self.file.by_id(self.work_time), "recurrence_type": self.recurrence_type}, + ) + Data.load(IfcStore.get_file()) + return {"FINISHED"} + + +class UnassignRecurrencePattern(bpy.types.Operator): + bl_idname = "bim.unassign_recurrence_pattern" + bl_label = "Unassign Recurrence Pattern" + recurrence_pattern: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.unassign_recurrence_pattern", + self.file, + **{"recurrence_pattern": self.file.by_id(self.recurrence_pattern)}, + ) + Data.load(self.file) + return {"FINISHED"} + + +class AddTimePeriod(bpy.types.Operator): + bl_idname = "bim.add_time_period" + bl_label = "Add Time Period" + recurrence_pattern: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMWorkCalendarProperties + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.add_time_period", + self.file, + **{ + "recurrence_pattern": self.file.by_id(self.recurrence_pattern), + "start_time": self.props.start_time, + "end_time": self.props.start_time, + }, + ) + Data.load(IfcStore.get_file()) + return {"FINISHED"} + + +class RemoveTimePeriod(bpy.types.Operator): + bl_idname = "bim.remove_time_period" + bl_label = "Remove Time Period" + time_period: bpy.props.IntProperty() + + def execute(self, context): + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "sequence.remove_time_period", + self.file, + **{"time_period": self.file.by_id(self.time_period)}, + ) + 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 cd8a589eb7..15a25f33d6 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -170,7 +170,30 @@ class WorkCalendar(PropertyGroup): class BIMWorkCalendarProperties(PropertyGroup): work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute) - is_editing: BoolProperty(name="Is Editing", default=False) + work_time_attributes: CollectionProperty(name="Work Time Attributes", type=Attribute) + is_editing: StringProperty(name="Is Editing") work_calendars: CollectionProperty(name="Work Calendar", type=WorkCalendar) - active_work_calendar_index: IntProperty(name="Active Work Calendar Index") active_work_calendar_id: IntProperty(name="Active Work Calendar Id") + active_work_time_id: IntProperty(name="Active Work Time Id") + weekday_component_monday: BoolProperty(name="M") + weekday_component_tuesday: BoolProperty(name="T") + weekday_component_wednesday: BoolProperty(name="W") + weekday_component_thursday: BoolProperty(name="T") + weekday_component_friday: BoolProperty(name="F") + weekday_component_saturday: BoolProperty(name="S") + weekday_component_sunday: BoolProperty(name="S") + dummy_bool: BoolProperty(name="Active Work Calendar Id") + dummy_int: IntProperty(name="Active Work Calendar Id") + recurrence_types: EnumProperty(items=[ + ("DAILY", "Daily", "e.g. Every day"), + ("WEEKLY", "Weekly", "e.g. Every Friday"), + ("MONTHLY_BY_DAY_OF_MONTH", "Monthly on Specified Date", "e.g. Every 2nd of each Month"), + ("MONTHLY_BY_POSITION", "Monthly on Specified Weekday", "e.g. Every 1st Friday of each Month"), + # https://forums.buildingsmart.org/t/what-does-by-day-count-and-by-weekday-count-mean-in-ifcrecurrencetypeenum/3571 + # ("BY_DAY_COUNT", "", ""), + # ("BY_WEEKDAY_COUNT", "", ""), + ("YEARLY_BY_DAY_OF_MONTH", "Yearly on Specified Date", "e.g. Every 2nd of October"), + ("YEARLY_BY_POSITION", "Yearly on Specified Weekday", "e.g. Every 1st Friday of October"), + ], name="Recurrence Types") + start_time: StringProperty(name="Start Time") + end_time: StringProperty(name="End Time") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index c4381f5ba0..a395eedb1c 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -266,29 +266,69 @@ class BIM_PT_work_calendars(Panel): if not Data.is_loaded: Data.load(IfcStore.get_file()) self.props = context.scene.BIMWorkCalendarProperties + + row = self.layout.row() + row.operator("bim.add_work_calendar", icon="ADD") + + for work_calendar_id, work_calendar in Data.work_calendars.items(): + self.draw_work_calendar_ui(work_calendar_id, work_calendar) + + def draw_work_calendar_ui(self, work_calendar_id, work_calendar): row = self.layout.row(align=True) - row.label(text="{} Work Calendar Found".format(len(Data.work_calendars)), icon="TEXT") - if self.props.is_editing: - row.operator("bim.add_work_calendar", text="", icon="ADD") - row.operator("bim.disable_work_calendar_editing_ui", text="", icon="CHECKMARK") + row.label(text=work_calendar["Name"] or "Unnamed", icon="VIEW_ORTHO") + if self.props.active_work_calendar_id == work_calendar_id: + row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_work_calendar", text="", icon="CANCEL") + elif self.props.active_work_calendar_id: + row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = work_calendar_id else: - row.operator("bim.load_work_calendars", text="", icon="GREASEPENCIL") + op = row.operator("bim.enable_editing_work_calendar_times", text="", icon="MESH_GRID") + op.work_calendar = work_calendar_id + op = row.operator("bim.enable_editing_work_calendar", text="", icon="GREASEPENCIL") + op.work_calendar = work_calendar_id + row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = work_calendar_id - if self.props.is_editing: - self.layout.template_list( - "BIM_UL_work_calendars", - "", - self.props, - "work_calendars", - self.props, - "active_work_calendar_index", - ) + if self.props.active_work_calendar_id == work_calendar_id: + if self.props.is_editing == "ATTRIBUTES": + self.draw_editable_ui() + elif self.props.is_editing == "WORKTIMES": + self.draw_work_times_ui(work_calendar_id, work_calendar) - if self.props.active_work_calendar_id: - self.draw_editable_ui(context) + def draw_work_times_ui(self, work_calendar_id, work_calendar): + row = self.layout.row(align=True) + op = row.operator("bim.add_work_time", text="Add Work Time", icon="ADD") + op.work_calendar = work_calendar_id + op.time_type = "WorkingTimes" + op = row.operator("bim.add_work_time", text="Add Exception Time", icon="ADD") + op.work_calendar = work_calendar_id + op.time_type = "ExceptionTimes" - def draw_editable_ui(self, context): - for attribute in self.props.work_calendar_attributes: + for work_time_id in work_calendar["WorkingTimes"]: + self.draw_work_time_ui(Data.work_times[work_time_id], time_type="WorkingTimes") + + for work_time_id in work_calendar["ExceptionTimes"]: + self.draw_work_time_ui(Data.work_times[work_time_id], time_type="ExceptionTimes") + + def draw_work_time_ui(self, work_time, time_type): + row = self.layout.row(align=True) + row.label(text=work_time["Name"] or "Unnamed", icon="MESH_GRID" if time_type == "WorkingTimes" else "LIGHTPROBE_GRID") + if self.props.active_work_time_id == work_time["id"]: + row.operator("bim.edit_work_time", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_work_time", text="", icon="CANCEL") + elif self.props.active_work_time_id: + op = row.operator("bim.remove_work_time", text="", icon="X") + op.work_time = work_time["id"] + else: + op = row.operator("bim.enable_editing_work_time", text="", icon="GREASEPENCIL") + op.work_time = work_time["id"] + op = row.operator("bim.remove_work_time", text="", icon="X") + op.work_time = work_time["id"] + + if self.props.active_work_time_id == work_time["id"]: + self.draw_editable_work_time_ui(work_time) + + def draw_editable_work_time_ui(self, work_time): + for attribute in self.props.work_time_attributes: row = self.layout.row(align=True) if attribute.data_type == "string": row.prop(attribute, "string_value", text=attribute.name) @@ -297,18 +337,59 @@ class BIM_PT_work_calendars(Panel): if attribute.is_optional: row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") + if work_time["RecurrencePattern"]: + self.draw_editable_recurrence_pattern_ui(Data.recurrence_patterns[work_time["RecurrencePattern"]]) + else: + row = self.layout.row(align=True) + row.prop(self.props, "recurrence_types", icon="RECOVER_LAST", text="") + op = row.operator("bim.assign_recurrence_pattern", icon="ADD", text="") + op.work_time = work_time["id"] + op.recurrence_type = self.props.recurrence_types -class BIM_UL_work_calendars(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - if item: - row = layout.row(align=True) - row.label(text=item.name) - if context.scene.BIMWorkCalendarProperties.active_work_calendar_id == item.ifc_definition_id: - row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_work_calendar", text="", icon="X") - elif context.scene.BIMWorkCalendarProperties.active_work_calendar_id: - row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id - else: - op = row.operator("bim.enable_editing_work_calendar", text="", icon="GREASEPENCIL") - op.work_calendar = item.ifc_definition_id - row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id + def draw_editable_recurrence_pattern_ui(self, recurrence_pattern): + box = self.layout.box() + row = box.row(align=True) + row.label(text=recurrence_pattern["RecurrenceType"], icon="RECOVER_LAST") + op = row.operator("bim.unassign_recurrence_pattern", text="", icon="X") + op.recurrence_pattern = recurrence_pattern["id"] + + row = box.row(align=True) + row.prop(self.props, "start_time", text="") + row.prop(self.props, "end_time", text="") + op = row.operator("bim.add_time_period", text="", icon="ADD") + op.recurrence_pattern = recurrence_pattern["id"] + + for time_period_id in recurrence_pattern["TimePeriods"]: + time_period = Data.time_periods[time_period_id] + row = box.row(align=True) + row.label(text="{} - {}".format(time_period["StartTime"], time_period["EndTime"]), icon="TIME") + op = row.operator("bim.remove_time_period", text="", icon="X") + op.time_period = time_period_id + + if recurrence_pattern["RecurrenceType"] == "DAILY": + pass # No need to show any custom UI + if recurrence_pattern["RecurrenceType"] == "WEEKLY": + row = box.row(align=True) + row.prop(self.props, "weekday_component_monday", text="M") + row.prop(self.props, "weekday_component_tuesday", text="T") + row.prop(self.props, "weekday_component_wednesday", text="W") + row.prop(self.props, "weekday_component_thursday", text="T") + row.prop(self.props, "weekday_component_friday", text="F") + row.prop(self.props, "weekday_component_saturday", text="S") + row.prop(self.props, "weekday_component_sunday", text="S") + + row = box.row(align=True) + row.prop(self.props, "dummy_int", text="Recurrence Interval") + row = box.row(align=True) + row.prop(self.props, "dummy_int", text="Occurs N Times") + + + def draw_editable_ui(self): + for attribute in self.props.work_calendar_attributes: + row = self.layout.row(align=True) + if attribute.data_type == "string": + row.prop(attribute, "string_value", text=attribute.name) + elif attribute.data_type == "enum": + row.prop(attribute, "enum_value", text=attribute.name) + if attribute.is_optional: + row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py new file mode 100644 index 0000000000..a323a6a553 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_time_period.py @@ -0,0 +1,25 @@ +import ifcopenshell.api +import ifcopenshell.util.date +from datetime import datetime +from datetime import timedelta + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "recurrence_pattern": None, + "start_time": None, + "end_time": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + time_period = self.file.create_entity("IfcTimePeriod") + time_period.StartTime = ifcopenshell.util.date.datetime2ifc(self.settings["start_time"], "IfcTime") + time_period.EndTime = ifcopenshell.util.date.datetime2ifc(self.settings["end_time"], "IfcTime") + time_periods = list(self.settings["recurrence_pattern"].TimePeriods or []) + time_periods.append(time_period) + self.settings["recurrence_pattern"].TimePeriods = time_periods + return time_period diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py index 40589b5098..c05eb3085e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py @@ -1,17 +1,17 @@ class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {"work_calendar": None, "type": "WorkingTimes", "name": None} + self.settings = {"work_calendar": None, "time_type": None} for key, value in settings.items(): self.settings[key] = value def execute(self): - work_time = self.file.create_entity("IfcWorkTime", **{"Name": self.settings["name"]}) - if self.settings["type"] == "WorkingTimes": + work_time = self.file.create_entity("IfcWorkTime") + if self.settings["time_type"] == "WorkingTimes": working_times = list(self.settings["work_calendar"].WorkingTimes or []) working_times.append(work_time) self.settings["work_calendar"].WorkingTimes = working_times - elif self.settings["type"] == "ExceptionTimes": + elif self.settings["time_type"] == "ExceptionTimes": exception_times = list(self.settings["work_calendar"].ExceptionTimes or []) exception_times.append(work_time) self.settings["work_calendar"].ExceptionTimes = exception_times diff --git a/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py similarity index 76% rename from src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py rename to src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py index ec6883dc7d..9cbd7f1669 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_recurrence_pattern.py @@ -8,8 +8,11 @@ class Usecase: def execute(self): recurrence = self.file.createIfcRecurrencePattern(self.settings["recurrence_type"]) - if self.settings["parent"].is_a("IfcWorkTime") and self.settings["parent"].RecurrencePattern: - if len(self.file.get_inverse(self.settings["parent"].RecurrencePattern)) == 1: + if self.settings["parent"].is_a("IfcWorkTime"): + if ( + self.settings["parent"].RecurrencePattern + and len(self.file.get_inverse(self.settings["parent"].RecurrencePattern)) == 1 + ): self.file.remove(self.settings["parent"].RecurrencePattern) self.settings["parent"].RecurrencePattern = recurrence elif self.settings["parent"].is_a("IfcTaskTimeRecurring"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py index 0333c524b3..5f7ad96676 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py @@ -5,6 +5,10 @@ class Data: is_loaded = False work_plans = {} work_schedules = {} + work_calendars = {} + work_times = {} + recurrence_patterns = {} + time_periods = {} tasks = {} task_times = {} @@ -14,6 +18,9 @@ class Data: cls.work_plans = {} cls.work_schedules = {} cls.work_calendars = {} + cls.work_times = {} + cls.recurrence_patterns = {} + cls.time_periods = {} cls.tasks = {} cls.task_times = {} @@ -25,6 +32,9 @@ class Data: cls.load_work_plans() cls.load_work_schedules() cls.load_work_calendars() + cls.load_work_times() + cls.load_recurrence_patterns() + cls.load_time_periods() cls.load_tasks() cls.load_task_times() cls.is_loaded = True @@ -71,10 +81,37 @@ class Data: for work_calendar in cls._file.by_type("IfcWorkCalendar"): data = work_calendar.get_info() del data["OwnerHistory"] - del data["WorkingTimes"] - del data["ExceptionTimes"] + data["WorkingTimes"] = [t.id() for t in work_calendar.WorkingTimes or []] + data["ExceptionTimes"] = [t.id() for t in work_calendar.ExceptionTimes or []] cls.work_calendars[work_calendar.id()] = data + @classmethod + def load_work_times(cls): + cls.work_times = {} + for work_time in cls._file.by_type("IfcWorkTime"): + data = work_time.get_info() + data["Start"] = ifcopenshell.util.date.ifc2datetime(data["Start"]) if data["Start"] else None + data["Finish"] = ifcopenshell.util.date.ifc2datetime(data["Finish"]) if data["Finish"] else None + data["RecurrencePattern"] = work_time.RecurrencePattern.id() if work_time.RecurrencePattern else None + cls.work_times[work_time.id()] = data + + @classmethod + def load_recurrence_patterns(cls): + cls.recurrence_patterns = {} + for recurrence_pattern in cls._file.by_type("IfcRecurrencePattern"): + data = recurrence_pattern.get_info() + data["TimePeriods"] = [t.id() for t in recurrence_pattern.TimePeriods or []] + cls.recurrence_patterns[recurrence_pattern.id()] = data + + @classmethod + def load_time_periods(cls): + cls.time_periods = {} + for time_period in cls._file.by_type("IfcTimePeriod"): + cls.time_periods[time_period.id()] = { + "StartTime": ifcopenshell.util.date.ifc2datetime(time_period.StartTime), + "EndTime": ifcopenshell.util.date.ifc2datetime(time_period.EndTime), + } + @classmethod def load_tasks(cls): cls.tasks = {} diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py index ee57a173bc..f7691ad4e3 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -13,9 +13,14 @@ class Usecase: if name == "TimePeriods" and value: periods = [] for period in value: - periods.append(self.file.create_entity("IfcTimePeriod", **{ - "StartTime": ifcopenshell.util.date.datetime2ifc(period[0]), - "EndTime": ifcopenshell.util.date.datetime2ifc(period[1]) - })) + periods.append( + self.file.create_entity( + "IfcTimePeriod", + **{ + "StartTime": ifcopenshell.util.date.datetime2ifc(period[0]), + "EndTime": ifcopenshell.util.date.datetime2ifc(period[1]), + }, + ) + ) value = periods setattr(self.settings["recurrence_pattern"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py index 057cec6aba..c2952b704a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -10,6 +10,6 @@ class Usecase: def execute(self): for name, value in self.settings["attributes"].items(): - if name in ["Start", "Finish"]: + if value and name in ["Start", "Finish"]: value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") setattr(self.settings["work_time"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py new file mode 100644 index 0000000000..4b0d59d7aa --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_time_period.py @@ -0,0 +1,12 @@ +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"time_period": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.file.remove(self.settings["time_period"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index ee3c89327a..6950012bd9 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -1,3 +1,6 @@ +import ifcopenshell + + class Usecase: def __init__(self, file, **settings): self.file = file diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py new file mode 100644 index 0000000000..10562c1840 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_time.py @@ -0,0 +1,9 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"work_time": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.file.remove(self.settings["work_time"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py new file mode 100644 index 0000000000..23ec89376a --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/unassign_recurrence_pattern.py @@ -0,0 +1,12 @@ +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"recurrence_pattern": None} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.file.remove(self.settings["recurrence_pattern"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/time/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/time/add_work_time.py deleted file mode 100644 index e69de29bb2..0000000000