From e2d4a3ae24c9364e4adeb0346955c57e7e805273 Mon Sep 17 00:00:00 2001 From: bosonprojets Date: Fri, 9 Sep 2022 02:17:28 +0100 Subject: [PATCH] Refactor Resource module --- .../blenderbim/bim/module/cost/operator.py | 6 +- .../blenderbim/bim/module/resource/data.py | 89 +++ .../bim/module/resource/operator.py | 512 +++--------------- .../blenderbim/bim/module/resource/ui.py | 63 +-- src/blenderbim/blenderbim/core/resource.py | 172 ++++++ src/blenderbim/blenderbim/core/tool.py | 27 + src/blenderbim/blenderbim/tool/__init__.py | 1 + src/blenderbim/blenderbim/tool/resource.py | 308 +++++++++++ 8 files changed, 712 insertions(+), 466 deletions(-) create mode 100644 src/blenderbim/blenderbim/bim/module/resource/data.py create mode 100644 src/blenderbim/blenderbim/core/resource.py create mode 100644 src/blenderbim/blenderbim/tool/resource.py diff --git a/src/blenderbim/blenderbim/bim/module/cost/operator.py b/src/blenderbim/blenderbim/bim/module/cost/operator.py index 4d08f98010..7ae99a4ef7 100644 --- a/src/blenderbim/blenderbim/bim/module/cost/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cost/operator.py @@ -27,7 +27,7 @@ from blenderbim.bim.ifc import IfcStore from bpy_extras.io_utils import ImportHelper from ifcopenshell.api.cost.data import Data from ifcopenshell.api.unit.data import Data as UnitData -from ifcopenshell.api.resource.data import Data as ResourceData +from blenderbim.bim.module.resource.data import ResourceData class AddCostSchedule(bpy.types.Operator): @@ -604,7 +604,7 @@ class AddCostValue(bpy.types.Operator): value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=parent) ifcopenshell.api.run("cost.edit_cost_value", self.file, cost_value=value, attributes=attributes) if parent.is_a("IfcConstructionResource"): - ResourceData.load(self.file) + ResourceData.load() else: Data.load(self.file) return {"FINISHED"} @@ -630,7 +630,7 @@ class RemoveCostItemValue(bpy.types.Operator): cost_value=self.file.by_id(self.cost_value), ) if parent.is_a("IfcConstructionResource"): - ResourceData.load(self.file) + ResourceData.load() else: Data.load(self.file) return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/resource/data.py b/src/blenderbim/blenderbim/bim/module/resource/data.py new file mode 100644 index 0000000000..0822299186 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/resource/data.py @@ -0,0 +1,89 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021-2022 Dion Moult , Yassine Oualid +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import bpy +import blenderbim.tool as tool +from ifcopenshell.api.cost.data import CostValueTrait +import ifcopenshell + + +def refresh(): + ResourceData.is_loaded = False + + +class ResourceData(CostValueTrait): + data = {} + is_loaded = False + cost_values = {} + + @classmethod + def load(cls): + cls.data = { + "has_resources": cls.has_resources(), + } + cls.load_resources() + cls.is_loaded = True + + @classmethod + def has_resources(cls): + return bool(tool.Ifc.get().by_type("IfcResource")) + + @classmethod + def number_of_resources_loaded(cls): + return len(tool.Ifc.get().by_type("IfcResource")) + + @classmethod + def load_resources(cls): + cls.data["resources"] = {} + for resource in tool.Ifc.get().by_type("IfcResource"): + data = resource.get_info() + del data["OwnerHistory"] + data["IsNestedBy"] = [] + for rel in resource.IsNestedBy: + [data["IsNestedBy"].append(o.id()) for o in rel.RelatedObjects] + data["Nests"] = [] + for rel in resource.Nests: + [data["Nests"].append(rel.RelatingObject.id())] + data["ResourceOf"] = [] + 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() + data["TotalCostQuantity"] = cls.get_total_quantity(resource) + if resource.BaseQuantity: + data["BaseQuantity"] = resource.BaseQuantity.get_info() + del data["BaseQuantity"]["Unit"] + if resource.BaseCosts: + data["BaseCosts"] = [e.id() for e in resource.BaseCosts] + cls.load_cost_values(resource, data) + cls.data["resources"][resource.id()] = data + cls.data["number_of_resources_loaded"] = cls.number_of_resources_loaded() + + def load_resource_times(cls): + cls.data["resource_times"] = {} + for resource_time in tool.Ifc.get().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.data["resource_times"][resource_time.id()] = data diff --git a/src/blenderbim/blenderbim/bim/module/resource/operator.py b/src/blenderbim/blenderbim/bim/module/resource/operator.py index 016b8aa11d..b85825804c 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/operator.py +++ b/src/blenderbim/blenderbim/bim/module/resource/operator.py @@ -17,17 +17,9 @@ # along with BlenderBIM Add-on. If not, see . import bpy -import json -import time -import isodate -import ifcopenshell.api -import blenderbim.bim.helper -import blenderbim.bim.module.sequence.helper as helper -from datetime import datetime -from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.resource.data import Data -from ifcopenshell.api.unit.data import Data as UnitData - +from bpy_extras.io_utils import ImportHelper +import blenderbim.core.resource as core +import blenderbim.tool as tool class LoadResources(bpy.types.Operator): bl_idname = "bim.load_resources" @@ -35,66 +27,9 @@ class LoadResources(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.tprops = context.scene.BIMResourceTreeProperties - self.tprops.resources.clear() - - self.contracted_resources = json.loads(self.props.contracted_resources) - Data.load(IfcStore.get_file()) - for resource_id, data in Data.resources.items(): - if not data["HasContext"]: - continue - self.create_new_resource_li(resource_id, 0) - bpy.ops.bim.load_resource_properties() - self.props.is_editing = True + core.load_resources(tool.Resource) return {"FINISHED"} - def create_new_resource_li(self, related_object_id, level_index): - resource = Data.resources[related_object_id] - new = self.tprops.resources.add() - new.ifc_definition_id = related_object_id - new.is_expanded = related_object_id not in self.contracted_resources - new.level_index = level_index - if resource["IsNestedBy"]: - new.has_children = True - if new.is_expanded: - for related_object_id in resource["IsNestedBy"]: - self.create_new_resource_li(related_object_id, level_index + 1) - return {"FINISHED"} - - -class EnableEditingResource(bpy.types.Operator): - bl_idname = "bim.enable_editing_resource" - bl_label = "Enable Editing Resource" - bl_options = {"REGISTER", "UNDO"} - resource: bpy.props.IntProperty() - - def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.props.active_resource_id = self.resource - self.props.resource_attributes.clear() - self.props.editing_resource_type = "ATTRIBUTES" - self.enable_editing_resource() - return {"FINISHED"} - - def enable_editing_resource(self): - data = Data.resources[self.resource] - for attribute in IfcStore.get_schema().declaration_by_name(data["type"]).all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity" or isinstance(data_type, tuple): - continue - new = self.props.resource_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()] - class LoadResourceProperties(bpy.types.Operator): bl_idname = "bim.load_resource_properties" @@ -103,15 +38,36 @@ class LoadResourceProperties(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.tprops = context.scene.BIMResourceTreeProperties - self.props.is_resource_update_enabled = False - for item in self.tprops.resources: - if self.resource and item.ifc_definition_id != self.resource: - continue - resource = Data.resources[item.ifc_definition_id] - item.name = resource["Name"] or "Unnamed" - self.props.is_resource_update_enabled = True + core.load_resource_properties( + tool.Resource, resource=tool.Ifc.get().by_id(self.resource) if self.resource else None + ) + return {"FINISHED"} + + +class AddResource(bpy.types.Operator, tool.Ifc.Operator): + bl_idname = "bim.add_resource" + bl_label = "Add resource" + bl_options = {"REGISTER", "UNDO"} + ifc_class: bpy.props.StringProperty() + parent_resource: bpy.props.IntProperty() + + def _execute(self, context): + core.add_resource( + tool.Ifc, + tool.Resource, + ifc_class=self.ifc_class, + parent_resource=tool.Ifc.get().by_id(self.parent_resource) if self.parent_resource else None, + ) + + +class EnableEditingResource(bpy.types.Operator): + bl_idname = "bim.enable_editing_resource" + bl_label = "Enable Editing Resource" + bl_options = {"REGISTER", "UNDO"} + resource: bpy.props.IntProperty() + + def execute(self, context): + core.enable_editing_resource(tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) return {"FINISHED"} @@ -121,8 +77,7 @@ class DisableEditingResource(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - context.scene.BIMResourceProperties.active_resource_id = 0 - context.scene.BIMResourceProperties.active_task_time_id = 0 + core.disable_editing_resource(tool.Resource) return {"FINISHED"} @@ -132,73 +87,31 @@ class DisableResourceEditingUI(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - context.scene.BIMResourceProperties.is_editing = False + core.disable_resource_editing_ui(tool.Resource) return {"FINISHED"} -class AddResource(bpy.types.Operator): - bl_idname = "bim.add_resource" - bl_label = "Add resource" - bl_options = {"REGISTER", "UNDO"} - ifc_class: bpy.props.StringProperty() - resource: bpy.props.IntProperty() - - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - - def _execute(self, context): - ifcopenshell.api.run( - "resource.add_resource", - IfcStore.get_file(), - parent_resource=IfcStore.get_file().by_id(self.resource) if self.resource else None, - ifc_class=self.ifc_class, - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_resources() - return {"FINISHED"} - - -class EditResource(bpy.types.Operator): +class EditResource(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_resource" bl_label = "Edit Resource" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - props = context.scene.BIMResourceProperties - attributes = blenderbim.bim.helper.export_attributes(props.resource_attributes) - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.edit_resource", - self.file, - **{"resource": self.file.by_id(props.active_resource_id), "attributes": attributes}, + core.edit_resource( + tool.Ifc, + tool.Resource, + resource=tool.Ifc.get().by_id(context.scene.BIMResourceProperties.active_resource_id), ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_resource_properties(resource=props.active_resource_id) - bpy.ops.bim.disable_editing_resource() - return {"FINISHED"} -class RemoveResource(bpy.types.Operator): +class RemoveResource(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_resource" bl_label = "Remove Resource" bl_options = {"REGISTER", "UNDO"} resource: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - ifcopenshell.api.run( - "resource.remove_resource", - IfcStore.get_file(), - resource=IfcStore.get_file().by_id(self.resource), - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_resources() - return {"FINISHED"} + core.remove_resource(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) class ExpandResource(bpy.types.Operator): @@ -208,13 +121,7 @@ class ExpandResource(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMResourceProperties - self.file = IfcStore.get_file() - contracted_resources = json.loads(props.contracted_resources) - contracted_resources.remove(self.resource) - props.contracted_resources = json.dumps(contracted_resources) - Data.load(self.file) - bpy.ops.bim.load_resources() + core.expand_resource(tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) return {"FINISHED"} @@ -225,107 +132,40 @@ class ContractResource(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMResourceProperties - self.file = IfcStore.get_file() - contracted_resources = json.loads(props.contracted_resources) - contracted_resources.append(self.resource) - props.contracted_resources = json.dumps(contracted_resources) - Data.load(self.file) - bpy.ops.bim.load_resources() + core.contract_resource(tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) return {"FINISHED"} -class AssignResource(bpy.types.Operator): +class AssignResource(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_resource" bl_label = "Assign Resource" bl_options = {"REGISTER", "UNDO"} resource: bpy.props.IntProperty() related_object: bpy.props.StringProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - related_objects = ( - [bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects - ) - for related_object in related_objects: - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.assign_resource", - self.file, - relating_resource=self.file.by_id(self.resource), - related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), - ) - Data.load(self.file) - return {"FINISHED"} + core.assign_resource(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) -class UnassignResource(bpy.types.Operator): +class UnassignResource(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unassign_resource" bl_label = "Unassign Resource" bl_options = {"REGISTER", "UNDO"} resource: 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 context.selected_objects - ) - for related_object in related_objects: - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.unassign_resource", - self.file, - relating_resource=self.file.by_id(self.resource), - related_object=self.file.by_id(related_object.BIMObjectProperties.ifc_definition_id), - ) - Data.load(self.file) - return {"FINISHED"} + def _execute(self, context): + core.unassign_resource(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) -class EnableEditingResourceTime(bpy.types.Operator): +class EnableEditingResourceTime(bpy.types.Operator, tool.Ifc.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() - props.resource_time_attributes.clear() - - 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 + core.enable_editing_resource_time(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) class DisableEditingResourceTime(bpy.types.Operator): @@ -334,63 +174,31 @@ class DisableEditingResourceTime(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - context.scene.BIMResourceProperties.active_resource_time_id = 0 - bpy.ops.bim.disable_editing_resource() + core.disable_editing_resource_time(tool.Resource) return {"FINISHED"} -class EditResourceTime(bpy.types.Operator): +class EditResourceTime(bpy.types.Operator, tool.Ifc.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 + core.edit_resource_time( + tool.Ifc, + tool.Resource, + resource_time=tool.Ifc.get().by_id(context.scene.BIMResourceProperties.active_resource_time_id), ) - 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": - if prop.is_null: - attributes[prop.name] = None - return True - attributes[prop.name] = helper.parse_datetime(prop.string_value) - return True - elif prop.name == "LevelingDelay" or "Work" in prop.name: - if prop.is_null: - attributes[prop.name] = None - return True - attributes[prop.name] = helper.parse_duration(prop.string_value) - return True - - -class CalculateResourceWork(bpy.types.Operator): +class CalculateResourceWork(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.calculate_resource_work" bl_label = "Calculate Resource Work" bl_options = {"REGISTER", "UNDO"} resource: bpy.props.IntProperty() - def execute(self, context): - self.file = IfcStore.get_file() - ifcopenshell.api.run("resource.calculate_resource_work", self.file, resource=self.file.by_id(self.resource)) - Data.load(self.file) - bpy.ops.bim.load_resources() - return {"FINISHED"} + def _execute(self, context): + core.calculate_resource_work(tool.Ifc, tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) class EnableEditingResourceCosts(bpy.types.Operator): @@ -400,10 +208,7 @@ class EnableEditingResourceCosts(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMResourceProperties - props.active_resource_id = self.resource - props.editing_resource_type = "COSTS" - bpy.ops.bim.disable_editing_resource_cost_value() + core.enable_editing_resource_costs(tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) return {"FINISHED"} @@ -413,21 +218,7 @@ class DisableEditingResourceCostValue(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - props = context.scene.BIMResourceProperties - props.active_cost_value_id = 0 - props.cost_value_editing_type = "" - return {"FINISHED"} - - -class DisableEditingResourceCostValue(bpy.types.Operator): - bl_idname = "bim.disable_editing_resource_cost_value" - bl_label = "Disable Editing Resource Cost Value" - bl_options = {"REGISTER", "UNDO"} - - def execute(self, context): - props = context.scene.BIMResourceProperties - props.active_cost_value_id = 0 - props.cost_value_editing_type = "" + core.disable_editing_resource_cost_value(tool.Resource) return {"FINISHED"} @@ -438,11 +229,7 @@ class EnableEditingResourceCostValueFormula(bpy.types.Operator): cost_value: bpy.props.IntProperty() def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.props.cost_value_attributes.clear() - self.props.active_cost_value_id = self.cost_value - self.props.cost_value_editing_type = "FORMULA" - self.props.cost_value_formula = Data.cost_values[self.cost_value]["Formula"] + core.enable_editing_resource_cost_value_formula(tool.Resource, cost_value=tool.Ifc.get().by_id(self.cost_value)) return {"FINISHED"} @@ -453,127 +240,28 @@ class EnableEditingResourceCostValue(bpy.types.Operator): cost_value: bpy.props.IntProperty() def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.props.cost_value_attributes.clear() - self.props.active_cost_value_id = self.cost_value - self.props.cost_value_editing_type = "ATTRIBUTES" - data = Data.cost_values[self.cost_value] - - blenderbim.bim.helper.import_attributes( - data["type"], - self.props.cost_value_attributes, - data, - lambda name, prop, data: self.import_attributes(name, prop, data, context), - ) + core.enable_editing_resource_cost_value(tool.Resource, cost_value=tool.Ifc.get().by_id(self.cost_value)) return {"FINISHED"} - def import_attributes(self, name, prop, data, context): - if name == "AppliedValue": - # TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure) - prop = self.props.cost_value_attributes.add() - prop.data_type = "float" - prop.name = "AppliedValue" - prop.is_optional = True - prop.float_value = 0.0 if prop.is_null else data[name] - return True - elif name == "UnitBasis": - prop = self.props.cost_value_attributes.add() - prop.name = "UnitBasisValue" - prop.data_type = "float" - prop.is_null = data["UnitBasis"] is None - prop.is_optional = True - if data["UnitBasis"]: - prop.float_value = data["UnitBasis"]["ValueComponent"] or 0 - else: - prop.float_value = 0 - prop = self.props.cost_value_attributes.add() - prop.name = "UnitBasisUnit" - prop.data_type = "enum" - prop.is_null = prop.is_optional = False - units = {} - for unit_id, unit in UnitData.units.items(): - if unit.get("UnitType", None) in [ - "AREAUNIT", - "LENGTHUNIT", - "TIMEUNIT", - "VOLUMEUNIT", - "MASSUNIT", - "USERDEFINED", - ]: - if unit["type"] == "IfcContextDependentUnit": - units[unit_id] = f"{unit['UnitType']} / {unit['Name']}" - else: - name = unit["Name"] - if unit.get("Prefix", None): - name = f"(unit['Prefix']) {name}" - units[unit_id] = f"{unit['UnitType']} / {name}" - prop.enum_items = json.dumps(units) - if data["UnitBasis"] and data["UnitBasis"]["UnitComponent"]: - prop.enum_value = str(data["UnitBasis"]["UnitComponent"]) - return True - - -class EditResourceCostValueFormula(bpy.types.Operator): +class EditResourceCostValueFormula(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_resource_cost_value_formula" bl_label = "Edit Resource Cost Value Formula" bl_options = {"REGISTER", "UNDO"} cost_value: 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() - ifcopenshell.api.run( - "cost.edit_cost_value_formula", - self.file, - **{"cost_value": self.file.by_id(self.cost_value), "formula": props.cost_value_formula}, - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.disable_editing_resource_cost_value() - return {"FINISHED"} + core.edit_resource_cost_value_formula(tool.Ifc, tool.Resource, cost_value=tool.Ifc.get().by_id(self.cost_value)) -class EditResourceCostValue(bpy.types.Operator): +class EditResourceCostValue(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_resource_cost_value" bl_label = "Edit Resource Cost Value" bl_options = {"REGISTER", "UNDO"} cost_value: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - props = context.scene.BIMResourceProperties - attributes = blenderbim.bim.helper.export_attributes( - props.cost_value_attributes, lambda attributes, prop: self.export_attributes(attributes, prop, context) - ) - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "cost.edit_cost_value", - self.file, - **{"cost_value": self.file.by_id(self.cost_value), "attributes": attributes}, - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.disable_editing_resource_cost_value() - return {"FINISHED"} - - def export_attributes(self, attributes, prop, context): - if prop.name == "UnitBasisValue": - if prop.is_null: - attributes["UnitBasis"] = None - return True - attributes["UnitBasis"] = { - "ValueComponent": prop.float_value or 1, - "UnitComponent": IfcStore.get_file().by_id( - int(context.scene.BIMResourceProperties.cost_value_attributes.get("UnitBasisUnit").enum_value) - ), - } - return True - if prop.name == "UnitBasisUnit": - return True + core.edit_resource_cost_value(tool.Ifc, tool.Resource, cost_value=tool.Ifc.get().by_id(self.cost_value)) class EnableEditingResourceBaseQuantity(bpy.types.Operator): @@ -583,52 +271,29 @@ class EnableEditingResourceBaseQuantity(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - props = context.scene.BIMResourceProperties - props.active_resource_id = self.resource - props.editing_resource_type = "QUANTITY" + core.enable_editing_resource_base_quantity(tool.Resource, resource=tool.Ifc.get().by_id(self.resource)) return {"FINISHED"} -class AddResourceQuantity(bpy.types.Operator): +class AddResourceQuantity(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_resource_quantity" bl_label = "Add Resource Quantity" bl_options = {"REGISTER", "UNDO"} resource: bpy.props.IntProperty() ifc_class: bpy.props.StringProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.add_resource_quantity", - self.file, - resource=self.file.by_id(self.resource), - ifc_class=self.ifc_class, - ) - Data.load(self.file) - return {"FINISHED"} + core.add_resource_quantity(tool.Ifc, ifc_class=self.ifc_class, resource=tool.Ifc.get().by_id(self.resource)) -class RemoveResourceQuantity(bpy.types.Operator): +class RemoveResourceQuantity(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_resource_quantity" bl_label = "Remove Resource Quantity" bl_options = {"REGISTER", "UNDO"} resource: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.remove_resource_quantity", - self.file, - resource=self.file.by_id(self.resource), - ) - Data.load(self.file) - return {"FINISHED"} + core.remove_resource_quantity(tool.Ifc, resource=tool.Ifc.get().by_id(self.resource)) class EnableEditingResourceQuantity(bpy.types.Operator): @@ -638,11 +303,9 @@ class EnableEditingResourceQuantity(bpy.types.Operator): resource: bpy.props.IntProperty() def execute(self, context): - self.props = context.scene.BIMResourceProperties - self.props.quantity_attributes.clear() - self.props.is_editing_quantity = True - data = Data.resources[self.resource]["BaseQuantity"] - blenderbim.bim.helper.import_attributes(data["type"], self.props.quantity_attributes, data) + core.enable_editing_resource_quantity( + tool.Resource, resource_quantity=tool.Ifc.get().by_id(self.resource).BaseQuantity + ) return {"FINISHED"} @@ -652,29 +315,18 @@ class DisableEditingResourceQuantity(bpy.types.Operator): bl_options = {"REGISTER", "UNDO"} def execute(self, context): - props = context.scene.BIMResourceProperties - props.is_editing_quantity = False + core.disable_editing_resource_quantity(tool.Resource) return {"FINISHED"} -class EditResourceQuantity(bpy.types.Operator): +class EditResourceQuantity(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_resource_quantity" bl_label = "Edit Resource Quantity" bl_options = {"REGISTER", "UNDO"} physical_quantity: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - props = context.scene.BIMResourceProperties - attributes = blenderbim.bim.helper.export_attributes(props.quantity_attributes) - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "resource.edit_resource_quantity", - self.file, - **{"physical_quantity": self.file.by_id(self.physical_quantity), "attributes": attributes}, + core.edit_resource_quantity( + tool.Resource, tool.Ifc, physical_quantity=tool.Ifc.get().by_id(self.physical_quantity) ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.disable_editing_resource_quantity() - return {"FINISHED"} + diff --git a/src/blenderbim/blenderbim/bim/module/resource/ui.py b/src/blenderbim/blenderbim/bim/module/resource/ui.py index 2ad834d88f..778512a120 100644 --- a/src/blenderbim/blenderbim/bim/module/resource/ui.py +++ b/src/blenderbim/blenderbim/bim/module/resource/ui.py @@ -19,7 +19,7 @@ import blenderbim.bim.helper from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore -from ifcopenshell.api.resource.data import Data +from blenderbim.bim.module.resource.data import ResourceData class BIM_PT_resources(Panel): @@ -39,17 +39,21 @@ class BIM_PT_resources(Panel): def draw(self, context): self.props = context.scene.BIMResourceProperties self.tprops = context.scene.BIMResourceTreeProperties - - if not Data.is_loaded: - Data.load(IfcStore.get_file()) + if not ResourceData.is_loaded: + ResourceData.load() row = self.layout.row(align=True) - row.label(text=f"{len(Data.resources)} Resources Found") + if ResourceData.data["has_resources"]: + row.label( + text="{} Resources Found".format(ResourceData.data["number_of_resources_loaded"]), + icon="TEXT", + ) + else: + row.label(text="No Resources found.", icon="COMMUNITY") if self.props.is_editing: row.operator("bim.disable_resource_editing_ui", text="", icon="CANCEL") else: - row.operator("bim.load_resources", text="", icon="GREASEPENCIL") - + row.operator("bim.load_resources", text="Load Resources", icon="GREASEPENCIL") if not self.props.is_editing: return @@ -76,17 +80,17 @@ class BIM_PT_resources(Panel): row = self.layout.row(align=True) op = row.operator("bim.add_resource", text="Add SubContract", icon="TEXT") op.ifc_class = "IfcSubContractResource" - op.resource = 0 + op.parent_resource = 0 op = row.operator("bim.add_resource", text="Add Crew", icon="COMMUNITY") op.ifc_class = "IfcCrewResource" - op.resource = 0 + op.parent_resource = 0 total_resources = len(self.tprops.resources) if not total_resources or self.props.active_resource_index >= total_resources: return ifc_definition_id = self.tprops.resources[self.props.active_resource_index].ifc_definition_id - resource = Data.resources[ifc_definition_id] + resource = ResourceData.data["resources"][ifc_definition_id] if resource["type"] != "IfcSubContractResource": icon_map = { @@ -100,26 +104,13 @@ class BIM_PT_resources(Panel): for ifc_class, icon in icon_map.items(): label = ifc_class.replace("Ifc", "").replace("Construction", "").replace("Resource", "") op = row.operator("bim.add_resource", text=label, icon=icon) - op.resource = ifc_definition_id + op.parent_resource = ifc_definition_id op.ifc_class = ifc_class row = self.layout.row(align=True) row.alignment = "RIGHT" - if self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "ATTRIBUTES": - row.operator("bim.edit_resource", text="", icon="CHECKMARK") - row.operator("bim.disable_editing_resource", text="", icon="CANCEL") - elif self.props.active_resource_id == ifc_definition_id and self.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 self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "COSTS": - row.operator("bim.disable_editing_resource", text="", icon="CANCEL") - elif self.props.active_resource_id == ifc_definition_id and self.props.editing_resource_type == "QUANTITY": - row.operator("bim.disable_editing_resource", text="", icon="CANCEL") - elif self.props.active_resource_id: - row.operator("bim.add_resource", text="", icon="ADD").resource = ifc_definition_id - row.operator("bim.remove_resource", text="", icon="X").resource = ifc_definition_id - else: + if not self.props.active_resource_id: if resource["type"] in ["IfcLaborResource", "IfcConstructionEquipmentResource"]: op = row.operator("bim.calculate_resource_work", text="", icon="TEMP") op.resource = ifc_definition_id @@ -138,7 +129,7 @@ class BIM_PT_resources(Panel): blenderbim.bim.helper.draw_attributes(self.props.resource_time_attributes, self.layout) def draw_editable_resource_quantity_ui(self): - resource = Data.resources[self.props.active_resource_id] + resource = ResourceData.data["resources"][self.props.active_resource_id] if resource["BaseQuantity"]: quantity = resource["BaseQuantity"] @@ -177,16 +168,16 @@ class BIM_PT_resources(Panel): if self.props.cost_types == "CATEGORY": op.cost_category = self.props.cost_category - for cost_value_id in Data.resources[self.props.active_resource_id]["BaseCosts"] or []: + for cost_value_id in ResourceData.data["resources"][self.props.active_resource_id]["BaseCosts"] or []: row = self.layout.row(align=True) self.draw_readonly_cost_value_ui(row, cost_value_id) if self.props.cost_value_editing_type == "ATTRIBUTES": box = self.layout.box() - self.draw_editable_cost_value_ui(box, Data.cost_values[self.props.active_cost_value_id]) + self.draw_editable_cost_value_ui(box, ResourceData.cost_values[self.props.active_cost_value_id]) def draw_readonly_cost_value_ui(self, layout, cost_value_id): - cost_value = Data.cost_values[cost_value_id] + cost_value = ResourceData.cost_values[cost_value_id] if self.props.active_cost_value_id == cost_value_id and self.props.cost_value_editing_type == "FORMULA": layout.prop(self.props, "cost_value_formula", text="") @@ -225,7 +216,7 @@ class BIM_PT_resources(Panel): class BIM_UL_resources(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - resource = Data.resources[item.ifc_definition_id] + resource = ResourceData.data["resources"][item.ifc_definition_id] icon_map = { "IfcSubContractResource": "TEXT", "IfcCrewResource": "COMMUNITY", @@ -251,13 +242,19 @@ class BIM_UL_resources(UIList): else: row.label(text="", icon="DOT") row.prop(item, "name", emboss=False, text="", icon=icon_map[resource["type"]]) - - if context.active_object: + if context.active_object and not props.active_resource_id: oprops = context.active_object.BIMObjectProperties row = layout.row(align=True) - if oprops.ifc_definition_id in Data.resources[item.ifc_definition_id]["ResourceOf"]: + if oprops.ifc_definition_id in ResourceData.data["resources"][item.ifc_definition_id]["ResourceOf"]: op = row.operator("bim.unassign_resource", text="", icon="KEYFRAME_HLT", emboss=False) op.resource = item.ifc_definition_id else: 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.editing_resource_type == "ATTRIBUTES": + row.operator("bim.edit_resource", text="", icon="CHECKMARK") + elif props.editing_resource_type == "USAGE": + row.operator("bim.edit_resource_time", text="", icon="CHECKMARK") + row.operator("bim.disable_editing_resource", text="", icon="CANCEL") diff --git a/src/blenderbim/blenderbim/core/resource.py b/src/blenderbim/blenderbim/core/resource.py new file mode 100644 index 0000000000..046a2608fb --- /dev/null +++ b/src/blenderbim/blenderbim/core/resource.py @@ -0,0 +1,172 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult, Yassine Oualid +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +# ############################################################################ # + + +def load_resources(resource): + resource.load_resources() + resource.load_resource_properties() + + +def add_resource(tool_ifc, resource_tool, ifc_class, parent_resource=None): + tool_ifc.run("resource.add_resource", ifc_class=ifc_class, parent_resource=parent_resource) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def load_resource_properties(resource_tool, resource=None): + resource_tool.load_resource_properties() + + +def disable_editing_resource(resource_tool): + resource_tool.disable_editing_resource() + + +def disable_resource_editing_ui(resource_tool): + resource_tool.disable_resource_editing_ui() + + +def enable_editing_resource(resource_tool, resource): + resource_tool.enable_editing_resource(resource) + resource_tool.load_resource_attributes(resource) + + +def edit_resource(ifc, resource_tool, resource): + attributes = resource_tool.get_resource_attributes() + ifc.run("resource.edit_resource", resource=resource, attributes=attributes) + resource_tool.load_resource_properties() + resource_tool.disable_editing_resource() + + +def remove_resource(ifc, resource_tool, resource=None): + ifc.run("resource.remove_resource", resource=resource) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def enable_editing_resource_time(ifc_tool, resource_tool, resource): + resource_time = resource_tool.get_resource_time(resource) + if resource_time is None: + resource_time = ifc_tool.run("resource.add_resource_time", resource=resource) + resource_tool.enable_editing_resource_time(resource) + resource_tool.load_resource_time_attributes(resource_time) + + +def edit_resource_time(ifc, resource_tool, resource_time): + attributes = resource_tool.get_resource_time_attributes() + ifc.run("resource.edit_resource_time", resource_time=resource_time, attributes=attributes) + resource_tool.disable_editing_resource() + + +def disable_editing_resource_time(resource_tool): + resource_tool.disable_editing_resource() + + +def calculate_resource_work(ifc, resource_tool, resource): + ifc.run("calculate_resource_work", resource=resource) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def enable_editing_resource_costs(resource_tool, resource): + resource_tool.enable_editing_resource_costs(resource) + resource_tool.disable_editing_resource_cost_value() + + +def disable_editing_resource_cost_value(resource_tool): + resource_tool.disable_editing_resource_cost_value() + + +def enable_editing_resource_cost_value(resource_tool, cost_value): + resource_tool.enable_editing_cost_value_attributes(cost_value) + resource_tool.load_cost_value_attributes(cost_value) + + +def enable_editing_resource_cost_value_formula(resource_tool, cost_value): + resource_tool.enable_editing_resource_cost_value_formula(cost_value) + + +def edit_resource_cost_value_formula(ifc, resource_tool, cost_value): + formula = resource_tool.get_resource_cost_value_formula() + ifc.run("cost.edit_cost_value_formula", cost_value=cost_value, formula=formula) + resource_tool.disable_editing_resource_cost_value() + + +def edit_resource_cost_value(ifc, resource_tool, cost_value): + attributes = resource_tool.get_resource_cost_value_attributes() + ifc.run("cost.edit_cost_value", cost_value=cost_value, attributes=attributes) + resource_tool.disable_editing_resource_cost_value() + + +def enable_editing_resource_base_quantity(resource_tool, resource): + resource_tool.enable_editing_resource_base_quantity(resource) + + +def add_resource_quantity(ifc, ifc_class, resource): + ifc.run("resource.add_resource_quantity", resource=resource, ifc_class=ifc_class) + + +def remove_resource_quantity(ifc, resource): + ifc.run("resource.remove_resource_quantity", resource=resource) + + +def enable_editing_resource_quantity(resource_tool, resource_quantity=None): + resource_tool.enable_editing_resource_quantity(resource_quantity) + + +def disable_editing_resource_quantity(resource_tool): + resource_tool.disable_editing_resource_quantity() + + +def edit_resource_quantity(resource_tool, ifc, physical_quantity=None): + attributes = resource_tool.get_resource_quantity_attributes() + ifc.run("resource.edit_resource_quantity", physical_quantity=physical_quantity, attributes=attributes) + resource_tool.disable_editing_resource_quantity() + + +def import_resources(resource_tool, file_path): + resource_tool.import_resources(file_path) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def expand_resource(resource_tool, resource): + resource_tool.expand_resource(resource) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def contract_resource(resource_tool, resource): + resource_tool.contract_resource(resource) + resource_tool.load_resources() + resource_tool.load_resource_properties() + + +def assign_resource(ifc, resource_tool, resource=None, products=None): + if not products: + products = resource_tool.get_selected_products() + for product in products: + rel = ifc.run("resource.assign_resource", relating_resource=resource, related_object=product) + + +def unassign_resource(ifc, resource_tool, resource=None, products=None): + if not products: + products = resource_tool.get_selected_products() + for product in products: + ifc.run("resource.unassign_resource", relating_resource=resource, related_object=product) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 88f207c31f..25f14cea25 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -401,6 +401,33 @@ class Qto: def set_qto_result(cls, result): pass +@interface +class Resource: + def load_resources(cls): pass + def load_resource_properties(cls): pass + def disable_editing_resource(cls): pass + def disable_resource_editing_ui(cls): pass + def load_resource_attributes(cls, resource): pass + def enable_editing_resource(cls, resource): pass + def get_resource_attributes(cls): pass + def enable_editing_resource_time(cls, resource): pass + def get_resource_time(cls, resource): pass + def load_resource_time_attributes(cls, resource_time): pass + def get_resource_time_attributes(cls): pass + def enable_editing_resource_costs(cls, resource): pass + def disable_editing_resource_cost_value(cls): pass + def enable_editing_resource_cost_value_formula(cls, cost_value): pass + def load_cost_value_attributes(cls, cost_value): pass + def enable_editing_cost_value_attributes(cls, cost_value): pass + def get_resource_cost_value_formula(cls): pass + def get_resource_cost_value_attributes(cls): pass + def enable_editing_resource_base_quantity(cls, resource): pass + def enable_editing_resource_quantity(cls, resource_quantity): pass + def disable_editing_resource_quantity(cls): pass + def get_resource_quantity_attributes(cls): pass + def expand_resource(cls, resource): pass + def contract_resource(cls, resource): pass + @interface class Root: def add_dynamic_opening_voids(cls, element, obj): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 62e6410c98..97c45170b6 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -39,6 +39,7 @@ from blenderbim.tool.patch import Patch from blenderbim.tool.project import Project from blenderbim.tool.pset import Pset from blenderbim.tool.qto import Qto +from blenderbim.tool.resource import Resource from blenderbim.tool.root import Root from blenderbim.tool.sequence import Sequence from blenderbim.tool.spatial import Spatial diff --git a/src/blenderbim/blenderbim/tool/resource.py b/src/blenderbim/blenderbim/tool/resource.py new file mode 100644 index 0000000000..62b884c6e1 --- /dev/null +++ b/src/blenderbim/blenderbim/tool/resource.py @@ -0,0 +1,308 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult, Yassine Oualid +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +# ############################################################################ # + +import bpy +import blenderbim.core.tool +import blenderbim.tool as tool +import blenderbim.bim.helper +import blenderbim.bim.module.sequence.helper as helper +import json +import time +import isodate +from datetime import datetime +from dateutil import parser +import ifcopenshell.util.date as ifcdateutils +import ifcopenshell.util.cost +from ifcopenshell.api.unit.data import Data as UnitData + + +class Resource(blenderbim.core.tool.Resource): + @classmethod + def load_resources(cls): + def create_new_resource_li(resource, level_index): + new = bpy.context.scene.BIMResourceTreeProperties.resources.add() + new.ifc_definition_id = resource.id() + new.is_expanded = resource.id() not in contracted_resources + new.level_index = level_index + if resource.IsNestedBy: + new.has_children = True + if new.is_expanded: + for rel in resource.IsNestedBy: + [ + create_new_resource_li(nested_resource, level_index + 1) + for nested_resource in rel.RelatedObjects + ] + + props = bpy.context.scene.BIMResourceProperties + tprops = bpy.context.scene.BIMResourceTreeProperties + tprops.resources.clear() + contracted_resources = json.loads(props.contracted_resources) + + for resource in tool.Ifc.get().by_type("IfcResource"): + if not resource.HasContext: + continue + create_new_resource_li(resource, 0) + props.is_editing = True + + @classmethod + def load_resource_properties(cls): + props = bpy.context.scene.BIMResourceProperties + tprops = bpy.context.scene.BIMResourceTreeProperties + props.is_resource_update_enabled = False + for item in tprops.resources: + resource = tool.Ifc.get().by_id(item.ifc_definition_id) + item.name = resource.Name if resource else "Unnamed" + props.is_resource_update_enabled = True + + @classmethod + def disable_editing_resource(cls): + bpy.context.scene.BIMResourceProperties.active_resource_id = 0 + bpy.context.scene.BIMResourceProperties.active_resource_time_id = 0 + + @classmethod + def disable_resource_editing_ui(cls): + bpy.context.scene.BIMResourceProperties.is_editing = False + + @classmethod + def load_resource_attributes(cls, resource): + blenderbim.bim.helper.import_attributes2(resource, bpy.context.scene.BIMResourceProperties.resource_attributes) + + @classmethod + def enable_editing_resource(cls, resource): + props = bpy.context.scene.BIMResourceProperties + props.active_resource_id = resource.id() + props.resource_attributes.clear() + props.editing_resource_type = "ATTRIBUTES" + + @classmethod + def get_resource_attributes(cls): + return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMResourceProperties.resource_attributes) + + @classmethod + def enable_editing_resource_time(cls, resource): + props = bpy.context.scene.BIMResourceProperties + props.resource_time_attributes.clear() + props.active_resource_time_id = resource.Usage.id() + props.active_resource_id = resource.id() + props.editing_resource_type = "USAGE" + + @classmethod + def get_resource_time(cls, resource): + return resource.Usage if resource.Usage else None + + @classmethod + def load_resource_time_attributes(cls, resource_time): + def callback(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 ifcdateutils.datetime2ifc(data[name], "IfcDuration") + return True + + blenderbim.bim.helper.import_attributes2( + resource_time, bpy.context.scene.BIMResourceProperties.resource_time_attributes, callback + ) + + @classmethod + def get_resource_time_attributes(cls): + def callback(attributes, prop): + if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime": + if prop.is_null: + attributes[prop.name] = None + return True + attributes[prop.name] = helper.parse_datetime(prop.string_value) + return True + elif prop.name == "LevelingDelay" or "Work" in prop.name: + if prop.is_null: + attributes[prop.name] = None + return True + attributes[prop.name] = helper.parse_duration(prop.string_value) + return True + + props = bpy.context.scene.BIMResourceProperties + return blenderbim.bim.helper.export_attributes(props.resource_time_attributes, callback) + + @classmethod + def enable_editing_resource_costs(cls, resource): + props = bpy.context.scene.BIMResourceProperties + props.active_resource_id = resource.id() + props.editing_resource_type = "COSTS" + resource + + @classmethod + def disable_editing_resource_cost_value(cls): + props = bpy.context.scene.BIMResourceProperties + props.active_cost_value_id = 0 + props.cost_value_editing_type = "" + + @classmethod + def enable_editing_resource_cost_value_formula(cls, cost_value): + props = bpy.context.scene.BIMResourceProperties + props.cost_value_attributes.clear() + props.active_cost_value_id = cost_value.id() + props.cost_value_editing_type = "FORMULA" + props.cost_value_formula = ifcopenshell.util.cost.serialise_cost_value(cost_value) if cost_value else "" + + @classmethod + def load_cost_value_attributes(cls, cost_value): + def callback(name, prop, data): + if name == "AppliedValue": + # TODO: for now, only support simple IfcValues (which are effectively IfcMonetaryMeasure) + prop = props.cost_value_attributes.add() + prop.data_type = "float" + prop.name = "AppliedValue" + prop.is_optional = True + prop.float_value = 0.0 if prop.is_null or not data[name] else data[name][0] + return True + elif name == "UnitBasis": + prop = props.cost_value_attributes.add() + prop.name = "UnitBasisValue" + prop.data_type = "float" + prop.is_null = data["UnitBasis"] is None + prop.is_optional = True + if data["UnitBasis"]: + prop.float_value = data["UnitBasis"].ValueComponent[0] or 0 + else: + prop.float_value = 0 + + prop = props.cost_value_attributes.add() + prop.name = "UnitBasisUnit" + prop.data_type = "enum" + prop.is_null = prop.is_optional = False + units = {} + if not UnitData.is_loaded: + UnitData.load(tool.Ifc.get()) + for unit_id, unit in UnitData.units.items(): + if unit.get("UnitType", None) in [ + "AREAUNIT", + "LENGTHUNIT", + "TIMEUNIT", + "VOLUMEUNIT", + "MASSUNIT", + "USERDEFINED", + ]: + if unit["type"] == "IfcContextDependentUnit": + units[unit_id] = f"{unit['UnitType']} / {unit['Name']}" + else: + name = unit["Name"] + if unit.get("Prefix", None): + name = f"(unit['Prefix']) {name}" + units[unit_id] = f"{unit['UnitType']} / {name}" + prop.enum_items = json.dumps(units) + if data["UnitBasis"] and data["UnitBasis"].UnitComponent: + name = data["UnitBasis"].UnitComponent.Name + unit_type = data["UnitBasis"].UnitComponent.UnitType + prop.enum_value = str(data["UnitBasis"].UnitComponent.id()) + return True + + props = bpy.context.scene.BIMResourceProperties + blenderbim.bim.helper.import_attributes2(cost_value, props.cost_value_attributes, callback) + + @classmethod + def enable_editing_cost_value_attributes(cls, cost_value): + props = bpy.context.scene.BIMResourceProperties + props.cost_value_attributes.clear() + props.active_cost_value_id = cost_value.id() + props.cost_value_editing_type = "ATTRIBUTES" + + @classmethod + def get_resource_cost_value_formula(cls): + return bpy.context.scene.BIMResourceProperties.cost_value_formula + + @classmethod + def get_resource_cost_value_attributes(cls): + def callback(attributes, prop): + if prop.name == "UnitBasisValue": + if prop.is_null: + attributes["UnitBasis"] = None + return True + attributes["UnitBasis"] = { + "ValueComponent": prop.float_value or 1, + "UnitComponent": tool.Ifc.get().by_id( + int( + bpy.context.scene.BIMResourceProperties.cost_value_attributes.get( + "UnitBasisUnit" + ).enum_value + ) + ), + } + return True + if prop.name == "UnitBasisUnit": + return True + + return blenderbim.bim.helper.export_attributes( + bpy.context.scene.BIMResourceProperties.cost_value_attributes, callback + ) + + @classmethod + def enable_editing_resource_base_quantity(cls, resource): + props = bpy.context.scene.BIMResourceProperties + props.active_resource_id = resource.id() + props.editing_resource_type = "QUANTITY" + + @classmethod + def enable_editing_resource_quantity(cls, resource_quantity): + props = bpy.context.scene.BIMResourceProperties + props.quantity_attributes.clear() + props.is_editing_quantity = True + blenderbim.bim.helper.import_attributes2(resource_quantity, props.quantity_attributes) + + @classmethod + def disable_editing_resource_quantity(cls): + bpy.context.scene.BIMResourceProperties.is_editing_quantity = False + + @classmethod + def get_resource_quantity_attributes(cls): + return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMResourceProperties.quantity_attributes) + + @classmethod + def expand_resource(cls, resource): + props = bpy.context.scene.BIMResourceProperties + contracted_resources = json.loads(props.contracted_resources) + contracted_resources.remove(resource.id()) + props.contracted_resources = json.dumps(contracted_resources) + + @classmethod + def contract_resource(cls, resource): + props = bpy.context.scene.BIMResourceProperties + contracted_resources = json.loads(props.contracted_resources) + contracted_resources.append(resource.id()) + props.contracted_resources = json.dumps(contracted_resources) + + @classmethod + def get_selected_products(cls): + return [ + tool.Ifc.get_entity(obj) + for obj in bpy.context.selected_objects + if obj.BIMObjectProperties.ifc_definition_id + ] or [] + + @classmethod + def import_resources(cls, file_path): + from ifc4d.csv2ifc import Csv2Ifc + + start = time.time() + p62ifc = Csv2Ifc() + p62ifc.csv = file_path + p62ifc.file = tool.Ifc.get() + p62ifc.execute() + print("Importing Resources CSV finished in {:.2f} seconds".format(time.time() - start))