mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
Fix #1727. BIMTester now runs within the BlenderBIM Add-on, implement attribute checking in IDS, and fix some minor IDS bugs.
This commit is contained in:
@@ -33,11 +33,13 @@ ifeq ($(PYVERSION), py37)
|
|||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py37h5f1835d_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py37h5f1835d_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py37h95e2c48_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py37h0379df6_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/30/c0/d0526314971fc661b083ab135747dc68446a3022686da8c16d25fcf6ef07/lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl
|
||||||
endif
|
endif
|
||||||
ifeq ($(PYVERSION), py39)
|
ifeq ($(PYVERSION), py39)
|
||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/linux-64/hpp-fcl-1.7.5-py39hbcdfc36_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/linux-64/eigenpy-2.6.5-py39h5aed9d1_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/linux-64/boost-1.74.0-py39h5472131_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/19/d9/a69c6aff5673554df48120565a14a50eaa41d29ae03b02faa0b023666318/lxml-4.6.3-cp39-cp39-manylinux2014_x86_64.whl
|
||||||
endif
|
endif
|
||||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
|
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/linux-64/assimp-5.0.1-hedfc422_6.tar.bz2
|
||||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2
|
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/linux-64/octomap-1.9.7-h4bd325d_0.tar.bz2
|
||||||
@@ -49,11 +51,13 @@ ifeq ($(PYVERSION), py37)
|
|||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py37h2d7f23a_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py37h2d7f23a_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py37h0695097_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py37hd79e0ac_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/1e/3e/f0abc15d5dac50939bccc589aae336d5ead4c72e7ad1039a2e0f3630ea92/lxml-4.6.3-cp37-cp37m-macosx_10_9_x86_64.whl
|
||||||
endif
|
endif
|
||||||
ifeq ($(PYVERSION), py39)
|
ifeq ($(PYVERSION), py39)
|
||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/osx-64/hpp-fcl-1.7.5-py39h1e32b98_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/osx-64/eigenpy-2.6.5-py39h5405915_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/osx-64/boost-1.74.0-py39ha641261_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/b8/74/a71f7ad72e8db54ce899efab84507b801660750cbbfa6a39e6717557d36a/lxml-4.6.3-cp39-cp39-macosx_10_9_x86_64.whl
|
||||||
endif
|
endif
|
||||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
|
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/osx-64/assimp-5.0.1-h1224e73_6.tar.bz2
|
||||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2
|
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/osx-64/octomap-1.9.7-h940c156_0.tar.bz2
|
||||||
@@ -65,11 +69,13 @@ ifeq ($(PYVERSION), py37)
|
|||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py37h839d6b1_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py37h839d6b1_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py37h2c32e34_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py37h3b38789_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/9e/5e/171ee9d40a600f565fe691ec5bf7596247ec62cfb2edc00c91afe8ea837b/lxml-4.6.3-cp37-cp37m-win_amd64.whl
|
||||||
endif
|
endif
|
||||||
ifeq ($(PYVERSION), py39)
|
ifeq ($(PYVERSION), py39)
|
||||||
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2
|
HPPFCL_URL:=https://anaconda.org/conda-forge/hpp-fcl/1.7.5/download/win-64/hpp-fcl-1.7.5-py39h2e7c763_0.tar.bz2
|
||||||
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2
|
EIGENPY_URL:=https://anaconda.org/conda-forge/eigenpy/2.6.5/download/win-64/eigenpy-2.6.5-py39h3ce40e6_0.tar.bz2
|
||||||
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
|
BOOST_URL:=https://anaconda.org/conda-forge/boost/1.74.0/download/win-64/boost-1.74.0-py39hefe7e4c_3.tar.bz2
|
||||||
|
LXML_URL:=https://files.pythonhosted.org/packages/72/d4/426ecb8849c47c3e370c87aa0ac05d85768df917ffea27fcd6686a5e6495/lxml-4.6.3-cp39-cp39-win_amd64.whl
|
||||||
endif
|
endif
|
||||||
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
|
ASSIMP_URL:=https://anaconda.org/conda-forge/assimp/5.0.1/download/win-64/assimp-5.0.1-hc2aa0de_6.tar.bz2
|
||||||
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2
|
OCTOMAP_URL:=https://anaconda.org/conda-forge/octomap/1.9.7/download/win-64/octomap-1.9.7-h5362a0b_0.tar.bz2
|
||||||
@@ -350,6 +356,13 @@ endif
|
|||||||
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
|
cd dist/working/ && patch ../blenderbim/libs/site/packages/behave/runner_util.py < runner_util.patch
|
||||||
rm -rf dist/working
|
rm -rf dist/working
|
||||||
|
|
||||||
|
# Required by ids
|
||||||
|
mkdir dist/working
|
||||||
|
cd dist/working && wget $(LXML_URL)
|
||||||
|
cd dist/working && cp *.whl lxml.zip && unzip lxml.zip
|
||||||
|
cp -r dist/working/lxml dist/blenderbim/libs/site/packages/
|
||||||
|
rm -rf dist/working
|
||||||
|
|
||||||
# Required by behave
|
# Required by behave
|
||||||
mkdir dist/working
|
mkdir dist/working
|
||||||
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
|
cd dist/working && wget https://files.pythonhosted.org/packages/f4/65/220bb4075fddb09d5b3ea2c1c1fa66c1c72be9361ec187aab50fa161e576/parse-1.15.0.tar.gz
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import os
|
|||||||
import bpy
|
import bpy
|
||||||
import tempfile
|
import tempfile
|
||||||
import webbrowser
|
import webbrowser
|
||||||
import ifcopenshell
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import bimtester
|
import bimtester
|
||||||
@@ -31,7 +30,6 @@ except:
|
|||||||
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
|
print("Failed to load BIMTester. Try disabling other add-ons, in particular Blender-OSM. See bug #1318.")
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import cycle
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
@@ -42,7 +40,7 @@ class ExecuteBIMTester(bpy.types.Operator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
props = context.scene.BimTesterProperties
|
props = context.scene.BimTesterProperties
|
||||||
return props.ifc_file and props.feature
|
return (props.ifc_file or props.should_load_from_memory) and props.feature
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.BimTesterProperties
|
props = context.scene.BimTesterProperties
|
||||||
@@ -243,19 +241,3 @@ class QAHelper:
|
|||||||
is_in_scenario = False
|
is_in_scenario = False
|
||||||
destination.write(source_line)
|
destination.write(source_line)
|
||||||
os.remove(filename + "~")
|
os.remove(filename + "~")
|
||||||
|
|
||||||
|
|
||||||
colour_list = [
|
|
||||||
(0.651, 0.81, 0.892, 1),
|
|
||||||
(0.121, 0.471, 0.706, 1),
|
|
||||||
(0.699, 0.876, 0.54, 1),
|
|
||||||
(0.199, 0.629, 0.174, 1),
|
|
||||||
(0.983, 0.605, 0.602, 1),
|
|
||||||
(0.89, 0.101, 0.112, 1),
|
|
||||||
(0.989, 0.751, 0.427, 1),
|
|
||||||
(0.986, 0.497, 0.1, 1),
|
|
||||||
(0.792, 0.699, 0.839, 1),
|
|
||||||
(0.414, 0.239, 0.603, 1),
|
|
||||||
(0.993, 0.999, 0.6, 1),
|
|
||||||
(0.693, 0.349, 0.157, 1),
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -17,14 +17,19 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import bpy
|
import bpy
|
||||||
import pytest
|
import pytest
|
||||||
|
import webbrowser
|
||||||
import blenderbim
|
import blenderbim
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
# Monkey-patch webbrowser opening since we want to test headlessly
|
||||||
|
webbrowser.open = lambda x: True
|
||||||
|
|
||||||
|
|
||||||
class NewFile:
|
class NewFile:
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
@@ -64,8 +69,15 @@ def the_object_name_is_selected(name):
|
|||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
|
||||||
|
|
||||||
def i_select_value_in_prop(value, prop):
|
def i_set_prop_to_value(prop, value):
|
||||||
exec(f'bpy.context.{prop} = "{value}"')
|
try:
|
||||||
|
exec(f'bpy.context.{prop} = "{value}"')
|
||||||
|
except:
|
||||||
|
exec(f"bpy.context.{prop} = {value}")
|
||||||
|
|
||||||
|
|
||||||
|
def i_enable_prop(prop):
|
||||||
|
exec(f"bpy.context.{prop} = True")
|
||||||
|
|
||||||
|
|
||||||
def i_press_operator(operator):
|
def i_press_operator(operator):
|
||||||
@@ -136,11 +148,17 @@ def the_object_name1_and_name2_are_different_elements(name1, name2):
|
|||||||
assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}"
|
assert element1 != element2, f"Objects {name1} and {name2} have same elements {element1} and {element2}"
|
||||||
|
|
||||||
|
|
||||||
|
def the_file_name_should_contain_value(name, value):
|
||||||
|
with open(name, "r") as f:
|
||||||
|
assert value in f.read()
|
||||||
|
|
||||||
|
|
||||||
definitions = {
|
definitions = {
|
||||||
"an empty IFC project": an_empty_ifc_project,
|
"an empty IFC project": an_empty_ifc_project,
|
||||||
"I add a cube": i_add_a_cube,
|
"I add a cube": i_add_a_cube,
|
||||||
'the object "(.*)" is selected': the_object_name_is_selected,
|
'the object "(.*)" is selected': the_object_name_is_selected,
|
||||||
'I select "(.*)" in "(.*)"': i_select_value_in_prop,
|
'I set "(.*)" to "(.*)"': i_set_prop_to_value,
|
||||||
|
'I enable "(.*)"': i_enable_prop,
|
||||||
'I press "(.*)"': i_press_operator,
|
'I press "(.*)"': i_press_operator,
|
||||||
'the object "(.*)" is an "(.*)"': the_object_name_is_an_ifc_class,
|
'the object "(.*)" is an "(.*)"': the_object_name_is_an_ifc_class,
|
||||||
'the object "(.*)" is in the collection "(.*)"': the_object_name_is_in_the_collection_collection,
|
'the object "(.*)" is in the collection "(.*)"': the_object_name_is_in_the_collection_collection,
|
||||||
@@ -151,6 +169,7 @@ definitions = {
|
|||||||
'the object "(.*)" is contained in "(.*)"': the_object_name_is_contained_in_container_name,
|
'the object "(.*)" is contained in "(.*)"': the_object_name_is_contained_in_container_name,
|
||||||
"I duplicate the selected objects": i_duplicate_the_selected_objects,
|
"I duplicate the selected objects": i_duplicate_the_selected_objects,
|
||||||
'the object "(.*)" and "(.*)" are different elements': the_object_name1_and_name2_are_different_elements,
|
'the object "(.*)" and "(.*)" are different elements': the_object_name1_and_name2_are_different_elements,
|
||||||
|
'the file "(.*)" should contain "(.*)"': the_file_name_should_contain_value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -158,6 +177,7 @@ definitions = {
|
|||||||
def run(scenario):
|
def run(scenario):
|
||||||
for line in scenario.split("\n"):
|
for line in scenario.split("\n"):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
line = line.replace("{cwd}", os.getcwd())
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
match = None
|
match = None
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||||
|
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||||
|
#
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import test.bim.bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
class TestExecuteBIMTester(test.bim.bootstrap.NewFile):
|
||||||
|
@test.bim.bootstrap.scenario
|
||||||
|
def test_executing_bimtester(self):
|
||||||
|
return """
|
||||||
|
Given an empty IFC project
|
||||||
|
When I enable "scene.BimTesterProperties.should_load_from_memory"
|
||||||
|
And I set "scene.BimTesterProperties.feature" to "{cwd}/test/files/sample-ids.xml"
|
||||||
|
And I press "bim.execute_bim_tester"
|
||||||
|
Then the file "{cwd}/test/files/sample-ids.xml.html" should contain "Tests passed: <strong>1 / 1</strong> (100%)"
|
||||||
|
"""
|
||||||
@@ -26,7 +26,7 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
|
|||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
Then the object "IfcWall/Cube" is an "IfcWall"
|
Then the object "IfcWall/Cube" is an "IfcWall"
|
||||||
And the object "IfcWall/Cube" is in the collection "Collection"
|
And the object "IfcWall/Cube" is in the collection "Collection"
|
||||||
@@ -39,8 +39,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
|
|||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And I select "IfcElementType" in "scene.BIMRootProperties.ifc_product"
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||||
And I select "IfcWallType" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
Then the object "IfcWallType/Cube" is an "IfcWallType"
|
Then the object "IfcWallType/Cube" is an "IfcWallType"
|
||||||
And the object "IfcWallType/Cube" is in the collection "Types"
|
And the object "IfcWallType/Cube" is in the collection "Types"
|
||||||
@@ -53,8 +53,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
|
|||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And I select "IfcSpatialElement" in "scene.BIMRootProperties.ifc_product"
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcSpatialElement"
|
||||||
And I select "IfcBuilding" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcBuilding"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
Then the object "IfcBuilding/Cube" is an "IfcBuilding"
|
Then the object "IfcBuilding/Cube" is an "IfcBuilding"
|
||||||
And the object "IfcBuilding/Cube" is in the collection "IfcBuilding/Cube"
|
And the object "IfcBuilding/Cube" is in the collection "IfcBuilding/Cube"
|
||||||
@@ -67,8 +67,8 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
|
|||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And I select "IfcElement" in "scene.BIMRootProperties.ifc_product"
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElement"
|
||||||
And I select "IfcOpeningElement" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcOpeningElement"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement"
|
Then the object "IfcOpeningElement/Cube" is an "IfcOpeningElement"
|
||||||
And the object "IfcOpeningElement/Cube" is in the collection "IfcOpeningElements"
|
And the object "IfcOpeningElement/Cube" is in the collection "IfcOpeningElements"
|
||||||
@@ -82,7 +82,7 @@ class TestAssignClass(test.bim.bootstrap.NewFile):
|
|||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And the object "Cube" is placed in the collection "IfcBuildingStorey/My Storey"
|
And the object "Cube" is placed in the collection "IfcBuildingStorey/My Storey"
|
||||||
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
Then the object "IfcWall/Cube" is contained in "My Storey"
|
Then the object "IfcWall/Cube" is contained in "My Storey"
|
||||||
"""
|
"""
|
||||||
@@ -95,7 +95,7 @@ class TestCopyClass(test.bim.bootstrap.NewFile):
|
|||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I add a cube
|
When I add a cube
|
||||||
And the object "Cube" is selected
|
And the object "Cube" is selected
|
||||||
And I select "IfcWall" in "scene.BIMRootProperties.ifc_class"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I duplicate the selected objects
|
And I duplicate the selected objects
|
||||||
Then the object "IfcWall/Cube" and "IfcWall/Cube.001" are different elements
|
Then the object "IfcWall/Cube" and "IfcWall/Cube.001" are different elements
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ids xmlns="http://standards.buildingsmart.org/IDS" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://standards.buildingsmart.org/IDS ids_04.xsd">
|
||||||
|
<specification name="Sample IDS" necessity="required">
|
||||||
|
<applicability>
|
||||||
|
<entity>
|
||||||
|
<name><simpleValue>IfcBuilding</simpleValue></name>
|
||||||
|
</entity>
|
||||||
|
</applicability>
|
||||||
|
<requirements>
|
||||||
|
<property location="any">
|
||||||
|
<propertyset><simpleValue>attribute</simpleValue></propertyset>
|
||||||
|
<name><simpleValue>name</simpleValue></name>
|
||||||
|
<value><simpleValue>My Building</simpleValue></value>
|
||||||
|
</property>
|
||||||
|
</requirements>
|
||||||
|
</specification>
|
||||||
|
<info/>
|
||||||
|
</ids>
|
||||||
@@ -41,13 +41,16 @@ class ReportGenerator:
|
|||||||
return
|
return
|
||||||
|
|
||||||
for scenario in feature["elements"]:
|
for scenario in feature["elements"]:
|
||||||
scenario_data = self.process_scenario(scenario)
|
scenario_data = self.process_scenario(scenario, feature)
|
||||||
if scenario_data:
|
if scenario_data:
|
||||||
data["scenarios"].append(scenario_data)
|
data["scenarios"].append(scenario_data)
|
||||||
|
|
||||||
data["total_passes"] = sum([s["total_passes"] for s in data["scenarios"]])
|
data["total_passes"] = sum([s["total_passes"] for s in data["scenarios"]])
|
||||||
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
|
data["total_steps"] = sum([s["total_steps"] for s in data["scenarios"]])
|
||||||
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
|
try:
|
||||||
|
data["pass_rate"] = round((data["total_passes"] / data["total_steps"]) * 100)
|
||||||
|
except ZeroDivisionError:
|
||||||
|
data["pass_rate"] = 0
|
||||||
|
|
||||||
data.update(self.get_template_strings())
|
data.update(self.get_template_strings())
|
||||||
|
|
||||||
@@ -57,7 +60,7 @@ class ReportGenerator:
|
|||||||
) as template:
|
) as template:
|
||||||
out.write(pystache.render(template.read(), data))
|
out.write(pystache.render(template.read(), data))
|
||||||
|
|
||||||
def process_scenario(self, scenario):
|
def process_scenario(self, scenario, feature):
|
||||||
if len(scenario["steps"]) == 0:
|
if len(scenario["steps"]) == 0:
|
||||||
print("Scenario '{}' in feature '{}' has no steps.".format(scenario["name"], feature["name"]))
|
print("Scenario '{}' in feature '{}' has no steps.".format(scenario["name"], feature["name"]))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class TestRunner:
|
|||||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||||
ids_handler = IDSHandler()
|
ids_handler = IDSHandler()
|
||||||
logger.addHandler(ids_handler)
|
logger.addHandler(ids_handler)
|
||||||
ids_file = ifcopenshell.ids.ids(args["feature"])
|
ids_file = ifcopenshell.ids.ids.open(args["feature"])
|
||||||
ids_file.validate(IfcStore.file, logger)
|
ids_file.validate(IfcStore.file, logger)
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
|||||||
@@ -16,11 +16,9 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import operator
|
|
||||||
import os
|
|
||||||
import csv
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
@@ -30,13 +28,8 @@ import ifcopenshell.util.placement
|
|||||||
from bcf.v2.bcfxml import BcfXml
|
from bcf.v2.bcfxml import BcfXml
|
||||||
from bcf.v2 import data as bcf
|
from bcf.v2 import data as bcf
|
||||||
|
|
||||||
from bcf import bcfxml
|
|
||||||
|
|
||||||
from xmlschema import XMLSchema
|
from xmlschema import XMLSchema
|
||||||
from xmlschema import XMLSchemaConverter
|
|
||||||
from xmlschema import etree_tostring
|
from xmlschema import etree_tostring
|
||||||
from lxml import etree as ElementTree
|
|
||||||
from xmlschema.validators import facets
|
|
||||||
from xmlschema.validators import identities
|
from xmlschema.validators import identities
|
||||||
|
|
||||||
|
|
||||||
@@ -233,13 +226,18 @@ class ids:
|
|||||||
else:
|
else:
|
||||||
logger.debug("No applicable elements found. None required.")
|
logger.debug("No applicable elements found. None required.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
percentage = self.ifc_passed / self.ifc_applicable * 100
|
||||||
|
except ZeroDivisionError:
|
||||||
|
percentage = 0
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Out of %s IFC elements, %s were applicable and %s of them passed (%s)."
|
"Out of %s IFC elements, %s were applicable and %s of them passed (%s)."
|
||||||
% (
|
% (
|
||||||
len(ifc_file.by_type("IfcProduct")),
|
len(ifc_file.by_type("IfcProduct")),
|
||||||
self.ifc_applicable,
|
self.ifc_applicable,
|
||||||
self.ifc_passed,
|
self.ifc_passed,
|
||||||
str(self.ifc_passed / self.ifc_applicable * 100) + "%",
|
str(percentage) + "%",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for h in logger.handlers:
|
for h in logger.handlers:
|
||||||
@@ -686,25 +684,28 @@ class property(facet):
|
|||||||
|
|
||||||
self.location = self.node["@location"]
|
self.location = self.node["@location"]
|
||||||
|
|
||||||
# TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue'
|
if self.propertyset == "attribute":
|
||||||
instance_props = ifcopenshell.util.element.get_psets(inst)
|
val = {k.lower(): v for k, v in inst.get_info().items()}.get(self.name, None)
|
||||||
|
|
||||||
if ifcopenshell.util.element.get_type(inst):
|
|
||||||
type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst))
|
|
||||||
else:
|
else:
|
||||||
type_props = {}
|
# TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue'
|
||||||
|
instance_props = ifcopenshell.util.element.get_psets(inst)
|
||||||
|
|
||||||
if self.location == "instance":
|
if ifcopenshell.util.element.get_type(inst):
|
||||||
props = instance_props
|
type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst))
|
||||||
elif self.location == "type" and type_props:
|
else:
|
||||||
props = type_props
|
type_props = {}
|
||||||
elif self.location == "any" and (instance_props or type_props):
|
|
||||||
props = {**instance_props, **type_props}
|
|
||||||
else:
|
|
||||||
props = {}
|
|
||||||
|
|
||||||
pset = props.get(self.propertyset)
|
if self.location == "instance":
|
||||||
val = pset.get(self.name) if pset else None
|
props = instance_props
|
||||||
|
elif self.location == "type" and type_props:
|
||||||
|
props = type_props
|
||||||
|
elif self.location == "any" and (instance_props or type_props):
|
||||||
|
props = {**instance_props, **type_props}
|
||||||
|
else:
|
||||||
|
props = {}
|
||||||
|
|
||||||
|
pset = props.get(self.propertyset)
|
||||||
|
val = pset.get(self.name) if pset else None
|
||||||
|
|
||||||
self.location_msg = location[self.location]
|
self.location_msg = location[self.location]
|
||||||
di = {"name": self.name, "propertyset": self.propertyset, "value": "'%s'" % val, "location": self.location_msg}
|
di = {"name": self.name, "propertyset": self.propertyset, "value": "'%s'" % val, "location": self.location_msg}
|
||||||
@@ -913,7 +914,7 @@ class restriction:
|
|||||||
elif n[3:6] == "max":
|
elif n[3:6] == "max":
|
||||||
r.options.append("<=")
|
r.options.append("<=")
|
||||||
else:
|
else:
|
||||||
self.options.append("==")
|
r.options.append("==")
|
||||||
r.options[-1] += str(ids_dict[n]["@value"])
|
r.options[-1] += str(ids_dict[n]["@value"])
|
||||||
elif n == "pattern":
|
elif n == "pattern":
|
||||||
r.type = "pattern"
|
r.type = "pattern"
|
||||||
|
|||||||
Reference in New Issue
Block a user