2019-10-15 11:58:26 +11:00
import ifcopenshell
2020-04-02 18:38:42 +11:00
from behave import step
2019-10-15 11:58:26 +11:00
class IfcFile ( object ) :
file = None
2020-01-16 17:55:37 +11:00
bookmarks = { }
2019-10-15 11:58:26 +11:00
@classmethod
def load ( cls , path = None ) :
cls . file = ifcopenshell . open ( path )
@classmethod
def get ( cls ) :
2020-04-06 11:59:28 +10:00
if not cls . file :
assert False , ' No file was loaded, so this requirement cannot be checked '
2019-10-15 11:58:26 +11:00
return cls . file
2020-01-16 17:55:37 +11:00
@classmethod
def get_pset ( cls , element , pset_name ) :
if not element . IsDefinedBy :
return
for relationship in element . IsDefinedBy :
if relationship . RelatingPropertyDefinition . Name == pset_name :
return relationship . RelatingPropertyDefinition
@classmethod
def get_property ( cls , element , pset_name , property_name ) :
pset = cls . get_pset ( element , pset_name )
if not pset :
return
for prop in pset . HasProperties :
if prop . Name == property_name :
return prop
2020-01-02 13:01:17 +11:00
2020-04-06 11:59:28 +10:00
# Project setup MicroMVD
2020-04-02 18:38:42 +11:00
@step ( ' The IFC file " {file} " must be provided ' )
2019-10-15 11:58:26 +11:00
def step_impl ( context , file ) :
2020-04-06 11:59:28 +10:00
try :
IfcFile . load ( file )
except :
assert False , f ' The file { file } could not be loaded '
@step ( ' IFC data must use the {schema} schema ' )
def step_impl ( context , schema ) :
assert IfcFile . get ( ) . schema == schema , \
' We expected a schema of {} but instead got {} ' . format (
schema , IfcFile . get ( ) . schema )
2019-10-15 11:58:26 +11:00
2020-01-02 13:01:17 +11:00
2020-04-02 18:38:42 +11:00
@step ( ' The IFC file " {file} " is exempt from being provided ' )
2019-11-21 09:46:37 +11:00
def step_impl ( context , file ) :
2020-01-16 17:55:37 +11:00
pass
2019-11-21 09:46:37 +11:00
2020-01-02 13:01:17 +11:00
2020-04-06 11:59:28 +10:00
@step ( ' No further requirements are specified because {reason} ' )
def step_impl ( context , reason ) :
pass
@step ( ' The project name, code, or short identifier must be " {value} " ' )
def step_impl ( context , value ) :
project_value = getattr ( IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ] , ' Name ' )
assert project_value == value , ' We expected a value of " {} " but instead got " {} " ' . format (
value , project_value )
@step ( ' The project must have a longer form name of " {value} " ' )
def step_impl ( context , value ) :
project_value = getattr ( IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ] , ' LongName ' )
assert project_value == value , ' We expected a value of " {} " but instead got " {} " ' . format (
value , project_value )
@step ( ' The project must be described as " {value} " ' )
def step_impl ( context , value ) :
project_value = getattr ( IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ] , ' Description ' )
assert project_value == value , ' We expected a value of " {} " but instead got " {} " ' . format (
value , project_value )
@step ( ' The project must be categorised under " {value} " ' )
def step_impl ( context , value ) :
project_value = getattr ( IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ] , ' ObjectType ' )
assert project_value == value , ' We expected a value of " {} " but instead got " {} " ' . format (
value , project_value )
@step ( ' The project must contain information about the " {value} " phase ' )
def step_impl ( context , value ) :
project_value = getattr ( IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ] , ' Phase ' )
assert project_value == value , ' We expected a value of " {} " but instead got " {} " ' . format (
value , project_value )
@step ( ' The project must contain 3D geometry representing the shape of objects ' )
def step_impl ( context ) :
assert get_subcontext ( ' Body ' , ' Model ' , ' MODEL_VIEW ' )
@step ( ' The project must contain 3D geometry representing clearance zones ' )
def step_impl ( context ) :
assert get_subcontext ( ' Clearance ' , ' Model ' , ' MODEL_VIEW ' )
@step ( ' The project must contain 3D geometry representing the center of gravity of objects ' )
def step_impl ( context ) :
assert get_subcontext ( ' CoG ' , ' Model ' , ' MODEL_VIEW ' )
@step ( ' The project must contain 3D geometry representing the object bounding boxes ' )
def step_impl ( context ) :
assert get_subcontext ( ' Box ' , ' Model ' , ' MODEL_VIEW ' )
def get_subcontext ( identifier , type , target_view ) :
project = IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ]
for rep_context in project . RepresentationContexts :
for subcontext in rep_context . HasSubContexts :
if subcontext . ContextIdentifier == identifier \
and subcontext . ContextType == type \
and subcontext . TargetView == target_view :
return True
assert False , ' The subcontext with identifier {} , type {} , and target view {} could not be found ' . format (
identifier , type , target_view )
# Custom MicroMVD
2019-10-15 11:58:26 +11:00
2020-07-14 18:22:32 +10:00
@step ( ' The element {id} is an {ifc_class} only ' )
def step_impl ( context , id , ifc_class ) :
2020-07-20 16:58:18 +10:00
try :
element = IfcFile . get ( ) . by_id ( id )
except :
assert False , f ' The element with { id } could not be found. '
assert element . is_a ( ) == ifc_class , ' The element {} is an {} instead of {} . ' . format ( element , element . is_a ( ) , ifc_class )
2020-07-14 18:22:32 +10:00
2020-01-02 13:01:17 +11:00
2020-07-14 18:22:32 +10:00
@step ( ' The element {id} is an {ifc_class} ' )
2019-10-15 11:58:26 +11:00
def step_impl ( context , id , ifc_class ) :
2020-07-20 16:58:18 +10:00
try :
element = IfcFile . get ( ) . by_id ( id )
except :
assert False , f ' The element with { id } could not be found. '
assert element . is_a ( ifc_class ) , ' The element {} is an {} instead of {} . ' . format ( element , element . is_a ( ) , ifc_class )
2019-10-15 11:58:26 +11:00
2020-01-02 13:01:17 +11:00
2020-07-21 16:37:11 +10:00
@step ( ' The element {id} is further defined as a {predefined_type} ' )
def step_impl ( context , id , predefined_type ) :
try :
element = IfcFile . get ( ) . by_id ( id )
except :
assert False , f ' The element with { id } could not be found. '
if element . PredefinedType == predefined_type :
return
if element . PredefinedType == ' USERDEFINED ' and element . ObjectType == ' predefined_type ' :
return
2020-07-21 16:40:33 +10:00
assert False , ' The element {} has a predefined type of {} and an object type of {} instead of {} . ' . format ( element , element . PredefinedType , element . ObjectType , predefined_type )
2020-07-21 16:37:11 +10:00
2020-07-14 18:22:32 +10:00
@step ( ' The element {id} should not exist because {reason} ' )
2019-10-15 11:58:26 +11:00
def step_impl ( context , id , reason ) :
2020-07-20 16:58:18 +10:00
try :
element = IfcFile . get ( ) . by_id ( id )
except :
return
2020-07-21 15:26:43 +10:00
assert False , ' This element {} should be reevaluated. ' . format ( element )
2019-11-21 09:46:37 +11:00
2020-01-02 13:01:17 +11:00
2020-04-02 18:38:42 +11:00
@step ( u ' there is at least one {ifc_class} element ' )
2020-02-18 14:03:32 +11:00
def step_impl ( context , ifc_class ) :
assert len ( IfcFile . get ( ) . by_type ( ifc_class ) ) > = 1
2020-04-02 18:38:42 +11:00
@step ( u ' there are no {ifc_class} elements because {reason} ' )
2020-03-06 11:35:05 +11:00
def step_impl ( context , ifc_class , reason ) :
assert len ( IfcFile . get ( ) . by_type ( ifc_class ) ) == 0
2020-04-02 18:38:42 +11:00
@step ( ' all {ifc_class} elements have a name matching the pattern " {pattern} " ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , pattern ) :
import re
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
if not re . search ( pattern , element . Name ) :
assert False
2020-04-02 18:38:42 +11:00
@step ( ' all {ifc_class} elements have an {representation_class} representation ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , representation_class ) :
def is_item_a_representation ( item , representation ) :
if ' / ' in representation :
for cls in representation . split ( ' / ' ) :
if item . is_a ( cls ) :
return True
elif item . is_a ( representation ) :
return True
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
if not element . Representation :
continue
has_representation = False
for representation in element . Representation . Representations :
for item in representation . Items :
if item . is_a ( ' IfcMappedItem ' ) :
# We only check one more level deep.
for item2 in item . MappingSource . MappedRepresentation . Items :
if is_item_a_representation ( item2 , representation_class ) :
has_representation = True
else :
if is_item_a_representation ( item , representation_class ) :
has_representation = True
if not has_representation :
assert False
use_step_matcher ( ' re ' )
2020-04-02 18:38:42 +11:00
@step ( ' all (?P<ifc_class>.*) elements have an? (?P<attribute>.*) attribute ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , attribute ) :
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
if not getattr ( element , attribute ) :
assert False
2020-04-02 18:38:42 +11:00
@step ( ' all (?P<ifc_class>.*) elements have an? (?P<property_path>.* \ ..*) property ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , property_path ) :
pset_name , property_name = property_path . split ( ' . ' )
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
2020-01-16 17:55:37 +11:00
if not IfcFile . get_property ( element , pset_name , property_name ) :
2020-01-02 13:01:17 +11:00
assert False
2020-04-02 18:38:42 +11:00
@step ( ' all (?P<ifc_class>.*) elements have an? (?P<property_path>.* \ ..*) property value matching the pattern " (?P<pattern>.*) " ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , property_path , pattern ) :
import re
pset_name , property_name = property_path . split ( ' . ' )
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
2020-01-16 17:55:37 +11:00
prop = IfcFile . get_property ( element , pset_name , property_name )
if not prop :
2020-01-02 13:01:17 +11:00
assert False
2020-01-16 17:55:37 +11:00
# For now, we only check single values
if prop . is_a ( ' IfcPropertySingleValue ' ) :
if not ( prop . NominalValue \
and re . search ( pattern , prop . NominalValue . wrappedValue ) ) :
assert False
2020-04-02 18:38:42 +11:00
@step ( ' all (?P<ifc_class>.*) elements have an? (?P<attribute>.*) matching the pattern " (?P<pattern>.*) " ' )
2020-01-16 17:55:37 +11:00
def step_impl ( context , ifc_class , attribute , pattern ) :
import re
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
value = getattr ( element , attribute )
print ( f ' Checking value " { value } " for { element } ' )
assert re . search ( pattern , value )
2020-01-02 13:01:17 +11:00
2020-04-02 18:38:42 +11:00
@step ( ' all (?P<ifc_class>.*) elements have an? (?P<attributes>.*) taken from the list in " (?P<list_file>.*) " ' )
2020-02-18 14:03:32 +11:00
def step_impl ( context , ifc_class , attributes , list_file ) :
import csv
values = [ ]
with open ( list_file ) as csvfile :
reader = csv . reader ( csvfile )
for row in reader :
values . append ( row )
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
attribute_values = [ ]
for attribute in attributes . split ( ' , ' ) :
if not hasattr ( element , attribute ) :
assert False , f ' Failed at element { element . GlobalId } '
attribute_values . append ( getattr ( element , attribute ) )
if attribute_values not in values :
assert False , f ' Failed at element { element . GlobalId } '
2020-01-02 13:01:17 +11:00
use_step_matcher ( ' parse ' )
2020-04-02 18:38:42 +11:00
@step ( ' all {ifc_class} elements have a {qto_name} . {quantity_name} quantity ' )
2020-01-02 13:01:17 +11:00
def step_impl ( context , ifc_class , qto_name , quantity_name ) :
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
is_successful = False
if not element . IsDefinedBy :
assert False
for relationship in element . IsDefinedBy :
if relationship . RelatingPropertyDefinition . Name == qto_name :
for quantity in relationship . RelatingPropertyDefinition . Quantities :
if quantity . Name == quantity_name :
is_successful = True
if not is_successful :
assert False
2020-01-16 17:55:37 +11:00
2020-04-02 18:38:42 +11:00
@step ( u ' the project should have geolocation data ' )
2020-01-16 17:55:37 +11:00
def step_impl ( context ) :
if IfcFile . get ( ) . schema == ' IFC2X3 ' :
for site in IfcFile . get ( ) . by_type ( ' IfcSite ' ) :
if not IfcFile . get_pset ( site , ' EPset_MapConversion ' ) :
assert False
else :
project = IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ]
for context in project . RepresentationContexts :
if context . is_a ( ' IfcGeometricRepresentationContext ' ) \
and context . ContextType == ' Model ' \
and context . HasCoordinateOperation :
IfcFile . bookmarks [ ' geolocation ' ] = context . HasCoordinateOperation [ 0 ] . id ( )
return
assert False
2020-04-02 18:38:42 +11:00
@step ( u ' the project geolocation uses the " {crs_name} " CRS ' )
2020-01-16 17:55:37 +11:00
def step_impl ( context , crs_name ) :
if IfcFile . get ( ) . schema == ' IFC2X3 ' :
for site in IfcFile . get ( ) . by_type ( ' IfcSite ' ) :
if IfcFile . get_property ( site , ' EPset_ProjectedCRS ' , ' Name ' ) . NominalValue . wrappedValue != crs_name :
assert False
else :
assert IfcFile . get ( ) . by_id ( IfcFile . bookmarks [ ' geolocation ' ] ) . TargetCRS . Name == crs_name
use_step_matcher ( ' re ' )
2020-04-02 18:38:42 +11:00
@step ( u ' the geolocated datum has an? (?P<attribute>.*) of " (?P<value>.*) " ' )
2020-01-16 17:55:37 +11:00
def step_impl ( context , attribute , value ) :
if IfcFile . get ( ) . schema == ' IFC2X3 ' :
site = IfcFile . get ( ) . by_type ( ' IfcSite ' ) [ 0 ]
actual_value = IfcFile . get_property ( site , ' EPset_MapConversion ' , attribute ) . NominalValue . wrappedValue
else :
actual_value = getattr ( IfcFile . get ( ) . by_id ( IfcFile . bookmarks [ ' geolocation ' ] ) , attribute )
2020-04-02 18:38:42 +11:00
assert str ( actual_value ) == value , f ' The value was { actual_value } '
2020-01-16 17:55:37 +11:00
use_step_matcher ( ' parse ' )
2020-04-02 18:38:42 +11:00
@step ( u ' the project has a {attribute_name} attribute with a value of " {attribute_value} " ' )
2020-02-18 14:03:32 +11:00
def step_impl ( context , attribute_name , attribute_value ) :
2020-01-16 17:55:37 +11:00
project = IfcFile . get ( ) . by_type ( ' IfcProject ' ) [ 0 ]
2020-02-18 14:03:32 +11:00
assert getattr ( project , attribute_name ) == attribute_value
2020-01-16 17:55:37 +11:00
2020-04-02 18:38:42 +11:00
@step ( u ' there is an {ifc_class} element with a {attribute_name} attribute with a value of " {attribute_value} " ' )
2020-02-18 14:03:32 +11:00
def step_impl ( context , ifc_class , attribute_name , attribute_value ) :
elements = IfcFile . get ( ) . by_type ( ifc_class )
for element in elements :
if hasattr ( element , attribute_name ) \
and getattr ( element , attribute_name ) == attribute_value :
2020-01-16 17:55:37 +11:00
return
assert False
2020-04-02 18:38:42 +11:00
@step ( u ' all buildings have an address ' )
2020-01-16 17:55:37 +11:00
def step_impl ( context ) :
for building in IfcFile . get ( ) . by_type ( ' IfcBuilding ' ) :
if not building . BuildingAddress :
assert False , f ' The building " { building . Name } " has no address. '