mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 06:32:09 +00:00
Implement null check for IDS properties, and support restrictions
This commit is contained in:
@@ -873,7 +873,7 @@ class property(facet):
|
|||||||
# TODO '@href': 'http://identifier.buildingsmart.org/uri/buildingsmart/ifc-4.3/prop/FireRating', #https://identifier.buildingsmart.org/uri/something
|
# TODO '@href': 'http://identifier.buildingsmart.org/uri/buildingsmart/ifc-4.3/prop/FireRating', #https://identifier.buildingsmart.org/uri/something
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def __call__(self, inst, logger):
|
def __call__(self, inst, logger=None):
|
||||||
"""Validate an ifc instance against that property facet.
|
"""Validate an ifc instance against that property facet.
|
||||||
|
|
||||||
:param inst: IFC entity element
|
:param inst: IFC entity element
|
||||||
@@ -883,46 +883,41 @@ class property(facet):
|
|||||||
:return: result of the validation as bool and message
|
:return: result of the validation as bool and message
|
||||||
:rtype: facet_evaluation(bool, str)
|
:rtype: facet_evaluation(bool, str)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# self.location = self.node["@location"]
|
|
||||||
|
|
||||||
# TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue'
|
|
||||||
try:
|
|
||||||
instance_props = ifcopenshell.util.element.get_psets(inst)
|
|
||||||
except AttributeError:
|
|
||||||
instance_props = {}
|
|
||||||
|
|
||||||
if ifcopenshell.util.element.get_type(inst):
|
|
||||||
# TODO sometimes AttributeError: 'str' object has no attribute 'wrappedValue'
|
|
||||||
try:
|
|
||||||
type_props = ifcopenshell.util.element.get_psets(ifcopenshell.util.element.get_type(inst))
|
|
||||||
except AttributeError:
|
|
||||||
type_props = {}
|
|
||||||
else:
|
|
||||||
type_props = {}
|
|
||||||
|
|
||||||
if self.location == "instance":
|
if self.location == "instance":
|
||||||
props = instance_props
|
all_psets = ifcopenshell.util.element.get_psets(inst, should_inherit=False)
|
||||||
elif self.location == "type" and type_props:
|
elif self.location == "type":
|
||||||
props = type_props
|
element_type = ifcopenshell.util.element.get_type(inst)
|
||||||
elif self.location == "any" and (instance_props or type_props):
|
all_psets = ifcopenshell.util.element.get_psets(element_type, should_inherit=False)
|
||||||
props = {**instance_props, **type_props}
|
elif self.location == "any":
|
||||||
|
all_psets = ifcopenshell.util.element.get_psets(inst)
|
||||||
|
|
||||||
|
if isinstance(self.propertySet, str):
|
||||||
|
pset = all_psets.get(self.propertySet, None)
|
||||||
|
psets = {self.propertySet: pset} if pset else {}
|
||||||
else:
|
else:
|
||||||
|
psets = {k: v for k, v in all_psets.items() if k == self.propertySet}
|
||||||
|
|
||||||
|
is_pass = bool(psets)
|
||||||
|
|
||||||
|
if is_pass:
|
||||||
props = {}
|
props = {}
|
||||||
|
for pset_name, pset_props in psets.items():
|
||||||
|
props[pset_name] = {}
|
||||||
|
if isinstance(self.name, str):
|
||||||
|
prop = pset_props.get(self.name)
|
||||||
|
if prop:
|
||||||
|
props[pset_name][self.name] = prop
|
||||||
|
else:
|
||||||
|
props[pset_name] = {k: v for k, v in pset_props.items() if k == self.name}
|
||||||
|
|
||||||
pset = props.get(self.propertySet)
|
if not bool(props[pset_name]):
|
||||||
val = pset.get(self.name) if pset else None
|
is_pass = False
|
||||||
|
break
|
||||||
|
|
||||||
self.location_msg = location[self.location]
|
if self.value:
|
||||||
di = {"name": self.name, "propertySet": self.propertySet, "value": "'%s'" % val, "location": self.location_msg}
|
if any([v != self.value for v in props[pset_name].values()]):
|
||||||
|
is_pass = False
|
||||||
if val is not None:
|
break
|
||||||
msg = self.message % di
|
|
||||||
else:
|
|
||||||
if pset:
|
|
||||||
msg = "does not have %(location)sproperty '%(name)s' in a set '%(propertySet)s'" % di
|
|
||||||
else:
|
|
||||||
msg = "does not have %(location)sset '%(propertySet)s'" % di
|
|
||||||
|
|
||||||
# TODO implement data type comparison
|
# TODO implement data type comparison
|
||||||
# xs:string
|
# xs:string
|
||||||
@@ -935,7 +930,7 @@ class property(facet):
|
|||||||
# xs:dateTime YYYY-MM-DDThh:mm:ss
|
# xs:dateTime YYYY-MM-DDThh:mm:ss
|
||||||
# xs:duration PnYnMnDTnHnMnS
|
# xs:duration PnYnMnDTnHnMnS
|
||||||
|
|
||||||
return facet_evaluation(val == self.value, msg)
|
return facet_evaluation(is_pass, "todo")
|
||||||
|
|
||||||
|
|
||||||
class material(facet):
|
class material(facet):
|
||||||
|
|||||||
@@ -621,6 +621,116 @@ class TestIdsAuthoring(unittest.TestCase):
|
|||||||
"@instructions": "instructions",
|
"@instructions": "instructions",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def test_filtering_using_a_property_facet(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
|
||||||
|
# A name check by itself only checks that a property is non-null and non empty string
|
||||||
|
# The logic is that unfortunately most BIM users cannot differentiate between the two.
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": None})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
|
||||||
|
# A simple value checks an exact case-sensitive match
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", value="Bar")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Baz"})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
|
||||||
|
# Simple values only check string matches
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", value="1")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "1"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": ifc.createIfcInteger(1)})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
|
||||||
|
# Restrictions are supported for property sets. If multiple are matched, all must satisfy requirements.
|
||||||
|
restriction = ids.restriction.create(options="Foo_.*", type="pattern", base="string")
|
||||||
|
facet = ids.property.create(propertySet=restriction, name="Foo")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Baz")
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
|
||||||
|
# Restrictions are supported for names. If multiple are matched, all must satisfy requirements.
|
||||||
|
restriction = ids.restriction.create(options="Foo.*", type="pattern", base="string")
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name=restriction, value="x")
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "x"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
|
||||||
|
# Restrictions are supported for values. If multiple are matched, all must satisfy requirements.
|
||||||
|
restriction1 = ids.restriction.create(options="Foo.*", type="pattern", base="string")
|
||||||
|
restriction2 = ids.restriction.create(options=["x", "y"], type="enumeration", base="string")
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name=restriction1, value=restriction2)
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=element, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "y"})
|
||||||
|
assert bool(facet(element)) is True
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foobar": "x", "Foobaz": "z"})
|
||||||
|
assert bool(facet(element)) is False
|
||||||
|
|
||||||
|
# Location instance only checks on the instance, even if the instance is a type. Yes, weird, I know.
|
||||||
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall_type, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", location="instance")
|
||||||
|
assert bool(facet(wall)) is False
|
||||||
|
assert bool(facet(wall_type)) is True
|
||||||
|
|
||||||
|
# Location type only checks the type
|
||||||
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall_type, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", location="type")
|
||||||
|
assert bool(facet(wall)) is True
|
||||||
|
assert bool(facet(wall_type)) is True
|
||||||
|
|
||||||
|
# Location any checks inherited properties from the type
|
||||||
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall_type, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", location="any")
|
||||||
|
assert bool(facet(wall)) is True
|
||||||
|
assert bool(facet(wall_type)) is True
|
||||||
|
|
||||||
|
# Location any checks overriden properties from the occurrence
|
||||||
|
wall = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||||
|
wall_type = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWallType")
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=wall, relating_type=wall_type)
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall_type, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Baz"})
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=wall, name="Foo_Bar")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"Foo": "Bar"})
|
||||||
|
facet = ids.property.create(propertySet="Foo_Bar", name="Foo", value="Bar", location="any")
|
||||||
|
assert bool(facet(wall)) is True
|
||||||
|
assert bool(facet(wall_type)) is False
|
||||||
|
|
||||||
def test_material_create(self):
|
def test_material_create(self):
|
||||||
m = ids.material.create(location="any", value="Test_Value")
|
m = ids.material.create(location="any", value="Test_Value")
|
||||||
self.assertEqual(m.location, "any")
|
self.assertEqual(m.location, "any")
|
||||||
|
|||||||
Reference in New Issue
Block a user