diff --git a/src/blenderbim/blenderbim/bim/module/cost/__init__.py b/src/blenderbim/blenderbim/bim/module/cost/__init__.py index 7a8a31b1d3..8d420e367d 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/cost/__init__.py @@ -6,12 +6,16 @@ classes = ( operator.RemoveCostSchedule, operator.EditCostSchedule, operator.EnableEditingCostSchedule, + operator.EnableEditingCostItems, + operator.EnableEditingCostItem, operator.DisableEditingCostSchedule, operator.AddCostItem, operator.AddSummaryCostItem, operator.ExpandCostItem, operator.ContractCostItem, operator.RemoveCostItem, + operator.AssignControl, + operator.UnassignControl, prop.CostItem, prop.BIMCostProperties, ui.BIM_PT_cost_schedules, diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 80b6df245b..95e1b89008 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -15,6 +15,32 @@ class AddCostSchedule(bpy.types.Operator): return {"FINISHED"} +class EditCostSchedule(bpy.types.Operator): + bl_idname = "bim.edit_cost_schedule" + bl_label = "Edit Cost Schedule" + + def execute(self, context): + props = context.scene.BIMCostProperties + attributes = {} + for attribute in props.cost_schedule_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( + "cost.edit_cost_schedule", + self.file, + **{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_cost_schedule() + return {"FINISHED"} + + class RemoveCostSchedule(bpy.types.Operator): bl_idname = "bim.remove_cost_schedule" bl_label = "Remove Cost Schedule" @@ -38,10 +64,13 @@ class EnableEditingCostSchedule(bpy.types.Operator): def execute(self, context): self.props = context.scene.BIMCostProperties self.props.active_cost_schedule_id = self.cost_schedule - while len(self.props.cost_schedule_attributes) > 0: self.props.cost_schedule_attributes.remove(0) + self.enable_editing_cost_schedule() + self.props.is_editing = "COST_SCHEDULE" + return {"FINISHED"} + def enable_editing_cost_schedule(self): data = Data.cost_schedules[self.cost_schedule] for attribute in IfcStore.get_schema().declaration_by_name("IfcCostSchedule").all_attributes(): @@ -62,12 +91,22 @@ class EnableEditingCostSchedule(bpy.types.Operator): if data[attribute.name()]: new.enum_value = data[attribute.name()] + +class EnableEditingCostItems(bpy.types.Operator): + bl_idname = "bim.enable_editing_cost_items" + bl_label = "Enable Editing Cost Items" + cost_schedule: bpy.props.IntProperty() + + def execute(self, context): + self.props = context.scene.BIMCostProperties + self.props.active_cost_schedule_id = self.cost_schedule while len(self.props.cost_items) > 0: self.props.cost_items.remove(0) self.contracted_cost_items = json.loads(self.props.contracted_cost_items) for related_object_id in Data.cost_schedules[self.cost_schedule]["RelatedObjects"]: self.create_new_cost_item_li(related_object_id, 0) + self.props.is_editing = "COST_ITEMS" return {"FINISHED"} def create_new_cost_item_li(self, related_object_id, level_index): @@ -83,40 +122,15 @@ class EnableEditingCostSchedule(bpy.types.Operator): for related_object_id in cost_item["RelatedObjects"]: self.create_new_cost_item_li(related_object_id, level_index + 1) + return {"FINISHED"} + class DisableEditingCostSchedule(bpy.types.Operator): bl_idname = "bim.disable_editing_cost_schedule" bl_label = "Disable Editing Cost Schedule" def execute(self, context): - props = context.scene.BIMCostProperties - props.active_cost_schedule_id = 0 - return {"FINISHED"} - - -class EditCostSchedule(bpy.types.Operator): - bl_idname = "bim.edit_cost_schedule" - bl_label = "Edit Cost Schedule" - - def execute(self, context): - props = context.scene.BIMCostProperties - attributes = {} - for attribute in props.cost_schedule_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( - "cost.edit_cost_schedule", - self.file, - **{"cost_schedule": self.file.by_id(props.active_cost_schedule_id), "attributes": attributes} - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.disable_editing_cost_schedule() + context.scene.BIMCostProperties.active_cost_schedule_id = 0 return {"FINISHED"} @@ -130,7 +144,7 @@ class AddSummaryCostItem(bpy.types.Operator): self.file = IfcStore.get_file() ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_schedule": self.file.by_id(self.cost_schedule)}) Data.load(self.file) - bpy.ops.bim.enable_editing_cost_schedule(cost_schedule=self.cost_schedule) + bpy.ops.bim.enable_editing_cost_items(cost_schedule=self.cost_schedule) return {"FINISHED"} @@ -142,10 +156,9 @@ class AddCostItem(bpy.types.Operator): def execute(self, context): props = context.scene.BIMCostProperties self.file = IfcStore.get_file() - data = {"cost_item": self.file.by_id(self.cost_item)} - ifcopenshell.api.run("cost.add_cost_item", self.file, **data) + ifcopenshell.api.run("cost.add_cost_item", self.file, **{"cost_item": self.file.by_id(self.cost_item)}) Data.load(self.file) - bpy.ops.bim.enable_editing_cost_schedule(cost_schedule=props.active_cost_schedule_id) + bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id) return {"FINISHED"} @@ -160,7 +173,7 @@ class ExpandCostItem(bpy.types.Operator): contracted_cost_items = json.loads(props.contracted_cost_items) contracted_cost_items.remove(self.cost_item) props.contracted_cost_items = json.dumps(contracted_cost_items) - bpy.ops.bim.enable_editing_cost_schedule(cost_schedule=props.active_cost_schedule_id) + bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id) return {"FINISHED"} @@ -175,7 +188,7 @@ class ContractCostItem(bpy.types.Operator): contracted_cost_items = json.loads(props.contracted_cost_items) contracted_cost_items.append(self.cost_item) props.contracted_cost_items = json.dumps(contracted_cost_items) - bpy.ops.bim.enable_editing_cost_schedule(cost_schedule=props.active_cost_schedule_id) + bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id) return {"FINISHED"} @@ -197,5 +210,124 @@ class RemoveCostItem(bpy.types.Operator): contracted_cost_items.remove(props.active_cost_item_index) props.contracted_cost_items = json.dumps(contracted_cost_items) Data.load(self.file) - bpy.ops.bim.enable_editing_cost_schedule(cost_schedule=props.active_cost_schedule_id) + bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id) + return {"FINISHED"} + + +class EnableEditingCostItem(bpy.types.Operator): + bl_idname = "bim.enable_editing_cost_item" + bl_label = "Enable Editing Cost Item" + cost_item: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMCostProperties + while len(props.cost_item_attributes) > 0: + props.cost_item_attributes.remove(0) + + data = Data.cost_items[self.cost_item] + + for attribute in IfcStore.get_schema().declaration_by_name("IfcCostItem").all_attributes(): + data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) + if data_type == "entity": + continue + new = props.cost_item_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_cost_item_id = self.cost_item + return {"FINISHED"} + + +class DisableEditingCostItem(bpy.types.Operator): + bl_idname = "bim.disable_editing_cost_item" + bl_label = "Disable Editing Cost Item" + + def execute(self, context): + context.scene.BIMCostProperties.active_cost_item_id = 0 + return {"FINISHED"} + + +class EditCostItem(bpy.types.Operator): + bl_idname = "bim.edit_cost_item" + bl_label = "Edit Cost Item" + + def execute(self, context): + props = context.scene.BIMCostProperties + attributes = {} + for attribute in props.cost_item_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 == "boolean": + attributes[attribute.name] = attribute.bool_value + elif attribute.data_type == "integer": + attributes[attribute.name] = attribute.int_value + elif attribute.data_type == "enum": + attributes[attribute.name] = attribute.enum_value + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "cost.edit_cost_item", + self.file, + **{"cost_item": self.file.by_id(props.active_cost_item_id), "attributes": attributes}, + ) + Data.load(IfcStore.get_file()) + bpy.ops.bim.disable_editing_cost_item() + bpy.ops.bim.enable_editing_cost_items(cost_schedule=props.active_cost_schedule_id) + return {"FINISHED"} + + +class AssignControl(bpy.types.Operator): + bl_idname = "bim.assign_control" + bl_label = "Assign Control" + cost_item: bpy.props.IntProperty() + related_object: bpy.props.StringProperty() + + def execute(self, context): + related_objects = ( + [bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects + ) + for related_object in related_objects: + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "control.assign_control", + self.file, + related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), + relating_control=self.file.by_id(self.cost_item), + ) + Data.load(self.file) + return {"FINISHED"} + + +class UnassignControl(bpy.types.Operator): + bl_idname = "bim.unassign_control" + bl_label = "Unassign Control" + cost_item: bpy.props.IntProperty() + related_object: bpy.props.StringProperty() + + def execute(self, context): + related_objects = ( + [bpy.data.objects.get(self.related_object)] if self.related_object else bpy.context.selected_objects + ) + for related_object in related_objects: + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "control.unassign_control", + self.file, + related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), + relating_control=self.file.by_id(self.cost_item), + ) + Data.load(self.file) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/cost/prop.py b/src/blenderbim/blenderbim/bim/module/cost/prop.py index b2d2f26832..4be8e8399e 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/prop.py +++ b/src/blenderbim/blenderbim/bim/module/cost/prop.py @@ -1,4 +1,7 @@ import bpy +import ifcopenshell.api +from blenderbim.bim.ifc import IfcStore +from ifcopenshell.api.cost.data import Data from blenderbim.bim.prop import StrProperty, Attribute from bpy.types import PropertyGroup from bpy.props import ( @@ -13,8 +16,24 @@ from bpy.props import ( ) +def updateCostItemName(self, context): + if self.name == "Unnamed": + return + self.file = IfcStore.get_file() + props = context.scene.BIMCostProperties + ifcopenshell.api.run( + "cost.edit_cost_item", + self.file, + **{"cost_item": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}}, + ) + Data.load(IfcStore.get_file()) + if props.active_cost_item_id == self.ifc_definition_id: + attribute = props.cost_item_attributes.get("Name") + attribute.string_value = self.name + + class CostItem(PropertyGroup): - name: StringProperty(name="Name") + name: StringProperty(name="Name", update=updateCostItemName) ifc_definition_id: IntProperty(name="IFC Definition ID") has_children: BoolProperty(name="Has Children") is_expanded: BoolProperty(name="Is Expanded") @@ -23,7 +42,10 @@ class CostItem(PropertyGroup): class BIMCostProperties(PropertyGroup): cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute) + is_editing: StringProperty(name="Is Editing") active_cost_schedule_id: IntProperty(name="Active Cost Schedule Id") cost_items: CollectionProperty(name="Work Calendar", type=CostItem) + active_cost_item_id: IntProperty(name="Active Cost Id") active_cost_item_index: IntProperty(name="Active Cost Item Index") + cost_item_attributes: CollectionProperty(name="Task Attributes", type=Attribute) contracted_cost_items: StringProperty(name="Contracted Cost Items", default="[]") diff --git a/src/blenderbim/blenderbim/bim/module/cost/ui.py b/src/blenderbim/blenderbim/bim/module/cost/ui.py index 7c9edc329e..22a85127b1 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/ui.py +++ b/src/blenderbim/blenderbim/bim/module/cost/ui.py @@ -25,22 +25,34 @@ class BIM_PT_cost_schedules(Panel): row.operator("bim.add_cost_schedule", icon="ADD") for cost_schedule_id, cost_schedule in Data.cost_schedules.items(): - row = self.layout.row(align=True) - row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON") + self.draw_cost_schedule_ui(cost_schedule_id, cost_schedule) - if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id: + def draw_cost_schedule_ui(self, cost_schedule_id, cost_schedule): + row = self.layout.row(align=True) + row.label(text=cost_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON") + + if self.props.active_cost_schedule_id and self.props.active_cost_schedule_id == cost_schedule_id: + if self.props.is_editing == "COST_SCHEDULE": row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_cost_schedule", text="", icon="X") - elif self.props.active_cost_schedule_id: - row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id - else: - row.operator("bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL").cost_schedule = cost_schedule_id - row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id + elif self.props.is_editing == "COST_ITEMS": + row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id + row.operator("bim.disable_editing_cost_schedule", text="", icon="CANCEL") + elif self.props.active_cost_schedule_id: + row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id + else: + row.operator("bim.enable_editing_cost_items", text="", icon="OUTLINER").cost_schedule = cost_schedule_id + row.operator( + "bim.enable_editing_cost_schedule", text="", icon="GREASEPENCIL" + ).cost_schedule = cost_schedule_id + row.operator("bim.remove_cost_schedule", text="", icon="X").cost_schedule = cost_schedule_id - if self.props.active_cost_schedule_id == cost_schedule_id: - self.draw_editable_cost_schedule_ui(cost_schedule_id, cost_schedule) + if self.props.active_cost_schedule_id == cost_schedule_id: + if self.props.is_editing == "COST_SCHEDULE": + self.draw_editable_cost_schedule_ui() + elif self.props.is_editing == "COST_ITEMS": + self.draw_editable_cost_item_ui(cost_schedule_id) - def draw_editable_cost_schedule_ui(self, cost_schedule_id, cost_schedule): + def draw_editable_cost_schedule_ui(self): for attribute in self.props.cost_schedule_attributes: row = self.layout.row(align=True) if attribute.data_type == "string": @@ -50,9 +62,10 @@ class BIM_PT_cost_schedules(Panel): if attribute.is_optional: row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") - row = self.layout.row(align=True) - row.label(text="X Summary Cost Items") - row.operator("bim.add_summary_cost_item", text="", icon="ADD").cost_schedule = cost_schedule_id + # row = self.layout.row(align=True) + # row.label(text="X Summary Cost Items") + + def draw_editable_cost_item_ui(self, cost_schedule_id): self.layout.template_list( "BIM_UL_cost_items", "", @@ -61,22 +74,60 @@ class BIM_PT_cost_schedules(Panel): self.props, "active_cost_item_index", ) + if self.props.active_cost_item_id: + self.draw_editable_cost_item_attributes_ui() + + def draw_editable_cost_item_attributes_ui(self): + for attribute in self.props.cost_item_attributes: + row = self.layout.row(align=True) + if attribute.data_type == "string": + row.prop(attribute, "string_value", text=attribute.name) + elif attribute.data_type == "boolean": + row.prop(attribute, "bool_value", text=attribute.name) + elif attribute.data_type == "integer": + row.prop(attribute, "int_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_cost_items(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: + props = context.scene.BIMCostProperties row = layout.row(align=True) for i in range(0, item.level_index): row.label(text="", icon="BLANK1") if item.has_children: if item.is_expanded: - row.operator("bim.contract_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN").cost_item = item.ifc_definition_id + row.operator( + "bim.contract_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN" + ).cost_item = item.ifc_definition_id else: - row.operator("bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT").cost_item = item.ifc_definition_id + row.operator( + "bim.expand_cost_item", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT" + ).cost_item = item.ifc_definition_id else: row.label(text="", icon="DOT") - row.label(text=item.name) - row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id - op = row.operator("bim.remove_cost_item", text="", icon="X") - op.cost_item = item.ifc_definition_id + row.prop(item, "name", emboss=False, text="") + + if context.active_object: + oprops = context.active_object.BIMObjectProperties + row = layout.row(align=True) + if oprops.ifc_definition_id in Data.cost_items[item.ifc_definition_id]["Controls"]: + op = row.operator("bim.unassign_control", text="", icon="KEYFRAME_HLT", emboss=False) + op.cost_item = item.ifc_definition_id + else: + op = row.operator("bim.assign_control", text="", icon="KEYFRAME", emboss=False) + op.cost_item = item.ifc_definition_id + + if props.active_cost_item_id == item.ifc_definition_id: + row.operator("bim.edit_cost_item", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_cost_item", text="", icon="CANCEL") + else: + row.operator( + "bim.enable_editing_cost_item", text="", icon="GREASEPENCIL" + ).cost_item = item.ifc_definition_id + row.operator("bim.add_cost_item", text="", icon="ADD").cost_item = item.ifc_definition_id + row.operator("bim.remove_cost_item", text="", icon="X").cost_item = item.ifc_definition_id diff --git a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py index 6dd9b2c6b9..df151b8ef5 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/__init__.py @@ -15,7 +15,6 @@ classes = ( operator.EnableEditingWorkSchedule, operator.EnableEditingTasks, operator.DisableEditingWorkSchedule, - operator.LoadTasks, operator.DisableTaskEditingUI, operator.LoadWorkCalendars, operator.DisableWorkCalendarEditingUI, @@ -56,11 +55,13 @@ classes = ( ui.BIM_UL_tasks, ) + def register(): bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties) bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties) bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties) + def unregister(): del bpy.types.Scene.BIMWorkPlanProperties del bpy.types.Scene.BIMWorkScheduleProperties diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 66fb5232e7..dcb00ca14c 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -66,7 +66,7 @@ class EditWorkPlan(bpy.types.Operator): ifcopenshell.api.run( "sequence.edit_work_plan", self.file, - **{"work_plan": self.file.by_id(props.active_work_plan_id), "attributes": attributes} + **{"work_plan": self.file.by_id(props.active_work_plan_id), "attributes": attributes}, ) Data.load(IfcStore.get_file()) bpy.ops.bim.load_work_plans() @@ -157,7 +157,7 @@ class EditWorkSchedule(bpy.types.Operator): ifcopenshell.api.run( "sequence.edit_work_schedule", self.file, - **{"work_schedule": self.file.by_id(props.active_work_schedule_id), "attributes": attributes} + **{"work_schedule": self.file.by_id(props.active_work_schedule_id), "attributes": attributes}, ) Data.load(IfcStore.get_file()) bpy.ops.bim.disable_editing_work_schedule() @@ -236,7 +236,7 @@ class EnableEditingTasks(bpy.types.Operator): new = self.props.tasks.add() new.ifc_definition_id = related_object_id new.name = task["Name"] or "Unnamed" - new.identification = task["Identification"] or "X" + new.identification = task["Identification"] or "XXX" if task["TaskTime"]: task_time = Data.task_times[task["TaskTime"]] new.start = self.canonicalise_time(task_time["ScheduleStart"]) @@ -254,6 +254,7 @@ class EnableEditingTasks(bpy.types.Operator): if new.is_expanded: for related_object_id in task["RelatedObjects"]: self.create_new_task_li(related_object_id, level_index + 1) + return {"FINISHED"} def canonicalise_time(self, time): if not time: @@ -270,142 +271,6 @@ class DisableEditingWorkSchedule(bpy.types.Operator): return {"FINISHED"} -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" - - 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"} - - -class EditWorkCalendar(bpy.types.Operator): - bl_idname = "bim.edit_work_calendar" - bl_label = "Edit Work Calendar" - - def execute(self, context): - props = context.scene.BIMWorkCalendarProperties - attributes = {} - for attribute in props.work_calendar_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_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): - 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(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" - work_calendar: bpy.props.IntProperty() - - def execute(self, context): - props = context.scene.BIMWorkCalendarProperties - while len(props.work_calendar_attributes) > 0: - props.work_calendar_attributes.remove(0) - - data = Data.work_calendars[self.work_calendar] - - for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity": - continue - new = props.work_calendar_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 == "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_calendar_id = self.work_calendar - return {"FINISHED"} - - -class DisableEditingWorkCalendar(bpy.types.Operator): - bl_idname = "bim.disable_editing_work_calendar" - bl_label = "Disable Editing Work Calendar" - - def execute(self, context): - context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0 - return {"FINISHED"} - - -class LoadTasks(bpy.types.Operator): - bl_idname = "bim.load_tasks" - bl_label = "Load Tasks" - work_schedule: bpy.props.IntProperty() - - def execute(self, context): - props = context.scene.BIMWorkScheduleProperties - while len(props.tasks) > 0: - props.tasks.remove(0) - for ifc_definition_id in Data.work_schedules[self.work_schedule]["RelatedObjects"]: - task = Data.tasks[ifc_definition_id] - new = props.tasks.add() - new.ifc_definition_id = ifc_definition_id - new.name = task["Name"] or "Unnamed" - new.identification = task["Identification"] - return {"FINISHED"} - - class DisableTaskEditingUI(bpy.types.Operator): bl_idname = "bim.disable_task_editing_ui" bl_label = "Disable Task Editing UI" @@ -577,7 +442,7 @@ class EditTaskTime(bpy.types.Operator): ifcopenshell.api.run( "sequence.edit_task_time", self.file, - **{"task_time": self.file.by_id(props.active_task_time_id), "attributes": attributes} + **{"task_time": self.file.by_id(props.active_task_time_id), "attributes": attributes}, ) Data.load(IfcStore.get_file()) bpy.ops.bim.disable_editing_task_time() @@ -825,3 +690,121 @@ class GenerateGanttChart(bpy.types.Operator): ) for task_id in Data.tasks[task_id]["RelatedObjects"]: 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" + + 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"} + + +class EditWorkCalendar(bpy.types.Operator): + bl_idname = "bim.edit_work_calendar" + bl_label = "Edit Work Calendar" + + def execute(self, context): + props = context.scene.BIMWorkCalendarProperties + attributes = {} + for attribute in props.work_calendar_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_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): + 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(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" + work_calendar: bpy.props.IntProperty() + + def execute(self, context): + props = context.scene.BIMWorkCalendarProperties + while len(props.work_calendar_attributes) > 0: + props.work_calendar_attributes.remove(0) + + data = Data.work_calendars[self.work_calendar] + + for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes(): + data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) + if data_type == "entity": + continue + new = props.work_calendar_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 == "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_calendar_id = self.work_calendar + return {"FINISHED"} + + +class DisableEditingWorkCalendar(bpy.types.Operator): + bl_idname = "bim.disable_editing_work_calendar" + bl_label = "Disable Editing Work Calendar" + + def execute(self, context): + context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0 + return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index ea599001c4..ebf065ac39 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -25,11 +25,11 @@ def updateTaskName(self, context): ifcopenshell.api.run( "sequence.edit_task", self.file, - **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}} + **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Name": self.name}}, ) Data.load(IfcStore.get_file()) if props.active_task_id == self.ifc_definition_id: - attribute = context.scene.BIMWorkScheduleProperties.task_attributes.get("Name") + attribute = props.task_attributes.get("Name") attribute.string_value = self.name @@ -41,7 +41,7 @@ def updateTaskIdentification(self, context): ifcopenshell.api.run( "sequence.edit_task", self.file, - **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}} + **{"task": self.file.by_id(self.ifc_definition_id), "attributes": {"Identification": self.identification}}, ) Data.load(IfcStore.get_file()) if props.active_task_id == self.ifc_definition_id: @@ -96,7 +96,7 @@ def updateTaskTimeDateTime(self, context, startfinish): ifcopenshell.api.run( "sequence.edit_task_time", self.file, - **{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}} + **{"task_time": task_time, "attributes": {startfinish_key: startfinish_datetime}}, ) Data.load(IfcStore.get_file()) setattr(self, startfinish, canonicalise_time(startfinish_datetime)) diff --git a/src/blenderbim/blenderbim/bim/module/sequence/ui.py b/src/blenderbim/blenderbim/bim/module/sequence/ui.py index e1bb49fe11..1cc813390d 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/ui.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/ui.py @@ -93,7 +93,7 @@ class BIM_PT_work_schedules(Panel): def draw_work_schedule_ui(self, work_schedule_id, work_schedule): row = self.layout.row(align=True) - row.label(text=work_schedule["Name"] or "Unnamed", icon="TEXT") + row.label(text=work_schedule["Name"] or "Unnamed", icon="LINENUMBERS_ON") if self.props.active_work_schedule_id and self.props.active_work_schedule_id == work_schedule_id: if self.props.is_editing == "WORK_SCHEDULE": @@ -107,8 +107,7 @@ class BIM_PT_work_schedules(Panel): row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id else: row.operator("bim.enable_editing_tasks", text="", icon="ACTION").work_schedule = work_schedule_id - op = row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL") - op.work_schedule = work_schedule_id + row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL").work_schedule = work_schedule_id row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = work_schedule_id if self.props.active_work_schedule_id == work_schedule_id: @@ -172,70 +171,6 @@ class BIM_PT_work_schedules(Panel): row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="") -class BIM_PT_work_calendars(Panel): - bl_label = "IFC Work Calendars" - bl_idname = "BIM_PT_work_calendars" - bl_options = {"DEFAULT_CLOSED"} - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "scene" - - @classmethod - def poll(cls, context): - return IfcStore.get_file() - - def draw(self, context): - if not Data.is_loaded: - Data.load(IfcStore.get_file()) - self.props = context.scene.BIMWorkCalendarProperties - 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") - else: - row.operator("bim.load_work_calendars", text="", icon="GREASEPENCIL") - - 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: - self.draw_editable_ui(context) - - def draw_editable_ui(self, context): - 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="") - - -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 - - class BIM_UL_tasks(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): if item: @@ -304,3 +239,67 @@ class BIM_UL_tasks(UIList): row.operator("bim.enable_editing_task", text="", icon="GREASEPENCIL").task = item.ifc_definition_id 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 + + +class BIM_PT_work_calendars(Panel): + bl_label = "IFC Work Calendars" + bl_idname = "BIM_PT_work_calendars" + bl_options = {"DEFAULT_CLOSED"} + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "scene" + + @classmethod + def poll(cls, context): + return IfcStore.get_file() + + def draw(self, context): + if not Data.is_loaded: + Data.load(IfcStore.get_file()) + self.props = context.scene.BIMWorkCalendarProperties + 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") + else: + row.operator("bim.load_work_calendars", text="", icon="GREASEPENCIL") + + 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: + self.draw_editable_ui(context) + + def draw_editable_ui(self, context): + 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="") + + +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 diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py index f45c93e80e..d06224ab40 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py +++ b/src/ifcopenshell-python/ifcopenshell/api/control/assign_control.py @@ -6,8 +6,8 @@ class Usecase: def __init__(self, file, **settings): self.file = file self.settings = { - "related_object": None, "relating_control": None, + "related_object": None, } for key, value in settings.items(): self.settings[key] = value @@ -38,6 +38,6 @@ class Usecase: "OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file), "RelatedObjects": [self.settings["related_object"]], "RelatingControl": self.settings["relating_control"], - } + }, ) return controls diff --git a/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py new file mode 100644 index 0000000000..68f6ab673d --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/control/unassign_control.py @@ -0,0 +1,25 @@ +import ifcopenshell +import ifcopenshell.api + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "relating_control": None, + "related_object": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + for rel in self.settings["related_object"].HasAssignments or []: + if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != self.settings["relating_control"]: + continue + if len(rel.RelatedObjects) == 1: + return self.file.remove(rel) + related_objects = list(rel.RelatedObjects) + related_objects.remove(self.settings["related_object"]) + rel.RelatedObjects = related_objects + ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel}) + return rel diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py index 6a25361dea..ba83cebdf0 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/data.py @@ -38,7 +38,10 @@ class Data: del data["CostValues"] del data["CostQuantities"] data["RelatedObjects"] = [] + data["Controls"] = [] for rel in cost_item.IsNestedBy: [data["RelatedObjects"].append(o.id()) for o in rel.RelatedObjects if o.is_a("IfcCostItem")] + for rel in cost_item.Controls: + [data["Controls"].append(o.id()) for o in rel.RelatedObjects or []] cls.cost_items[cost_item.id()] = data cls.is_loaded=True diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py new file mode 100644 index 0000000000..c2e409bdb9 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/edit_cost_item.py @@ -0,0 +1,10 @@ +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"cost_item": 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["cost_item"], name, value)