diff --git a/src/blenderbim/blenderbim/bim/module/document/data.py b/src/blenderbim/blenderbim/bim/module/document/data.py new file mode 100644 index 0000000000..3718cb46a5 --- /dev/null +++ b/src/blenderbim/blenderbim/bim/module/document/data.py @@ -0,0 +1,84 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult +# +# 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 ifcopenshell +import ifcopenshell.util.schema +import blenderbim.tool as tool + + +def refresh(): + DocumentData.is_loaded = False + ObjectDocumentData.is_loaded = False + + +class DocumentData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "total_information": cls.total_information(), + "total_references": cls.total_references(), + } + cls.is_loaded = True + + @classmethod + def total_information(cls): + return len(tool.Ifc.get().by_type("IfcDocumentInformation")) + + @classmethod + def total_references(cls): + return len(tool.Ifc.get().by_type("IfcDocumentReference")) + + +class ObjectDocumentData: + data = {} + is_loaded = False + + @classmethod + def load(cls): + cls.data = { + "documents": cls.documents(), + } + cls.is_loaded = True + + @classmethod + def documents(cls): + results = [] + element = tool.Ifc.get_entity(bpy.context.active_object) + if not element: + return results + for rel in getattr(element, "HasAssociations", []): + if rel.is_a("IfcRelAssociatesDocument"): + if not rel.RelatingDocument.is_a("IfcDocumentReference"): + continue + if tool.Ifc.get_schema() == "IFC2X3": + identification = rel.RelatingDocument.ItemReference + else: + identification = rel.RelatingDocument.Identification + results.append( + { + "type": rel.RelatingDocument.is_a(), + "id": rel.RelatingDocument.id(), + "identification": identification, + "name": rel.RelatingDocument.Name, + } + ) + return results diff --git a/src/blenderbim/blenderbim/bim/module/document/operator.py b/src/blenderbim/blenderbim/bim/module/document/operator.py index a81a053524..d97070a38d 100644 --- a/src/blenderbim/blenderbim/bim/module/document/operator.py +++ b/src/blenderbim/blenderbim/bim/module/document/operator.py @@ -1,5 +1,5 @@ # BlenderBIM Add-on - OpenBIM Blender Add-on -# Copyright (C) 2020, 2021 Dion Moult +# Copyright (C) 2020, 2021, 2022 Dion Moult # # This file is part of BlenderBIM Add-on. # @@ -20,303 +20,155 @@ import bpy import json import ifcopenshell.api import ifcopenshell.util.attribute +import blenderbim.tool as tool +import blenderbim.core.document as core from blenderbim.bim.ifc import IfcStore from ifcopenshell.api.document.data import Data -class LoadInformation(bpy.types.Operator): +class LoadInformation(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.load_information" bl_label = "Load Information" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - self.file = IfcStore.get_file() - props = context.scene.BIMDocumentProperties - props.documents.clear() - for information_id, information in Data.information.items(): - new = props.documents.add() - new.name = information["Name"] or "Unnamed" - if self.file.schema == "IFC2X3": - new.identification = information["DocumentId"] or "*" - else: - new.identification = information["Identification"] or "*" - new.ifc_definition_id = information_id - props.is_editing = "information" - bpy.ops.bim.disable_editing_document() - return {"FINISHED"} + def _execute(self, context): + core.load_information(tool.Document) -class LoadDocumentReferences(bpy.types.Operator): +class LoadDocumentReferences(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.load_document_references" bl_label = "Load Document References" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - self.file = IfcStore.get_file() - props = context.scene.BIMDocumentProperties - props.documents.clear() - for reference_id, reference in Data.references.items(): - new = props.documents.add() - new.name = reference["Name"] or "Unnamed" - if self.file.schema == "IFC2X3": - new.identification = reference["ItemReference"] or "*" - else: - new.identification = reference["Identification"] or "*" - new.ifc_definition_id = reference_id - props.is_editing = "reference" - bpy.ops.bim.disable_editing_document() - return {"FINISHED"} + def _execute(self, context): + core.load_references(tool.Document) -class DisableDocumentEditingUI(bpy.types.Operator): +class DisableDocumentEditingUI(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.disable_document_editing_ui" bl_label = "Disable Document Editing UI" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - context.scene.BIMDocumentProperties.is_editing = "" - bpy.ops.bim.disable_editing_document() - return {"FINISHED"} + def _execute(self, context): + core.disable_document_editing_ui(tool.Document) -class EnableEditingDocument(bpy.types.Operator): +class EnableEditingDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_editing_document" bl_label = "Enable Editing Document" bl_options = {"REGISTER", "UNDO"} document: bpy.props.IntProperty() - def execute(self, context): - props = context.scene.BIMDocumentProperties - props.document_attributes.clear() - - if props.is_editing == "information": - data = Data.information[self.document] - ifc_class = "IfcDocumentInformation" - elif props.is_editing == "reference": - data = Data.references[self.document] - ifc_class = "IfcDocumentReference" - - for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes(): - data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) - if data_type == "entity": - continue - new = props.document_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_document_id = self.document - return {"FINISHED"} + def _execute(self, context): + core.enable_editing_document(tool.Document, document=tool.Ifc.get().by_id(self.document)) -class DisableEditingDocument(bpy.types.Operator): +class DisableEditingDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.disable_editing_document" bl_label = "Disable Editing Document" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - context.scene.BIMDocumentProperties.active_document_id = 0 - return {"FINISHED"} + def _execute(self, context): + core.disable_editing_document(tool.Document) -class AddInformation(bpy.types.Operator): +class AddInformation(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_information" bl_label = "Add Information" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - result = ifcopenshell.api.run("document.add_information", IfcStore.get_file()) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_information() - bpy.ops.bim.enable_editing_document(document=result.id()) - return {"FINISHED"} + core.add_information(tool.Ifc, tool.Document) -class AddDocumentReference(bpy.types.Operator): +class AddDocumentReference(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.add_document_reference" bl_label = "Add Document Reference" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - result = ifcopenshell.api.run("document.add_reference", IfcStore.get_file()) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_document_references() - bpy.ops.bim.enable_editing_document(document=result.id()) - return {"FINISHED"} + core.add_reference(tool.Ifc, tool.Document) -class EditInformation(bpy.types.Operator): +class EditInformation(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_information" bl_label = "Edit Information" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMDocumentProperties - attributes = {} - for attribute in props.document_attributes: - if attribute.is_null: - attributes[attribute.name] = None - elif attribute.enum_items: - attributes[attribute.name] = attribute.enum_value - else: - attributes[attribute.name] = attribute.string_value - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "document.edit_information", - self.file, - **{"information": self.file.by_id(props.active_document_id), "attributes": attributes} - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_information() - return {"FINISHED"} + core.edit_information(tool.Ifc, tool.Document, information=tool.Ifc.get().by_id(props.active_document_id)) -class EditDocumentReference(bpy.types.Operator): +class EditDocumentReference(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.edit_document_reference" bl_label = "Edit Document Reference" bl_options = {"REGISTER", "UNDO"} - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): props = context.scene.BIMDocumentProperties - attributes = {} - for attribute in props.document_attributes: - if attribute.is_null: - attributes[attribute.name] = None - else: - attributes[attribute.name] = attribute.string_value - self.file = IfcStore.get_file() - ifcopenshell.api.run( - "document.edit_reference", - self.file, - **{"reference": self.file.by_id(props.active_document_id), "attributes": attributes} - ) - Data.load(IfcStore.get_file()) - bpy.ops.bim.load_document_references() - return {"FINISHED"} + core.edit_reference(tool.Ifc, tool.Document, reference=tool.Ifc.get().by_id(props.active_document_id)) -class RemoveDocument(bpy.types.Operator): +class RemoveDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.remove_document" bl_label = "Remove Document" bl_options = {"REGISTER", "UNDO"} document: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - props = context.scene.BIMDocumentProperties - self.file = IfcStore.get_file() - ifcopenshell.api.run("document.remove_document", self.file, **{"document": self.file.by_id(self.document)}) - Data.load(IfcStore.get_file()) - if props.is_editing == "information": - bpy.ops.bim.load_information() - elif props.is_editing == "reference": - bpy.ops.bim.load_document_references() - return {"FINISHED"} + core.remove_document(tool.Ifc, tool.Document, document=tool.Ifc.get().by_id(self.document)) -class EnableAssigningDocument(bpy.types.Operator): +class EnableAssigningDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.enable_assigning_document" bl_label = "Enable Assigning Document" bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() - def execute(self, context): + def _execute(self, context): obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object - props = obj.BIMObjectDocumentProperties - if props.available_document_types == "IfcDocumentInformation": - bpy.ops.bim.load_information() - elif props.available_document_types == "IfcDocumentReference": - bpy.ops.bim.load_document_references() - props.is_adding = props.available_document_types - return {"FINISHED"} + core.enable_assigning_document(tool.Document, obj) -class DisableAssigningDocument(bpy.types.Operator): +class DisableAssigningDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.disable_assigning_document" bl_label = "Disable Assigning Document" bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() - def execute(self, context): + def _execute(self, context): obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object - props = obj.BIMObjectDocumentProperties - props.is_adding = "" - return {"FINISHED"} + core.enable_assigning_document(tool.Document, obj) -class AssignDocument(bpy.types.Operator): +class AssignDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.assign_document" bl_label = "Assign Document" bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() document: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - self.file = IfcStore.get_file() + document = tool.Ifc.get().by_id(self.document) objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects for obj in objs: - obj_id = obj.BIMObjectProperties.ifc_definition_id - if not obj_id: - continue - ifcopenshell.api.run( - "document.assign_document", - self.file, - **{ - "product": self.file.by_id(obj_id), - "document": self.file.by_id(self.document), - } - ) - Data.load(self.file, obj_id) - return {"FINISHED"} + element = tool.Ifc.get_entity(obj) + if element: + core.assign_document(tool.Ifc, product=element, document=document) -class UnassignDocument(bpy.types.Operator): +class UnassignDocument(bpy.types.Operator, tool.Ifc.Operator): bl_idname = "bim.unassign_document" bl_label = "Unassign Document" bl_options = {"REGISTER", "UNDO"} obj: bpy.props.StringProperty() document: bpy.props.IntProperty() - def execute(self, context): - return IfcStore.execute_ifc_operator(self, context) - def _execute(self, context): - self.file = IfcStore.get_file() + document = tool.Ifc.get().by_id(self.document) objs = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects for obj in objs: - obj_id = obj.BIMObjectProperties.ifc_definition_id - if not obj_id: - continue - ifcopenshell.api.run( - "document.unassign_document", - self.file, - **{ - "product": self.file.by_id(obj_id), - "document": self.file.by_id(self.document), - } - ) - Data.load(self.file, obj_id) - return {"FINISHED"} + element = tool.Ifc.get_entity(obj) + if element: + core.unassign_document(tool.Ifc, product=element, document=document) diff --git a/src/blenderbim/blenderbim/bim/module/document/ui.py b/src/blenderbim/blenderbim/bim/module/document/ui.py index cf603fc1af..a13995f694 100644 --- a/src/blenderbim/blenderbim/bim/module/document/ui.py +++ b/src/blenderbim/blenderbim/bim/module/document/ui.py @@ -19,7 +19,7 @@ from bpy.types import Panel, UIList from blenderbim.bim.ifc import IfcStore from blenderbim.bim.helper import draw_attributes -from ifcopenshell.api.document.data import Data +from blenderbim.bim.module.document.data import DocumentData, ObjectDocumentData class BIM_PT_documents(Panel): @@ -36,14 +36,14 @@ class BIM_PT_documents(Panel): return IfcStore.get_file() def draw(self, context): - if not Data.is_loaded: - Data.load(IfcStore.get_file()) + if not DocumentData.is_loaded: + DocumentData.load() self.props = context.scene.BIMDocumentProperties if not self.props.is_editing or self.props.is_editing == "information": row = self.layout.row(align=True) - row.label(text="{} Documents Found".format(len(Data.information)), icon="FILE") + row.label(text="{} Documents Found".format(DocumentData.data["total_information"]), icon="FILE") if self.props.is_editing == "information": row.operator("bim.add_information", text="", icon="ADD") row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL") @@ -52,7 +52,7 @@ class BIM_PT_documents(Panel): if not self.props.is_editing or self.props.is_editing == "reference": row = self.layout.row(align=True) - row.label(text="{} References Found".format(len(Data.references)), icon="FILE_HIDDEN") + row.label(text="{} References Found".format(DocumentData.data["total_references"]), icon="FILE_HIDDEN") if self.props.is_editing == "reference": row.operator("bim.add_document_reference", text="", icon="ADD") row.operator("bim.disable_document_editing_ui", text="", icon="CANCEL") @@ -89,43 +89,26 @@ class BIM_PT_object_documents(Panel): return bool(context.active_object.BIMObjectProperties.ifc_definition_id) def draw(self, context): + if not ObjectDocumentData.is_loaded: + ObjectDocumentData.load() + obj = context.active_object self.oprops = obj.BIMObjectProperties self.sprops = context.scene.BIMDocumentProperties self.props = obj.BIMObjectDocumentProperties self.file = IfcStore.get_file() - if not Data.is_loaded: - Data.load(IfcStore.get_file()) - if self.oprops.ifc_definition_id not in Data.products: - Data.load(IfcStore.get_file(), self.oprops.ifc_definition_id) self.draw_add_ui() - document_ids = Data.products[self.oprops.ifc_definition_id] - - if not document_ids: + if not ObjectDocumentData.data["documents"]: row = self.layout.row(align=True) row.label(text="No Documents", icon="FILE") - for document_id in document_ids: - try: - document = Data.information[document_id] - document_type = "IfcDocumentInformation" - icon = "FILE" - except: - document = Data.references[document_id] - document_type = "IfcDocumentReference" - icon = "FILE_HIDDEN" + for document in ObjectDocumentData.data["documents"]: row = self.layout.row(align=True) - if self.file.schema == "IFC2X3": - if document_type == "IfcDocumentInformation": - row.label(text=document.get("DocumentId") or "*", icon=icon) - elif document_type == "IfcDocumentReference": - row.label(text=document.get("ItemReference") or "*", icon=icon) - else: - row.label(text=document.get("Identification") or "*", icon=icon) - row.label(text=document.get("Name") or "Unnamed") - row.operator("bim.unassign_document", text="", icon="X").document = document_id + row.label(text=document["identification"] or "*", icon="FILE") + row.label(text=document["name"] or "Unnamed") + row.operator("bim.unassign_document", text="", icon="X").document = document["id"] def draw_add_ui(self): if self.props.is_adding: diff --git a/src/blenderbim/blenderbim/tool/document.py b/src/blenderbim/blenderbim/tool/document.py new file mode 100644 index 0000000000..a8b80b975d --- /dev/null +++ b/src/blenderbim/blenderbim/tool/document.py @@ -0,0 +1,93 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult +# +# 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 ifcopenshell.util.system +import blenderbim.core.tool +import blenderbim.tool as tool +from blenderbim.bim import import_ifc + + +class Document(blenderbim.core.tool.Document): + @classmethod + def disable_document_assignment_ui(cls, obj): + obj.BIMObjectDocumentProperties.is_adding = "" + + @classmethod + def disable_editing_document(cls): + bpy.context.scene.BIMDocumentProperties.active_document_id = 0 + + @classmethod + def disable_editing_ui(cls): + bpy.context.scene.BIMDocumentProperties.is_editing = "" + + @classmethod + def enable_document_assignment_ui(cls, obj): + obj.BIMObjectDocumentProperties.is_adding = "IfcDocumentReference" + + @classmethod + def enable_information_editing_ui(cls): + bpy.context.scene.BIMDocumentProperties.is_editing = "information" + + @classmethod + def enable_reference_editing_ui(cls): + bpy.context.scene.BIMDocumentProperties.is_editing = "reference" + + @classmethod + def export_document_attributes(cls): + return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMDocumentProperties.document_attributes) + + @classmethod + def import_document_attributes(cls, document): + props = bpy.context.scene.BIMDocumentProperties + props.document_attributes.clear() + blenderbim.bim.helper.import_attributes2(document, props.document_attributes) + + @classmethod + def import_information(cls): + props = bpy.context.scene.BIMDocumentProperties + props.documents.clear() + for element in tool.Ifc.get().by_type("IfcDocumentInformation"): + new = props.documents.add() + new.ifc_definition_id = element.id() + new.name = element.Name or "Unnamed" + if tool.Ifc.get_schema() == "IFC2X3": + new.identification = element.DocumentId or "*" + else: + new.identification = element.Identification or "*" + + @classmethod + def import_references(cls): + props = bpy.context.scene.BIMDocumentProperties + props.documents.clear() + for element in tool.Ifc.get().by_type("IfcDocumentReference"): + new = props.documents.add() + new.ifc_definition_id = element.id() + new.name = element.Name or "Unnamed" + if tool.Ifc.get_schema() == "IFC2X3": + new.identification = element.ItemReference or "*" + else: + new.identification = element.Identification or "*" + + @classmethod + def is_document_information(cls, document): + return document.is_a("IfcDocumentInformation") + + @classmethod + def set_active_document(cls, document): + bpy.context.scene.BIMDocumentProperties.active_document_id = document.id() diff --git a/src/blenderbim/test/core/bootstrap.py b/src/blenderbim/test/core/bootstrap.py index 31e87c9e9b..d18f7e815c 100644 --- a/src/blenderbim/test/core/bootstrap.py +++ b/src/blenderbim/test/core/bootstrap.py @@ -77,6 +77,13 @@ def demo(): prophet.verify() +@pytest.fixture +def document(): + prophet = Prophecy(blenderbim.core.tool.Document) + yield prophet + prophet.verify() + + @pytest.fixture def drawing(): prophet = Prophecy(blenderbim.core.tool.Drawing) diff --git a/src/blenderbim/test/core/test_document.py b/src/blenderbim/test/core/test_document.py new file mode 100644 index 0000000000..cbd9ed3e01 --- /dev/null +++ b/src/blenderbim/test/core/test_document.py @@ -0,0 +1,136 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult +# +# 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 blenderbim.core.document as subject +from test.core.bootstrap import ifc, document + + +class TestLoadInformation: + def test_run(self, document): + document.import_information().should_be_called() + document.enable_information_editing_ui().should_be_called() + document.disable_editing_document().should_be_called() + subject.load_information(document) + + +class TestLoadReferences: + def test_run(self, document): + document.import_references().should_be_called() + document.enable_reference_editing_ui().should_be_called() + document.disable_editing_document().should_be_called() + subject.load_references(document) + + +class TestDisableDocumentEditingUi: + def test_run(self, document): + document.disable_editing_ui().should_be_called() + document.disable_editing_document().should_be_called() + subject.disable_document_editing_ui(document) + + +class TestEnableEditingDocument: + def test_run(self, document): + document.import_document_attributes("document").should_be_called() + document.set_active_document("document").should_be_called() + subject.enable_editing_document(document, document="document") + + +class TestDisableEditingDocument: + def test_run(self, document): + document.disable_editing_document().should_be_called() + subject.disable_editing_document(document) + + +class TestAddInformation: + def test_run(self, ifc, document): + ifc.run("document.add_information").should_be_called().will_return("information") + document.import_information().should_be_called() + document.import_document_attributes("information").should_be_called() + document.set_active_document("information").should_be_called() + subject.add_information(ifc, document) + + +class TestAddReference: + def test_run(self, ifc, document): + ifc.run("document.add_reference").should_be_called().will_return("reference") + document.import_references().should_be_called() + document.import_document_attributes("reference").should_be_called() + document.set_active_document("reference").should_be_called() + subject.add_reference(ifc, document) + + +class TestEditInformation: + def test_run(self, ifc, document): + document.export_document_attributes().should_be_called().will_return("attributes") + ifc.run("document.edit_information", information="information", attributes="attributes").should_be_called() + document.disable_editing_document().should_be_called() + document.import_information().should_be_called() + subject.edit_information(ifc, document, information="information") + + +class TestEditInformation: + def test_run(self, ifc, document): + document.export_document_attributes().should_be_called().will_return("attributes") + ifc.run("document.edit_reference", reference="reference", attributes="attributes").should_be_called() + document.disable_editing_document().should_be_called() + document.import_references().should_be_called() + subject.edit_reference(ifc, document, reference="reference") + + +class TestRemoveDocument: + def test_remove_information(self, ifc, document): + document.is_document_information("document").should_be_called().will_return(True) + ifc.run("document.remove_document", document="document").should_be_called() + document.import_information().should_be_called() + document.disable_editing_document().should_be_called() + subject.remove_document(ifc, document, document="document") + + def test_remove_reference(self, ifc, document): + document.is_document_information("document").should_be_called().will_return(False) + ifc.run("document.remove_document", document="document").should_be_called() + document.import_references().should_be_called() + document.disable_editing_document().should_be_called() + subject.remove_document(ifc, document, document="document") + + +class TestEnableAssigningDocument: + def test_run(self, document): + document.import_references().should_be_called() + document.enable_reference_editing_ui().should_be_called() + document.disable_editing_document().should_be_called() + document.enable_document_assignment_ui("obj").should_be_called() + subject.enable_assigning_document(document, obj="obj") + + +class TestDisableAssigningDocument: + def test_run(self, document): + document.disable_document_assignment_ui("obj").should_be_called() + subject.disable_assigning_document(document, obj="obj") + + +class TestAssignDocument: + def test_run(self, ifc): + ifc.run("document.assign_document", product="product", document="document").should_be_called() + subject.assign_document(ifc, product="product", document="document") + + +class TestUnassignDocument: + def test_run(self, ifc): + ifc.run("document.unassign_document", product="product", document="document").should_be_called() + subject.unassign_document(ifc, product="product", document="document") diff --git a/src/blenderbim/test/tool/test_document.py b/src/blenderbim/test/tool/test_document.py new file mode 100644 index 0000000000..8353e5c8f2 --- /dev/null +++ b/src/blenderbim/test/tool/test_document.py @@ -0,0 +1,192 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 Dion Moult +# +# 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 ifcopenshell +import blenderbim.core.tool +import blenderbim.tool as tool +from test.bim.bootstrap import NewFile +from blenderbim.tool.document import Document as subject + + +class TestImplementsTool(NewFile): + def test_run(self): + assert isinstance(subject(), blenderbim.core.tool.Document) + + +class TestDisableDocumentAssignmentUI(NewFile): + def test_run(self): + obj = bpy.data.objects.new("Object", None) + obj.BIMObjectDocumentProperties.is_adding = "foo" + subject.disable_document_assignment_ui(obj) + assert obj.BIMObjectDocumentProperties.is_adding == "" + + +class TestDisableEditingDocument(NewFile): + def test_run(self): + bpy.context.scene.BIMDocumentProperties.active_document_id = 1 + subject.disable_editing_document() + assert bpy.context.scene.BIMDocumentProperties.active_document_id == 0 + + +class TestDisableEditingUI(NewFile): + def test_run(self): + bpy.context.scene.BIMDocumentProperties.is_editing = "is_editing" + subject.disable_editing_ui() + assert bpy.context.scene.BIMDocumentProperties.is_editing == "" + + +class TestEnableDocumentAssignmentUI(NewFile): + def test_run(self): + obj = bpy.data.objects.new("Object", None) + obj.BIMObjectDocumentProperties.is_adding = "" + subject.enable_document_assignment_ui(obj) + assert obj.BIMObjectDocumentProperties.is_adding == "IfcDocumentReference" + + +class TestEnableInformationEditingUI(NewFile): + def test_run(self): + bpy.context.scene.BIMDocumentProperties.is_editing = "" + subject.enable_information_editing_ui() + assert bpy.context.scene.BIMDocumentProperties.is_editing == "information" + + +class TestEnableReferenceEditingUI(NewFile): + def test_run(self): + bpy.context.scene.BIMDocumentProperties.is_editing = "" + subject.enable_reference_editing_ui() + assert bpy.context.scene.BIMDocumentProperties.is_editing == "reference" + + +class TestExportDocumentAttributes(NewFile): + def test_exporting_information(self): + TestImportDocumentAttributes().test_importing_information() + assert subject.export_document_attributes() == { + "Identification": "Identification", + "Name": "Name", + "Description": "Description", + "Location": "Location", + "Purpose": "Purpose", + "IntendedUse": "IntendedUse", + "Scope": "Scope", + "Revision": "Revision", + "CreationTime": "CreationTime", + "LastRevisionTime": "LastRevisionTime", + "ElectronicFormat": "ElectronicFormat", + "ValidFrom": "ValidFrom", + "ValidUntil": "ValidUntil", + "Confidentiality": "CONFIDENTIAL", + "Status": "DRAFT", + } + + +class TestImportDocumentAttributes(NewFile): + def test_importing_information(self): + ifc = ifcopenshell.file() + tool.Ifc().set(ifc) + document = ifc.createIfcDocumentInformation() + document.Identification = "Identification" + document.Name = "Name" + document.Description = "Description" + document.Location = "Location" + document.Purpose = "Purpose" + document.IntendedUse = "IntendedUse" + document.Scope = "Scope" + document.Revision = "Revision" + document.CreationTime = "CreationTime" + document.LastRevisionTime = "LastRevisionTime" + document.ElectronicFormat = "ElectronicFormat" + document.ValidFrom = "ValidFrom" + document.ValidUntil = "ValidUntil" + document.Confidentiality = "CONFIDENTIAL" + document.Status = "DRAFT" + subject().import_document_attributes(document) + props = bpy.context.scene.BIMDocumentProperties + assert props.document_attributes.get("Identification").string_value == "Identification" + assert props.document_attributes.get("Name").string_value == "Name" + assert props.document_attributes.get("Description").string_value == "Description" + assert props.document_attributes.get("Location").string_value == "Location" + assert props.document_attributes.get("Purpose").string_value == "Purpose" + assert props.document_attributes.get("IntendedUse").string_value == "IntendedUse" + assert props.document_attributes.get("Scope").string_value == "Scope" + assert props.document_attributes.get("Revision").string_value == "Revision" + assert props.document_attributes.get("CreationTime").string_value == "CreationTime" + assert props.document_attributes.get("LastRevisionTime").string_value == "LastRevisionTime" + assert props.document_attributes.get("ElectronicFormat").string_value == "ElectronicFormat" + assert props.document_attributes.get("ValidFrom").string_value == "ValidFrom" + assert props.document_attributes.get("ValidUntil").string_value == "ValidUntil" + assert props.document_attributes.get("Confidentiality").enum_value == "CONFIDENTIAL" + assert props.document_attributes.get("Status").enum_value == "DRAFT" + + def test_importing_reference(self): + ifc = ifcopenshell.file() + tool.Ifc().set(ifc) + document = ifc.createIfcDocumentInformation() + document.Location = "Location" + document.Identification = "Identification" + document.Name = "Name" + document.Description = "Description" + subject().import_document_attributes(document) + props = bpy.context.scene.BIMDocumentProperties + assert props.document_attributes.get("Location").string_value == "Location" + assert props.document_attributes.get("Identification").string_value == "Identification" + assert props.document_attributes.get("Name").string_value == "Name" + assert props.document_attributes.get("Description").string_value == "Description" + + +class TestImportInformation(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc().set(ifc) + document = ifc.createIfcDocumentInformation() + subject.import_information() + props = bpy.context.scene.BIMDocumentProperties + assert len(props.documents) == 1 + assert props.documents[0].ifc_definition_id == document.id() + assert props.documents[0].name == "Unnamed" + assert props.documents[0].identification == "*" + + +class TestImportReference(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc().set(ifc) + document = ifc.createIfcDocumentReference() + subject.import_references() + props = bpy.context.scene.BIMDocumentProperties + assert len(props.documents) == 1 + assert props.documents[0].ifc_definition_id == document.id() + assert props.documents[0].name == "Unnamed" + assert props.documents[0].identification == "*" + + +class TestIsDocumentInformation(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + information = ifc.createIfcDocumentInformation() + reference = ifc.createIfcDocumentReference() + assert subject.is_document_information(information) is True + assert subject.is_document_information(reference) is False + + +class TestSetActiveDocument(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + document = ifc.createIfcDocumentInformation() + subject.set_active_document(document) + assert bpy.context.scene.BIMDocumentProperties.active_document_id == document.id()