diff --git a/src/blenderbim/libraries/blenderbim-demo-library.ifc b/src/blenderbim/blenderbim/bim/data/libraries/IFC4 Demo Library.ifc similarity index 100% rename from src/blenderbim/libraries/blenderbim-demo-library.ifc rename to src/blenderbim/blenderbim/bim/data/libraries/IFC4 Demo Library.ifc diff --git a/src/blenderbim/blenderbim/bim/module/aggregate/ui.py b/src/blenderbim/blenderbim/bim/module/aggregate/ui.py index c092e27adb..9f9ff240e5 100644 --- a/src/blenderbim/blenderbim/bim/module/aggregate/ui.py +++ b/src/blenderbim/blenderbim/bim/module/aggregate/ui.py @@ -75,7 +75,7 @@ class BIM_PT_aggregate(Panel): row.operator("bim.add_aggregate", icon="ADD", text="") row = layout.row(align=True) - parts = AggregateData.data['related_objects_amount'] + parts = AggregateData.data["related_objects_amount"] row.label(text=f"{parts or 'No'} Part{'s' if parts > 1 else ''}", icon="TRIA_DOWN") if AggregateData.data["has_related_objects"]: op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="") diff --git a/src/blenderbim/blenderbim/bim/module/project/data.py b/src/blenderbim/blenderbim/bim/module/project/data.py index 1fe5e85226..922f5501a9 100644 --- a/src/blenderbim/blenderbim/bim/module/project/data.py +++ b/src/blenderbim/blenderbim/bim/module/project/data.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . +import os import bpy import blenderbim.tool as tool from blenderbim.bim.ifc import IfcStore @@ -31,9 +32,16 @@ class ProjectData: @classmethod def load(cls): - cls.data = {"export_schema": cls.get_export_schema()} + cls.data = {"export_schema": cls.get_export_schema(), "template_file": cls.template_file()} cls.is_loaded = True @classmethod def get_export_schema(cls): return [(s, s, "") for s in IfcStore.schema_identifiers] + + @classmethod + def template_file(cls): + files = os.listdir(os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries")) + results = [("0", "Blank Project", "")] + results.extend([(f, f.replace(".ifc", ""), "") for f in files if ".ifc" in f]) + return results diff --git a/src/blenderbim/blenderbim/bim/module/project/operator.py b/src/blenderbim/blenderbim/bim/module/project/operator.py index 2276c57ef8..8ae9a03201 100644 --- a/src/blenderbim/blenderbim/bim/module/project/operator.py +++ b/src/blenderbim/blenderbim/bim/module/project/operator.py @@ -27,6 +27,7 @@ import ifcopenshell.util.selector import ifcopenshell.util.representation import blenderbim.bim.handler import blenderbim.tool as tool +import blenderbim.core.project as core import blenderbim.core.context import blenderbim.core.owner from blenderbim.bim.ifc import IfcStore @@ -45,80 +46,16 @@ class CreateProject(bpy.types.Operator): def execute(self, context): IfcStore.begin_transaction(self) IfcStore.add_transaction_operation(self, rollback=self.rollback, commit=lambda data: True) - result = self._execute(context) - self.transaction_data = {"file": self.file} + self._execute(context) + self.transaction_data = {"file": tool.Ifc.get()} IfcStore.add_transaction_operation(self, rollback=lambda data: True, commit=self.commit) IfcStore.end_transaction(self) - return result - - def _execute(self, context): - active_object = context.view_layer.objects.active - self.file = IfcStore.get_file() - if self.file: - return {"FINISHED"} - - IfcStore.file = ifcopenshell.api.run( - "project.create_file", **{"version": context.scene.BIMProjectProperties.export_schema} - ) - self.file = IfcStore.get_file() - - if self.file.schema == "IFC2X3": - person = blenderbim.core.owner.add_person(tool.Ifc) - organisation = blenderbim.core.owner.add_organisation(tool.Ifc) - user = blenderbim.core.owner.add_person_and_organisation(tool.Ifc, person=person, organisation=organisation) - blenderbim.core.owner.set_user(tool.Owner, user=user) - - project = bpy.data.objects.new(self.get_name("IfcProject", "My Project"), None) - site = bpy.data.objects.new(self.get_name("IfcSite", "My Site"), None) - building = bpy.data.objects.new(self.get_name("IfcBuilding", "My Building"), None) - building_storey = bpy.data.objects.new(self.get_name("IfcBuildingStorey", "My Storey"), None) - - bpy.ops.bim.assign_class(obj=project.name, ifc_class="IfcProject") - blenderbim.core.unit.assign_scene_units(tool.Ifc, tool.Unit) - - model = blenderbim.core.context.add_context( - tool.Ifc, context_type="Model", context_identifier="", target_view="", parent=0 - ) - body_context = blenderbim.core.context.add_context( - tool.Ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model - ) - blenderbim.core.context.add_context( - tool.Ifc, context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model - ) - plan = blenderbim.core.context.add_context( - tool.Ifc, context_type="Plan", context_identifier="", target_view="", parent=0 - ) - blenderbim.core.context.add_context( - tool.Ifc, context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan - ) - - blenderbim.bim.handler.refresh_ui_data() - context.scene.BIMRootProperties.contexts = str(body_context.id()) - - bpy.ops.bim.assign_class(obj=site.name, ifc_class="IfcSite") - bpy.ops.bim.assign_class(obj=building.name, ifc_class="IfcBuilding") - bpy.ops.bim.assign_class(obj=building_storey.name, ifc_class="IfcBuildingStorey") - - blenderbim.core.aggregate.assign_object( - tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=project, related_obj=site - ) - blenderbim.core.aggregate.assign_object( - tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=site, related_obj=building - ) - blenderbim.core.aggregate.assign_object( - tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=building, related_obj=building_storey - ) - - context.view_layer.objects.active = active_object return {"FINISHED"} - def get_name(self, ifc_class, name): - if not bpy.data.objects.get(f"{ifc_class}/{name}"): - return name - i = 2 - while bpy.data.objects.get(f"{ifc_class}/{name} {i}"): - i += 1 - return f"{name} {i}" + def _execute(self, context): + props = context.scene.BIMProjectProperties + template = None if props.template_file == "0" else props.template_file + core.create_project(tool.Ifc, tool.Project, schema=props.export_schema, template=template) def rollback(self, data): IfcStore.file = None @@ -373,7 +310,11 @@ class AppendLibraryElement(bpy.types.Operator): self.import_type_from_ifc(element, context) elif element.is_a("IfcMaterial"): self.import_material_from_ifc(element, context) - context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True + try: + context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True + except: + # TODO Remove this terrible code when I refactor this into the core + pass blenderbim.bim.handler.purge_module_data() return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/bim/module/project/prop.py b/src/blenderbim/blenderbim/bim/module/project/prop.py index 1db22e940c..c04121aea8 100644 --- a/src/blenderbim/blenderbim/bim/module/project/prop.py +++ b/src/blenderbim/blenderbim/bim/module/project/prop.py @@ -38,6 +38,12 @@ def get_export_schema(self, context): return ProjectData.data["export_schema"] +def get_template_file(self, context): + if not ProjectData.is_loaded: + ProjectData.load() + return ProjectData.data["template_file"] + + def update_filter_mode(self, context): self.filter_categories.clear() if self.filter_mode == "NONE": @@ -132,6 +138,7 @@ class BIMProjectProperties(PropertyGroup): links: CollectionProperty(name="Links", type=Link) active_link_index: IntProperty(name="Active Link Index") export_schema: EnumProperty(items=get_export_schema, name="IFC Schema") + template_file: EnumProperty(items=get_template_file, name="Template File") def get_library_element_index(self, lib_element): return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element)) diff --git a/src/blenderbim/blenderbim/bim/module/project/ui.py b/src/blenderbim/blenderbim/bim/module/project/ui.py index f24f96e7ac..bb94c0f174 100644 --- a/src/blenderbim/blenderbim/bim/module/project/ui.py +++ b/src/blenderbim/blenderbim/bim/module/project/ui.py @@ -156,6 +156,9 @@ class BIM_PT_project(Panel): row.prop(props, "area_unit", text="Area Unit") row = self.layout.row() row.prop(props, "volume_unit", text="Volume Unit") + row = self.layout.row() + row.prop(pprops, "template_file", text="Template") + row = self.layout.row(align=True) row.operator("bim.create_project") row.operator("bim.load_project") diff --git a/src/blenderbim/blenderbim/core/project.py b/src/blenderbim/blenderbim/core/project.py new file mode 100644 index 0000000000..614c255128 --- /dev/null +++ b/src/blenderbim/blenderbim/core/project.py @@ -0,0 +1,63 @@ +# 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 . + + +def create_project(ifc, project, schema=None, template=None): + if ifc.get(): + return + + ifc.set(ifc.run("project.create_file", version=schema)) + + if schema == "IFC2X3": + person = project.run_owner_add_person() + organisation = project.run_owner_add_organisation() + user = project.run_owner_add_person_and_organisation(person=person, organisation=organisation) + project.run_owner_set_user(user=user) + + project_obj = project.create_empty("My Project") + site = project.create_empty("My Site") + building = project.create_empty("My Building") + storey = project.create_empty("My Storey") + + project.run_root_assign_class(obj=project_obj, ifc_class="IfcProject") + project.run_unit_assign_scene_units() + + model = project.run_context_add_context(context_type="Model", context_identifier="", target_view="", parent=0) + body = project.run_context_add_context( + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model + ) + project.run_context_add_context( + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent=model + ) + plan = project.run_context_add_context(context_type="Plan", context_identifier="", target_view="", parent=0) + annotation = project.run_context_add_context( + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent=plan + ) + + project.run_root_assign_class(obj=site, ifc_class="IfcSite", context=body) + project.run_root_assign_class(obj=building, ifc_class="IfcBuilding", context=body) + project.run_root_assign_class(obj=storey, ifc_class="IfcBuildingStorey", context=body) + + project.run_aggregate_assign_object(relating_obj=project_obj, related_obj=site) + project.run_aggregate_assign_object(relating_obj=site, related_obj=building) + project.run_aggregate_assign_object(relating_obj=building, related_obj=storey) + + project.set_context(body) + + if template: + project.append_all_types_from_template(template) diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index d42ecf32c3..73866bee55 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -215,6 +215,7 @@ class Ifc: def get_schema(cls): pass def link(cls, element, obj): pass def run(cls, command, **kwargs): pass + def set(cls, ifc): pass def unlink(cls, element=None, obj=None): pass @@ -292,6 +293,21 @@ class Owner: def set_user(cls, user): pass +@interface +class Project: + def append_all_types_from_template(cls, template): pass + def create_empty(cls, name): pass + def run_aggregate_assign_object(cls, relating_obj=None, related_obj=None): pass + def run_context_add_context(cls, context_type=None, context_identifier=None, target_view=None, parent=None): pass + def run_owner_add_organisation(cls): pass + def run_owner_add_person(cls): pass + def run_owner_add_person_and_organisation(cls, person=None, organisation=None): pass + def run_owner_set_user(cls, user=None): pass + def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass + def run_unit_assign_scene_units(cls): pass + def set_context(cls, context): pass + + @interface class Pset: def get_element_pset(cls, element, pset_name): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index 82b9d537a7..a1141f28a0 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -33,6 +33,7 @@ from blenderbim.tool.misc import Misc from blenderbim.tool.model import Model from blenderbim.tool.owner import Owner 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.root import Root diff --git a/src/blenderbim/blenderbim/tool/project.py b/src/blenderbim/blenderbim/tool/project.py new file mode 100644 index 0000000000..bbf0f24d66 --- /dev/null +++ b/src/blenderbim/blenderbim/tool/project.py @@ -0,0 +1,100 @@ +# 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 os +import bpy +import blenderbim.core.tool +import blenderbim.tool as tool +from blenderbim.bim.ifc import IfcStore + + +class Project(blenderbim.core.tool.Project): + @classmethod + def append_all_types_from_template(cls, template): + # TODO refactor + filepath = os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", template) + bpy.ops.bim.select_library_file(filepath=filepath) + for element in IfcStore.library_file.by_type("IfcTypeProduct"): + bpy.ops.bim.append_library_element(definition=element.id()) + + @classmethod + def create_empty(cls, name): + return bpy.data.objects.new(name, None) + + @classmethod + def run_aggregate_assign_object(cls, relating_obj=None, related_obj=None): + return blenderbim.core.aggregate.assign_object( + tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=relating_obj, related_obj=related_obj + ) + + @classmethod + def run_context_add_context(cls, context_type=None, context_identifier=None, target_view=None, parent=None): + return blenderbim.core.context.add_context( + tool.Ifc, + context_type=context_type, + context_identifier=context_identifier, + target_view=target_view, + parent=parent, + ) + + @classmethod + def run_owner_add_organisation(cls): + return blenderbim.core.owner.add_organisation(tool.Ifc) + + @classmethod + def run_owner_add_person(cls): + return blenderbim.core.owner.add_person(tool.Ifc) + + @classmethod + def run_owner_add_person_and_organisation(cls, person=None, organisation=None): + return blenderbim.core.owner.add_person_and_organisation(tool.Ifc, person=person, organisation=organisation) + + @classmethod + def run_owner_set_user(cls, user=None): + return blenderbim.core.owner.set_user(tool.Owner, user=user) + + @classmethod + def run_root_assign_class( + cls, + obj=None, + ifc_class=None, + predefined_type=None, + should_add_representation=True, + context=None, + ifc_representation_class=None, + ): + return blenderbim.core.root.assign_class( + tool.Ifc, + tool.Collector, + tool.Root, + obj=obj, + ifc_class=ifc_class, + predefined_type=predefined_type, + should_add_representation=should_add_representation, + context=context, + ifc_representation_class=ifc_representation_class, + ) + + @classmethod + def run_unit_assign_scene_units(cls): + return blenderbim.core.unit.assign_scene_units(tool.Ifc, tool.Unit) + + @classmethod + def set_context(cls, context): + blenderbim.bim.handler.refresh_ui_data() + bpy.context.scene.BIMRootProperties.contexts = str(context.id()) diff --git a/src/blenderbim/test/core/bootstrap.py b/src/blenderbim/test/core/bootstrap.py index 059dfef0fb..31e87c9e9b 100644 --- a/src/blenderbim/test/core/bootstrap.py +++ b/src/blenderbim/test/core/bootstrap.py @@ -126,6 +126,13 @@ def patch(): prophet.verify() +@pytest.fixture +def project(): + prophet = Prophecy(blenderbim.core.tool.Project) + yield prophet + prophet.verify() + + @pytest.fixture def pset(): prophet = Prophecy(blenderbim.core.tool.Pset) diff --git a/src/blenderbim/test/core/test_project.py b/src/blenderbim/test/core/test_project.py new file mode 100644 index 0000000000..1b10132581 --- /dev/null +++ b/src/blenderbim/test/core/test_project.py @@ -0,0 +1,156 @@ +# 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.project as subject +from test.core.bootstrap import ifc, project + + +class TestCreateProject: + def test_do_nothing_if_a_project_already_exists(self, ifc, project): + ifc.get().should_be_called().will_return("ifc") + subject.create_project(ifc, project, schema="IFC4", template=None) + + def test_create_an_ifc4_project(self, ifc, project): + ifc.get().should_be_called().will_return(None) + ifc.run("project.create_file", version="IFC4").should_be_called().will_return("ifc") + ifc.set("ifc").should_be_called() + + project.create_empty("My Project").should_be_called().will_return("project") + project.create_empty("My Site").should_be_called().will_return("site") + project.create_empty("My Building").should_be_called().will_return("building") + project.create_empty("My Storey").should_be_called().will_return("storey") + project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called() + project.run_unit_assign_scene_units().should_be_called() + + project.run_context_add_context( + context_type="Model", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("model") + project.run_context_add_context( + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent="model" + ).should_be_called().will_return("body") + project.run_context_add_context( + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent="model" + ).should_be_called() + project.run_context_add_context( + context_type="Plan", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("plan") + project.run_context_add_context( + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan" + ).should_be_called() + + project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called() + project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called() + project.run_root_assign_class(obj="storey", ifc_class="IfcBuildingStorey", context="body").should_be_called() + + project.run_aggregate_assign_object(relating_obj="project", related_obj="site").should_be_called() + project.run_aggregate_assign_object(relating_obj="site", related_obj="building").should_be_called() + project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called() + + project.set_context("body").should_be_called() + + subject.create_project(ifc, project, schema="IFC4", template=None) + + def test_appending_project_template_types_if_specified(self, ifc, project): + ifc.get().should_be_called().will_return(None) + ifc.run("project.create_file", version="IFC4").should_be_called().will_return("ifc") + ifc.set("ifc").should_be_called() + + project.create_empty("My Project").should_be_called().will_return("project") + project.create_empty("My Site").should_be_called().will_return("site") + project.create_empty("My Building").should_be_called().will_return("building") + project.create_empty("My Storey").should_be_called().will_return("storey") + project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called() + project.run_unit_assign_scene_units().should_be_called() + + project.run_context_add_context( + context_type="Model", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("model") + project.run_context_add_context( + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent="model" + ).should_be_called().will_return("body") + project.run_context_add_context( + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent="model" + ).should_be_called() + project.run_context_add_context( + context_type="Plan", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("plan") + project.run_context_add_context( + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan" + ).should_be_called() + + project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called() + project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called() + project.run_root_assign_class(obj="storey", ifc_class="IfcBuildingStorey", context="body").should_be_called() + + project.run_aggregate_assign_object(relating_obj="project", related_obj="site").should_be_called() + project.run_aggregate_assign_object(relating_obj="site", related_obj="building").should_be_called() + project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called() + + project.set_context("body").should_be_called() + + project.append_all_types_from_template("template").should_be_called() + + subject.create_project(ifc, project, schema="IFC4", template="template") + + def test_create_an_ifc2x3_project_with_owner_defaults(self, ifc, project): + ifc.get().should_be_called().will_return(None) + ifc.run("project.create_file", version="IFC2X3").should_be_called().will_return("ifc") + ifc.set("ifc").should_be_called() + + project.run_owner_add_person().should_be_called().will_return("person") + project.run_owner_add_organisation().should_be_called().will_return("organisation") + project.run_owner_add_person_and_organisation( + person="person", organisation="organisation" + ).should_be_called().will_return("user") + project.run_owner_set_user(user="user").should_be_called() + + project.create_empty("My Project").should_be_called().will_return("project") + project.create_empty("My Site").should_be_called().will_return("site") + project.create_empty("My Building").should_be_called().will_return("building") + project.create_empty("My Storey").should_be_called().will_return("storey") + project.run_root_assign_class(obj="project", ifc_class="IfcProject").should_be_called() + project.run_unit_assign_scene_units().should_be_called() + + project.run_context_add_context( + context_type="Model", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("model") + project.run_context_add_context( + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent="model" + ).should_be_called().will_return("body") + project.run_context_add_context( + context_type="Model", context_identifier="Box", target_view="MODEL_VIEW", parent="model" + ).should_be_called() + project.run_context_add_context( + context_type="Plan", context_identifier="", target_view="", parent=0 + ).should_be_called().will_return("plan") + project.run_context_add_context( + context_type="Plan", context_identifier="Annotation", target_view="PLAN_VIEW", parent="plan" + ).should_be_called() + + project.run_root_assign_class(obj="site", ifc_class="IfcSite", context="body").should_be_called() + project.run_root_assign_class(obj="building", ifc_class="IfcBuilding", context="body").should_be_called() + project.run_root_assign_class(obj="storey", ifc_class="IfcBuildingStorey", context="body").should_be_called() + + project.run_aggregate_assign_object(relating_obj="project", related_obj="site").should_be_called() + project.run_aggregate_assign_object(relating_obj="site", related_obj="building").should_be_called() + project.run_aggregate_assign_object(relating_obj="building", related_obj="storey").should_be_called() + + project.set_context("body").should_be_called() + + subject.create_project(ifc, project, schema="IFC2X3", template=None) diff --git a/src/blenderbim/test/tool/test_project.py b/src/blenderbim/test/tool/test_project.py new file mode 100644 index 0000000000..a5fe84ddc2 --- /dev/null +++ b/src/blenderbim/test/tool/test_project.py @@ -0,0 +1,91 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2021 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.project import Project as subject + + +class TestImplementsTool(NewFile): + def test_run(self): + assert isinstance(subject(), blenderbim.core.tool.Project) + + +class TestAppendAllTypesFromTemplate(NewFile): + def test_nothing(self): + # TODO refactor + pass + + +class TestCreateEmpty(NewFile): + def test_run(self): + subject.create_empty("Foobar") + assert bpy.data.objects.get("Foobar") + assert not bpy.data.objects.get("Foobar").data + + +class TestRunAggregateAssignObject(NewFile): + def test_nothing(self): + pass + + +class TestRunContextAddContext(NewFile): + def test_nothing(self): + pass + + +class TestRunOwnerAddOrganisation(NewFile): + def test_nothing(self): + pass + + +class TestRunOwnerAddPerson(NewFile): + def test_nothing(self): + pass + + +class TestRunOwnerAddPersonAndOrganisation(NewFile): + def test_nothing(self): + pass + + +class TestRunOwnerSetUser(NewFile): + def test_nothing(self): + pass + + +class TestRunAssignClass(NewFile): + def test_nothing(self): + pass + + +class TestRunUnitAssignSceneUnits(NewFile): + def test_nothing(self): + pass + + +class TestSetContext(NewFile): + def test_run(self): + ifc = ifcopenshell.file() + tool.Ifc.set(ifc) + context = ifc.createIfcGeometricRepresentationContext() + subject.set_context(context) + assert bpy.context.scene.BIMRootProperties.contexts == str(context.id())