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())