From ae0789e4eccefbabc1c27b2e0546d7b25183847c Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 7 Apr 2021 13:15:16 +1000 Subject: [PATCH] New assign / unassign declaration usecase, minor code & formatting cleanup --- .../bim/module/sequence/operator.py | 70 ++++++++++++------- .../blenderbim/bim/module/sequence/prop.py | 2 +- .../blenderbim/bim/module/sequence/ui.py | 20 ++++-- .../api/aggregate/assign_object.py | 6 +- .../api/constraint/unassign_constraint.py | 3 - .../api/project/assign_declaration.py | 51 ++++++++++++++ .../api/project/unassign_declaration.py | 25 +++++++ .../ifcopenshell/api/pset/add_pset.py | 2 +- .../api/sequence/add_work_calendar.py | 35 ++-------- .../api/sequence/add_work_calendar_times.py | 37 ---------- .../api/sequence/add_work_plan.py | 17 +---- .../api/sequence/add_work_schedule.py | 42 ++++++----- .../api/sequence/add_work_time.py | 18 +++++ .../api/sequence/assign_workplan.py | 37 +++++----- .../ifcopenshell/api/sequence/data.py | 33 +++------ .../api/sequence/edit_recurrence_pattern.py | 21 ++++++ .../api/sequence/edit_work_calendar.py | 7 +- .../api/sequence/edit_work_plan.py | 7 +- .../api/sequence/edit_work_schedule.py | 7 +- .../api/sequence/edit_work_time.py | 15 ++++ .../api/sequence/remove_work_calendar.py | 10 +-- .../api/sequence/remove_work_plan.py | 10 +-- .../api/sequence/remove_work_schedule.py | 10 +-- .../api/time/add_recurrence_pattern.py | 27 ++++--- .../ifcopenshell/api/unit/assign_unit.py | 5 +- 25 files changed, 289 insertions(+), 228 deletions(-) create mode 100644 src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py delete mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar_times.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py create mode 100644 src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 6db448792d..f78fbf5513 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -16,11 +16,12 @@ class LoadWorkPlans(bpy.types.Operator): for ifc_definition_id, work_plan in Data.work_plans.items(): new = props.work_plans.add() new.ifc_definition_id = ifc_definition_id - new.name = work_plan["Name"] or "Unnamed" + new.name = work_plan["Name"] or "Unnamed" props.is_editing = True bpy.ops.bim.disable_editing_work_plan() return {"FINISHED"} + class DisableWorkPlanEditingUI(bpy.types.Operator): bl_idname = "bim.disable_work_plan_editing_ui" bl_label = "Disable WorkPlan Editing UI" @@ -58,25 +59,28 @@ class EditWorkPlan(bpy.types.Operator): attributes[attribute.name] = attribute.enum_value self.file = IfcStore.get_file() ifcopenshell.api.run( - "sequence.edit_work_plan", self.file, **{"work_plan": self.file.by_id(props.active_work_plan_id), "attributes": attributes} + "sequence.edit_work_plan", + self.file, + **{"work_plan": self.file.by_id(props.active_work_plan_id), "attributes": attributes} ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_plans() return {"FINISHED"} + class RemoveWorkPlan(bpy.types.Operator): bl_idname = "bim.remove_work_plan" bl_label = "Remove Work Plan" work_plan: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMWorkPlanProperties self.file = IfcStore.get_file() ifcopenshell.api.run("sequence.remove_work_plan", self.file, **{"work_plan": self.file.by_id(self.work_plan)}) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_plans() return {"FINISHED"} + class EnableEditingWorkPlan(bpy.types.Operator): bl_idname = "bim.enable_editing_work_plan" bl_label = "Enable Editing Work Plan" @@ -98,7 +102,9 @@ class EnableEditingWorkPlan(bpy.types.Operator): new.is_null = data[attribute.name()] is None new.is_optional = attribute.optional() new.data_type = data_type - if data_type == "string": + if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]: + 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)) @@ -107,6 +113,7 @@ class EnableEditingWorkPlan(bpy.types.Operator): props.active_work_plan_id = self.work_plan return {"FINISHED"} + class DisableEditingWorkPlan(bpy.types.Operator): bl_idname = "bim.disable_editing_work_plan" bl_label = "Disable Editing Work Plan" @@ -115,6 +122,7 @@ class DisableEditingWorkPlan(bpy.types.Operator): context.scene.BIMWorkPlanProperties.active_work_plan_id = 0 return {"FINISHED"} + class LoadWorkSchedules(bpy.types.Operator): bl_idname = "bim.load_work_schedules" bl_label = "Load Work Schedules" @@ -125,12 +133,13 @@ class LoadWorkSchedules(bpy.types.Operator): props.work_schedules.remove(0) for ifc_definition_id, work_schedule in Data.work_schedules.items(): new = props.work_schedules.add() - new.ifc_definition_id = ifc_definition_id or "Unnamed" - new.name = work_schedule["Name"] + new.ifc_definition_id = ifc_definition_id + new.name = work_schedule["Name"] or "Unnamed" props.is_editing = True bpy.ops.bim.disable_editing_work_schedule() return {"FINISHED"} + class DisableWorkScheduleEditingUI(bpy.types.Operator): bl_idname = "bim.disable_work_schedule_editing_ui" bl_label = "Disable WorkSchedule Editing UI" @@ -145,7 +154,7 @@ class AddWorkSchedule(bpy.types.Operator): bl_label = "Add Work Schedule" def execute(self, context): - result = ifcopenshell.api.run("sequence.add_work_schedule", IfcStore.get_file()) + ifcopenshell.api.run("sequence.add_work_schedule", IfcStore.get_file()) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_schedules() return {"FINISHED"} @@ -167,27 +176,31 @@ class EditWorkSchedule(bpy.types.Operator): elif attribute.data_type == "enum": attributes[attribute.name] = attribute.enum_value self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.edit_work_schedule", self.file, **{ - "work_schedule": self.file.by_id(props.active_work_schedule_id), - "attributes": attributes - }) + ifcopenshell.api.run( + "sequence.edit_work_schedule", + self.file, + **{"work_schedule": self.file.by_id(props.active_work_schedule_id), "attributes": attributes} + ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_schedules() return {"FINISHED"} + class RemoveWorkSchedule(bpy.types.Operator): bl_idname = "bim.remove_work_schedule" bl_label = "Remove Work Schedule" work_schedule: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMWorkScheduleProperties self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.remove_work_schedule", self.file, **{"work_schedule": self.file.by_id(self.work_schedule)}) - Data.load(IfcStore.get_file()) + ifcopenshell.api.run( + "sequence.remove_work_schedule", self.file, **{"work_schedule": self.file.by_id(self.work_schedule)} + ) + Data.load(self.file) bpy.ops.bim.load_work_schedules() return {"FINISHED"} + class EnableEditingWorkSchedule(bpy.types.Operator): bl_idname = "bim.enable_editing_work_schedule" bl_label = "Enable Editing Work Schedule" @@ -209,7 +222,9 @@ class EnableEditingWorkSchedule(bpy.types.Operator): new.is_null = data[attribute.name()] is None new.is_optional = attribute.optional() new.data_type = data_type - if data_type == "string": + if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]: + 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)) @@ -238,12 +253,13 @@ class LoadWorkCalendars(bpy.types.Operator): 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 or "Unnamed" - new.name = work_calendar["Name"] + 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" @@ -258,7 +274,7 @@ class AddWorkCalendar(bpy.types.Operator): bl_label = "Add Work Calendar" def execute(self, context): - result = ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file()) + ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file()) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_calendars() return {"FINISHED"} @@ -280,27 +296,31 @@ class EditWorkCalendar(bpy.types.Operator): elif attribute.data_type == "enum": attributes[attribute.name] = attribute.enum_value self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.edit_work_calendar", self.file, **{ - "work_calendar": self.file.by_id(props.active_work_calendar_id), - "attributes": attributes - }) + ifcopenshell.api.run( + "sequence.edit_work_calendar", + self.file, + **{"work_calendar": self.file.by_id(props.active_work_calendar_id), "attributes": attributes} + ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_calendars() return {"FINISHED"} + class RemoveWorkCalendar(bpy.types.Operator): bl_idname = "bim.remove_work_calendar" bl_label = "Remove Work Plan" work_calendar: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMWorkCalendarProperties self.file = IfcStore.get_file() - ifcopenshell.api.run("sequence.remove_work_calendar", self.file, **{"work_calendar": self.file.by_id(self.work_calendar)}) - Data.load(IfcStore.get_file()) + ifcopenshell.api.run( + "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" diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 10812f3ac1..29a1af6b14 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -61,4 +61,4 @@ class BIMWorkCalendarProperties(PropertyGroup): is_editing: BoolProperty(name="Is Editing", default=False) 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") \ No newline at end of file + active_work_calendar_id: IntProperty(name="Active Work Calendar Id") diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index 8eaaa8fcce..9ed96cef3c 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -43,7 +43,10 @@ class BIM_PT_work_plans(Panel): def draw_editable_ui(self, context): for attribute in self.props.work_plan_attributes: row = self.layout.row(align=True) - row.prop(attribute, "string_value", text=attribute.name) + 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="") @@ -104,10 +107,14 @@ class BIM_PT_work_schedules(Panel): def draw_editable_ui(self, context): for attribute in self.props.work_schedule_attributes: row = self.layout.row(align=True) - row.prop(attribute, "string_value", text=attribute.name) + 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="") + class BIM_UL_work_schedules(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: @@ -123,6 +130,7 @@ class BIM_UL_work_schedules(UIList): op.work_schedule = item.ifc_definition_id row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id + class BIM_PT_work_calendars(Panel): bl_label = "IFC Work Calendars" bl_idname = "BIM_PT_work_calendars" @@ -163,10 +171,14 @@ class BIM_PT_work_calendars(Panel): def draw_editable_ui(self, context): for attribute in self.props.work_calendar_attributes: row = self.layout.row(align=True) - row.prop(attribute, "string_value", text=attribute.name) + 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="") + class BIM_UL_work_calendars(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: @@ -230,4 +242,4 @@ class BIM_UL_tasks(UIList): row = layout.row(align=True) if item.identification: layout.label(text=item.identification) - layout.label(text=item.name) \ No newline at end of file + layout.label(text=item.name) diff --git a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py index 30a8a85d95..379ee8fdce 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py +++ b/src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py @@ -36,9 +36,9 @@ class Usecase: self.file.remove(decomposes) if is_decomposed_by: - related_objects = list(is_decomposed_by.RelatedObjects) - related_objects.append(self.settings["product"]) - is_decomposed_by.RelatedObjects = related_objects + related_objects = set(is_decomposed_by.RelatedObjects) + related_objects.add(self.settings["product"]) + is_decomposed_by.RelatedObjects = list(related_objects) ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": is_decomposed_by}) else: is_decomposed_by = self.file.create_entity( diff --git a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py index 63b7d17a2b..9cf6bb06ef 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py +++ b/src/ifcopenshell-python/ifcopenshell/api/constraint/unassign_constraint.py @@ -1,6 +1,3 @@ -import ifcopenshell - - class Usecase: def __init__(self, file, **settings): self.file = file diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py new file mode 100644 index 0000000000..ec476df3f2 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/project/assign_declaration.py @@ -0,0 +1,51 @@ +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "definition": None, + "relating_context": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + declares = None + if self.settings["relating_context"].Declares: + declares = self.settings["relating_context"].Declares[0] + + has_context = None + if self.settings["definition"].HasContext: + has_context = self.settings["definition"].HasContext[0] + + if has_context and has_context == declares: + return + + if has_context: + related_definitions = list(has_context.RelatedDefinitions) + related_definitions.remove(self.settings["definition"]) + if related_definitions: + has_context.RelatedDefinitions = related_definitions + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": has_context}) + else: + self.file.remove(has_context) + + if declares: + related_definitions = set(declares.RelatedDefinitions) + related_definitions.add(self.settings["definition"]) + declares.RelatedDefinitions = list(related_definitions) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": declares}) + else: + declares = self.file.create_entity( + "IfcRelDeclares", + **{ + "GlobalId": ifcopenshell.guid.new(), + "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), + "RelatedDefinitions": [self.settings["definition"]], + "RelatingContext": self.settings["relating_context"], + } + ) + return declares diff --git a/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py new file mode 100644 index 0000000000..78d4e31cfb --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/project/unassign_declaration.py @@ -0,0 +1,25 @@ +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "definition": None, + "relating_context": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + if not self.settings["definition"].HasContext: + return + rel = self.settings["definition"].HasContext[0] + related_definitions = set(rel.RelatedDefinitions) or set() + related_definitions.remove(self.settings["definition"]) + if len(related_definitions): + rel.RelatedDefinitions = list(related_definitions) + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + else: + self.file.remove(rel) diff --git a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py index 37062d3845..48b5ef8eec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py +++ b/src/ifcopenshell-python/ifcopenshell/api/pset/add_pset.py @@ -27,7 +27,7 @@ class Usecase: pset = self.file.create_entity( "IfcPropertySet", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"]} ) - has_property_sets = list(self.settings["product"].HasPropertySets) + has_property_sets = list(self.settings["product"].HasPropertySets or []) has_property_sets.append(pset) self.settings["product"].HasPropertySets = has_property_sets elif self.settings["product"].is_a("IfcMaterialDefinition"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py index f8526c0e37..106e38c426 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar.py @@ -1,17 +1,10 @@ import ifcopenshell.api -import ifcopenshell.util.date -from datetime import datetime, timedelta class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = { - "name": "Unnamed", - "predefined_type": "NOTDEFINED", - "working_times":[], - "exception_times":[] - } + self.settings = {"name": "Unnamed", "predefined_type": "NOTDEFINED", "working_times": [], "exception_times": []} for key, value in settings.items(): self.settings[key] = value @@ -23,26 +16,8 @@ class Usecase: predefined_type=self.settings["predefined_type"], name=self.settings["name"], ) - working_time = self.file.create_entity("IfcWorkTime", **{"Name":"DefaultWorkingTime"}) - self.settings["working_times"].append(working_time) - work_calendar.WorkingTimes = self.settings["working_times"] - - exception_time = self.file.create_entity("IfcWorkTime", **{"Name":"DefaultExceptionTime"}) - self.settings["exception_times"].append(exception_time) - work_calendar.ExceptionTimes = self.settings["exception_times"] - context = self.file.by_type("IfcContext")[0] - if context.Declares: - rel_declares = context.Declares[0] - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel_declares) - else: - rel_declares = self.file.create_entity("IfcRelDeclares", **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatingContext": context - }) - related_definitions = list(rel_declares.RelatedDefinitions or []) - related_definitions.append(work_calendar) - rel_declares.RelatedDefinitions = related_definitions - - return work_calendar \ No newline at end of file + ifcopenshell.api.run( + "project.assign_declaration", self.file, definition=work_calendar, relating_context=context + ) + return work_calendar diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar_times.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar_times.py deleted file mode 100644 index 307ae44256..0000000000 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_calendar_times.py +++ /dev/null @@ -1,37 +0,0 @@ -import ifcopenshell -import ifcopenshell.api -import ifcopenshell.util.date -from datetime import datetime, timedelta - - -class Usecase: - def __init__(self, file, **settings): - self.file = file - self.settings = { - "object": None, - "name":None, - "recurrence_pattern": None, - "start": datetime.now(), - "finish": datetime.now() + timedelta(days=365) - } - for key, value in settings.items(): - self.settings[key] = value - - def execute(self): - working_time = self.file.create_entity("IfcWorkTime", **{ - "Name": self.settings["name"], - "Start": ifcopenshell.util.date.datetime2ifc(self.settings["start"], "IfcTime"), - "Finish": ifcopenshell.util.date.datetime2ifc(self.settings["finish"], "IfcTime") - }) - - #TODO Implement user settings for recurrence pattern - working_time.RecurrencePattern = ifcopenshell.api.run("time.add_recurrence_pattern", self.file) - - if self.settings["object"].is_a('IfcWorkCalendar'): - if self.settings["object"].WorkingTimes: - working_times = list(self.settings["object"].WorkingTimes) - working_times.append(working_time) - self.settings["object"].WorkingTimes = working_times - else: - self.settings["object"].WorkingTimes = [working_time] - return working_time \ No newline at end of file diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py index f885077333..3d8e2cc3ca 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_plan.py @@ -25,18 +25,7 @@ class Usecase: work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(self.settings["start_time"], "IfcDateTime") context = self.file.by_type("IfcContext")[0] - if context.Declares: - rel_declares = context.Declares[0] - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel_declares) - else: - rel_declares = self.file.create_entity("IfcRelDeclares", **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatingContext": context - }) - - related_definitions = list(rel_declares.RelatedDefinitions or []) - related_definitions.append(work_plan) - rel_declares.RelatedDefinitions = related_definitions - + ifcopenshell.api.run( + "project.assign_declaration", self.file, definition=work_plan, relating_context=context + ) return work_plan diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py index cd7cb30ee0..e75bb4dde0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_schedule.py @@ -6,7 +6,12 @@ from datetime import datetime class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {"name": "Unnamed", "predefined_type": "NOTDEFINED", "start_time": datetime.now(), "work_plan":None} + self.settings = { + "name": "Unnamed", + "predefined_type": "NOTDEFINED", + "start_time": datetime.now(), + "work_plan": None, + } for key, value in settings.items(): self.settings[key] = value @@ -23,25 +28,18 @@ class Usecase: if person: work_schedule.Creators = [person] work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(self.settings["start_time"], "IfcDateTime") - - if self.settings["work_plan"]: - rel_aggregates = ifcopenshell.api.run("aggregate.assign_object", self.file, **{ - "product": work_schedule, - "relating_object": self.settings["work_plan"] - }) - else: - context = self.file.by_type("IfcContext")[0] - if context.Declares: - rel_declares = context.Declares[0] - ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel_declares) - else: - rel_declares = self.file.create_entity("IfcRelDeclares", **{ - "GlobalId": ifcopenshell.guid.new(), - "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), - "RelatingContext": context - }) - related_definitions = list(rel_declares.RelatedDefinitions or []) - related_definitions.append(work_schedule) - rel_declares.RelatedDefinitions = related_definitions - return work_schedule \ No newline at end of file + if self.settings["work_plan"]: + ifcopenshell.api.run( + "aggregate.assign_object", + self.file, + **{"product": work_schedule, "relating_object": self.settings["work_plan"]} + ) + else: + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + context = self.file.by_type("IfcContext")[0] + ifcopenshell.api.run( + "project.assign_declaration", self.file, definition=work_schedule, relating_context=context + ) + return work_schedule diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py new file mode 100644 index 0000000000..13e7ec080a --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/add_work_time.py @@ -0,0 +1,18 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"work_calendar": None, "type": "WorkingTimes", "name": 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": + 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": + exception_times = list(self.settings["work_calendar"].ExceptionTimes or []) + exception_times.append(work_time) + self.settings["work_calendar"].WorkingTimes = exception_times + return work_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py index 26b7042df6..aced43fe30 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/assign_workplan.py @@ -1,31 +1,26 @@ import ifcopenshell import ifcopenshell.api + class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = { - "work_schedule": None, - "work_plan":None, - } + self.settings = {"work_schedule": None, "work_plan": None} for key, value in settings.items(): self.settings[key] = value def execute(self): - ###Project declaration should be removed if the workschedule is assigned to a project.#### - if self.settings["work_schedule"].HasContext: - rel_declares = self.settings["work_schedule"].HasContext[0] - related_definitions = list(rel_declares.RelatedDefinitions) - related_definitions.remove(self.settings["work_schedule"]) - rel_declares.RelatedDefinitions = related_definitions - if self.settings["work_plan"].IsDecomposedBy: - rel_aggregates = self.settings["work_plan"].IsDecomposedBy[0] - related_objects = list(rel_aggregates.RelatedObjects or []) - related_objects.append(self.settings["work_schedule"]) - rel_aggregates.RelatedObjects = related_objects - else: - rel_aggregates = ifcopenshell.api.run("aggregate.assign_object", self.file, **{ - "product": self.settings["work_schedule"], - "relating_object": self.settings["work_plan"] - }) - return rel_aggregates \ No newline at end of file + # TODO: this is an ambiguity by buildingSMART + # See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510 + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definition=self.settings["work_schedule"], + relating_context=self.file.by_type("IfcContext")[0], + ) + rel_aggregates = ifcopenshell.api.run( + "aggregate.assign_object", + self.file, + **{"product": self.settings["work_schedule"], "relating_object": self.settings["work_plan"]} + ) + return rel_aggregates diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py index 523e06ed88..501af373c4 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/data.py @@ -1,5 +1,6 @@ import ifcopenshell.util.date + class Data: is_loaded = False work_plans = {} @@ -23,7 +24,7 @@ class Data: cls.load_work_schedules() cls.load_work_calendars() cls.load_tasks() - cls.is_loaded=True + cls.is_loaded = True @classmethod def load_work_plans(cls): @@ -33,10 +34,10 @@ class Data: del data["OwnerHistory"] if data["Creators"]: data["Creators"] = [p.id() for p in data["Creators"]] - data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"]).isoformat() - data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]).isoformat() + data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"]) + data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]) if data["FinishTime"]: - data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]).isoformat() + data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]) cls.work_plans[work_plan.id()] = data @classmethod @@ -47,35 +48,19 @@ class Data: del data["OwnerHistory"] if data["Creators"]: data["Creators"] = [p.id() for p in data["Creators"]] - data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"]).isoformat() - data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]).isoformat() + data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"]) + data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"]) if data["FinishTime"]: - data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]).isoformat() + data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"]) cls.work_schedules[work_schedule.id()] = data @classmethod def load_work_calendars(cls): for work_calendar in cls._file.by_type("IfcWorkCalendar"): data = work_calendar.get_info() - del data["OwnerHistory"] - ##### HOW DO WE NEST ENTITIES SUCH AS WORKTIME? #### - ##### HOW TO ALLOW DISPLAY OF TIME? AND DATES? #### + del data["OwnerHistory"] del data["WorkingTimes"] del data["ExceptionTimes"] - # if data["WorkingTimes"]: - # for worktime in data["WorkingTimes"]: - # worktime_data = {} - # if worktime.Start: - # worktime_data["start"] = ifcopenshell.util.date.ifc2datetime(worktime.Start).isoformat() - # if worktime.Start: - # worktime_data["finish"] = ifcopenshell.util.date.ifc2datetime(worktime.Start).isoformat() - # worktime = worktime_data - # if data["ExceptionTimes"]: - # for exceptiontime in data["ExceptionTimes"]: - # exceptiontime_data = {} - # exceptiontime_data["start"] = ifcopenshell.util.date.ifc2datetime(exceptiontime.Finish).isoformat() - # exceptiontime_data["finish"] = ifcopenshell.util.date.ifc2datetime(exceptiontime.Finish).isoformat() - # exceptiontime = exceptiontime_data cls.work_calendars[work_calendar.id()] = data @classmethod diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py new file mode 100644 index 0000000000..ee57a173bc --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_recurrence_pattern.py @@ -0,0 +1,21 @@ +import ifcopenshell.util.date + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"recurrence_pattern": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + 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]) + })) + value = periods + setattr(self.settings["recurrence_pattern"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py index 0516508733..f45974d960 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_calendar.py @@ -1,13 +1,10 @@ class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = { - "work_calendar": None, - "attributes": {} - } + self.settings = {"work_calendar": 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["work_calendar"], name, value) \ No newline at end of file + setattr(self.settings["work_calendar"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py index 3c51b745a2..3830f0c2c1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_plan.py @@ -1,13 +1,10 @@ class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = { - "work_plan": None, - "attributes": {} - } + self.settings = {"work_plan": 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["work_plan"], name, value) \ No newline at end of file + setattr(self.settings["work_plan"], name, value) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py index c1dced8bbe..e6782c30d1 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_schedule.py @@ -1,13 +1,10 @@ class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = { - "work_schedule": None, - "attributes": {} - } + self.settings = {"work_schedule": 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["work_schedule"], name, value) \ No newline at end of file + setattr(self.settings["work_schedule"], 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 new file mode 100644 index 0000000000..057cec6aba --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/edit_work_time.py @@ -0,0 +1,15 @@ +import ifcopenshell.util.date + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"work_time": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for name, value in self.settings["attributes"].items(): + if 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_work_calendar.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py index 549509f050..ee3c89327a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_calendar.py @@ -7,8 +7,10 @@ class Usecase: def execute(self): # TODO: do a deep purge - if self.settings["work_calendar"].HasContext: - rel_declares = self.settings["work_calendar"].HasContext[0] - if len(rel_declares.RelatedDefinitions) == 1: - self.file.remove(rel_declares) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definition=self.settings["work_calendar"], + relating_context=self.file.by_type("IfcContext")[0], + ) self.file.remove(self.settings["work_calendar"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py index 941000f7ff..ddd36ad88e 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_plan.py @@ -7,8 +7,10 @@ class Usecase: def execute(self): # TODO: do a deep purge - if self.settings["work_plan"].HasContext: - rel_declares = self.settings["work_plan"].HasContext[0] - if len(rel_declares.RelatedDefinitions) == 1: - self.file.remove(rel_declares) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definition=self.settings["work_plan"], + relating_context=self.file.by_type("IfcContext")[0], + ) self.file.remove(self.settings["work_plan"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py index 7a36eb2348..c0af69b25d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py +++ b/src/ifcopenshell-python/ifcopenshell/api/sequence/remove_work_schedule.py @@ -7,8 +7,10 @@ class Usecase: def execute(self): # TODO: do a deep purge - if self.settings["work_schedule"].HasContext: - rel_declares = self.settings["work_schedule"].HasContext[0] - if len(rel_declares.RelatedDefinitions) == 1: - self.file.remove(rel_declares) + ifcopenshell.api.run( + "project.unassign_declaration", + self.file, + definition=self.settings["work_schedule"], + relating_context=self.file.by_type("IfcContext")[0], + ) self.file.remove(self.settings["work_schedule"]) diff --git a/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py b/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py index 8e890a443b..ec6883dc7d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py +++ b/src/ifcopenshell-python/ifcopenshell/api/time/add_recurrence_pattern.py @@ -1,21 +1,18 @@ -import ifcopenshell -import ifcopenshell.api -from datetime import time - - class Usecase: def __init__(self, file, **settings): self.file = file - self.settings = {"recurrence_type": 'WEEKLY'} + self.settings = {"parent": None, "recurrence_type": "WEEKLY"} for key, value in settings.items(): self.settings[key] = value - + def execute(self): - - recurrence = self.file.createIfcRecurrencePattern(self.settings['recurrence_type']) - #recurrence.WeekdayComponent = [self.file.createIfcDayInWeekNumber(1),self.file.createIfcDayInWeekNumber(2)] - recurrence.TimePeriods = [ - self.file.createIfcTimePeriod(time(8).isoformat(),time(12).isoformat()), - self.file.createIfcTimePeriod(time(13).isoformat(),time(17).isoformat()) - ] - return recurrence \ No newline at end of file + 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: + self.file.remove(self.settings["parent"].RecurrencePattern) + self.settings["parent"].RecurrencePattern = recurrence + elif self.settings["parent"].is_a("IfcTaskTimeRecurring"): + if len(self.file.get_inverse(self.settings["parent"].Recurrence)) == 1: + self.file.remove(self.settings["parent"].Recurrence) + self.settings["parent"].Recurrence = recurrence diff --git a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py index f99e41220b..bedebcb3eb 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py +++ b/src/ifcopenshell-python/ifcopenshell/api/unit/assign_unit.py @@ -33,7 +33,10 @@ class Usecase(): # TODO: handle unit rewriting, which is complicated else: unit_assignment = self.file.createIfcUnitAssignment([u["ifc"] for u in self.settings.values()]) - self.file.by_type("IfcProject")[0].UnitsInContext = unit_assignment + if self.file.schema == "IFC2X3": + self.file.by_type("IfcProject")[0].UnitsInContext = unit_assignment + else: + self.file.by_type("IfcContext")[0].UnitsInContext = unit_assignment return unit_assignment def create_metric_unit(self, unit_type, data):