diff --git a/src/blenderbim/blenderbim/bim/module/resource/__init__.py b/src/blenderbim/blenderbim/bim/module/resource/__init__.py index 38db174c0d..c401265b4d 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/__init__.py +++ b/src/blenderbim/blenderbim/bim/module/resource/__init__.py @@ -14,6 +14,9 @@ classes = ( operator.ContractResource, operator.AssignResource, operator.UnassignResource, + operator.EnableEditingResourceTime, + operator.EditResourceTime, + operator.DisableEditingResourceTime, prop.Resource, prop.BIMResourceProperties, prop.BIMResourceTreeProperties, diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 03835444dd..4df0873846 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -3,6 +3,11 @@ import json import ifcopenshell.api from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.resource.data import Data +import blenderbim.bim.helper +import blenderbim.bim.module.sequence.helper as helper +import time +from datetime import datetime +import isodate class LoadResources(bpy.types.Operator): @@ -50,6 +55,7 @@ class EnableEditingResource(bpy.types.Operator): self.props.active_resource_id = self.resource while len(self.props.resource_attributes) > 0: self.props.resource_attributes.remove(0) + self.props.editing_resource_type = "ATTRIBUTES" self.enable_editing_resource() return {"FINISHED"} @@ -72,6 +78,7 @@ class EnableEditingResource(bpy.types.Operator): new.enum_value = data[attribute.name()] + class LoadResourceProperties(bpy.types.Operator): bl_idname = "bim.load_resource_properties" bl_label = "Load Resource Properties" @@ -98,6 +105,7 @@ class DisableEditingResource(bpy.types.Operator): def execute(self, context): context.scene.BIMResourceProperties.active_resource_id = 0 + context.scene.BIMResourceProperties.active_task_time_id = 0 return {"FINISHED"} @@ -143,15 +151,7 @@ class EditResource(bpy.types.Operator): def _execute(self, context): props = context.scene.BIMResourceProperties - attributes = {} - for attribute in props.resource_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 + attributes = blenderbim.bim.helper.export_attributes(props.resource_attributes) self.file = IfcStore.get_file() ifcopenshell.api.run( "resource.edit_resource", @@ -265,3 +265,87 @@ class UnassignResource(bpy.types.Operator): ) Data.load(self.file) return {"FINISHED"} + + +class EnableEditingResourceTime(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource_time" + bl_label = "Enable Editing Resource Usage" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + props = context.scene.BIMResourceProperties + self.file = IfcStore.get_file() + resource_time_id = Data.resources[self.resource]["Usage"] or self.add_resource_time().id() + while len(props.resource_time_attributes) > 0: + props.resource_time_attributes.remove(0) + + data = Data.resource_times[resource_time_id] + + blenderbim.bim.helper.import_attributes("IfcResourceTime", props.resource_time_attributes, data, self.import_attributes) + props.active_resource_time_id = resource_time_id + props.active_resource_id = self.resource + props.editing_resource_type = "USAGE" + return {"FINISHED"} + + def import_attributes(self, name, prop, data): + if prop.data_type == "string": + if isinstance(data[name], datetime): + prop.string_value = "" if prop.is_null else data[name].isoformat() + return True + elif isinstance(data[name], isodate.Duration): + prop.string_value = ( + "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration") + ) + return True + + def add_resource_time(self): + resource_time = ifcopenshell.api.run("resource.add_resource_time", self.file, resource=self.file.by_id(self.resource)) + Data.load(self.file) + return resource_time + + +class DisableEditingResourceTime(bpy.types.Operator): + bl_idname = "bim.disable_editing_resource_time" + bl_label = "Disable Editing Resource Time" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + context.scene.BIMResourceProperties.active_resource_time_id = 0 + bpy.ops.bim.disable_editing_resource() + return {"FINISHED"} + + +class EditResourceTime(bpy.types.Operator): + bl_idname = "bim.edit_resource_time" + bl_label = "Edit Resource Usage" + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + return IfcStore.execute_ifc_operator(self, context) + + def _execute(self, context): + self.props = context.scene.BIMResourceProperties + attributes = blenderbim.bim.helper.export_attributes(self.props.resource_time_attributes, self.export_attributes) + + self.file = IfcStore.get_file() + ifcopenshell.api.run( + "resource.edit_resource_time", + self.file, + **{"resource_time": self.file.by_id(self.props.active_resource_time_id), "attributes": attributes}, + ) + Data.load(self.file) + bpy.ops.bim.disable_editing_resource_time() + bpy.ops.bim.load_resource_properties(resource=self.props.active_resource_id) + return {"FINISHED"} + + def export_attributes(self, attributes, prop): + if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime": + attributes[prop.name] = helper.parse_datetime(prop.string_value) + return True + elif prop.name =="LevelingDelay" or "Work" in prop.name: + attributes[prop.name] = helper.parse_duration(prop.string_value) + return True diff --git a/src/blenderbim/blenderbim/bim/module/resource/prop.py b/src/blenderbim/blenderbim/bim/module/resource/prop.py index db1ca153e2..d7b285d372 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/prop.py +++ b/src/blenderbim/blenderbim/bim/module/resource/prop.py @@ -52,3 +52,6 @@ class BIMResourceProperties(PropertyGroup): contracted_resources: StringProperty(name="Contracted Resources", default="[]") is_resource_update_enabled: BoolProperty(name="Is Resource Update Enabled", default=True) is_loaded: BoolProperty(name="Is Editing") + active_resource_time_id: IntProperty(name="Active Resource Usage Id") + resource_time_attributes: CollectionProperty(name="Resource Usage Attributes", type=Attribute) + editing_resource_type: StringProperty(name="Editing Resource Type") diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index f3619ed2de..bb5c517202 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -1,7 +1,7 @@ from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.resource.data import Data - +import blenderbim.bim.helper class BIM_PT_resources(Panel): bl_label = "IFC Resources" @@ -65,24 +65,17 @@ class BIM_PT_resources(Panel): self.props, "active_resource_index", ) - if self.props.active_resource_id: - self.draw_editable_resource_ui() + if self.props.active_resource_id and self.props.editing_resource_type == "ATTRIBUTES": + self.draw_editable_resource_attributes_ui() - def draw_editable_resource_ui(self): - for attribute in self.props.resource_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 == "float": - row.prop(attribute, "float_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="") + elif self.props.active_resource_id and self.props.editing_resource_type == "USAGE": + self.draw_editable_resource_time_attributes_ui() + + def draw_editable_resource_attributes_ui(self): + blenderbim.bim.helper.draw_attributes(self.props.resource_attributes, self.layout) + + def draw_editable_resource_time_attributes_ui(self): + blenderbim.bim.helper.draw_attributes(self.props.resource_time_attributes, self.layout) class BIM_UL_resources(UIList): @@ -124,9 +117,12 @@ class BIM_UL_resources(UIList): op = row.operator("bim.assign_resource", text="", icon="KEYFRAME", emboss=False) op.resource = item.ifc_definition_id - if props.active_resource_id == item.ifc_definition_id: + if props.active_resource_id == item.ifc_definition_id and props.editing_resource_type == "ATTRIBUTES": row.operator("bim.edit_resource", text="", icon="CHECKMARK") row.operator("bim.disable_editing_resource", text="", icon="CANCEL") + elif props.active_resource_id == item.ifc_definition_id and props.editing_resource_type == "USAGE": + row.operator("bim.edit_resource_time", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_resource_time", text="", icon="CANCEL") elif props.active_resource_id: row.operator("bim.add_resource", text="", icon="ADD").resource = item.ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = item.ifc_definition_id @@ -134,5 +130,5 @@ class BIM_UL_resources(UIList): row.operator( "bim.enable_editing_resource", text="", icon="GREASEPENCIL" ).resource = item.ifc_definition_id - + row.operator("bim.enable_editing_resource_time", text="", icon="TIME").resource = item.ifc_definition_id row.operator("bim.remove_resource", text="", icon="X").resource = item.ifc_definition_id diff --git a/src/blenderbim/blenderbim/bim/module/sequence/operator.py b/src/blenderbim/blenderbim/bim/module/sequence/operator.py index 583fcf2e5d..c66726e4e4 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/operator.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/operator.py @@ -498,7 +498,7 @@ class RemoveTask(bpy.types.Operator): class EnableEditingTaskTime(bpy.types.Operator): bl_idname = "bim.enable_editing_task_time" - bl_label = "Enable Editing Task" + bl_label = "Enable Editing Task Time" bl_options = {"REGISTER", "UNDO"} task: bpy.props.IntProperty() diff --git a/src/blenderbim/blenderbim/bim/module/sequence/prop.py b/src/blenderbim/blenderbim/bim/module/sequence/prop.py index 348306e66c..e7a205f8a5 100644 --- a/src/blenderbim/blenderbim/bim/module/sequence/prop.py +++ b/src/blenderbim/blenderbim/bim/module/sequence/prop.py @@ -282,7 +282,7 @@ class BIMWorkScheduleProperties(PropertyGroup): ], name="Special Columns", ) - active_task_time_id: IntProperty(name="Active Task Id") + active_task_time_id: IntProperty(name="Active Task Time Id") task_time_attributes: CollectionProperty(name="Task Time Attributes", type=Attribute) contracted_tasks: StringProperty(name="Contracted Task Items", default="[]") is_task_update_enabled: BoolProperty(name="Is Task Update Enabled", default=True) diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py new file mode 100644 index 0000000000..fa2553b723 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/add_resource_time.py @@ -0,0 +1,15 @@ +import ifcopenshell.util.date + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = { + "resource": None, + } + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + resource_time = self.file.create_entity("IfcResourceTime") + self.settings["resource"].Usage = resource_time + return resource_time diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py index af068a88a4..99a989ccec 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/resource/data.py +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/data.py @@ -1,11 +1,13 @@ class Data: is_loaded = False resources = {} + resource_times = {} @classmethod def purge(cls): cls.is_loaded = False cls.resources = {} + cls.resource_times = {} @classmethod def load(cls, file): @@ -13,6 +15,7 @@ class Data: if not cls._file: return cls.load_resources() + cls.load_resource_times() cls.is_loaded=True @classmethod @@ -31,4 +34,20 @@ class Data: for rel in resource.ResourceOf: [data["ResourceOf"].append(o.id()) for o in rel.RelatedObjects] data["HasContext"] = resource.HasContext[0].RelatingContext.id() if resource.HasContext else None + if resource.Usage: + data["Usage"] = data["Usage"].id() cls.resources[resource.id()] = data + + @classmethod + def load_resource_times(cls): + cls.resource_times = {} + for resource_time in cls._file.by_type("IfcResourceTime"): + data = resource_time.get_info() + for key, value in data.items(): + if not value: + continue + if "Start" in key or "Finish" in key or key == "StatusTime": + data[key] = ifcopenshell.util.date.ifc2datetime(value) + elif "Work" in key or key =="LevelingDelay": + data[key] = ifcopenshell.util.date.ifc2datetime(value) + cls.resource_times[resource_time.id()] = data diff --git a/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py new file mode 100644 index 0000000000..9a959f3f58 --- /dev/null +++ b/src/ifcopenshell-python/ifcopenshell/api/resource/edit_resource_time.py @@ -0,0 +1,36 @@ +import datetime +import ifcopenshell.util.date + + +class Usecase: + def __init__(self, file, **settings): + self.file = file + self.settings = {"resource_time": None, "attributes": {}} + for key, value in settings.items(): + self.settings[key] = value + + def execute(self): + self.resource = self.get_resource() + + # If the user specifies both an end date and a duration, the duration takes priority + if ( + "ScheduleWork" in self.settings["attributes"].keys() + and "ScheduleFinish" in self.settings["attributes"].keys() + ): + del self.settings["attributes"]["ScheduleFinish"] + if ( + "ActualWork" in self.settings["attributes"].keys() + and "ActualFinish" in self.settings["attributes"].keys() + ): + del self.settings["attributes"]["ActualFinish"] + + for name, value in self.settings["attributes"].items(): + if value: + if "Start" in name or "Finish" in name or name == "StatusTime": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") + elif name == "ScheduleWork" or name == "ActualWork" or name == "RemainingTime": + value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") + setattr(self.settings["resource_time"], name, value) + + def get_resource(self): + return [e for e in self.file.get_inverse(self.settings["resource_time"]) if e.is_a("IfcResource")][0]