Add support for userdefined predefined types in IDS entity facets

This commit is contained in:
Dion Moult
2022-05-10 21:20:10 +10:00
parent ae298de8dc
commit 08fc62d4d9
2 changed files with 56 additions and 32 deletions
+29 -28
View File
@@ -173,7 +173,6 @@ class ids:
logging.basicConfig(level=logging.INFO, format="%(message)s")
logger.setLevel(logging.INFO)
# Consider other way around: for elem, for spec so we can see if an element pass all IDSes?
for spec in self.specifications:
self.ifc_applicable = 0
@@ -216,8 +215,8 @@ class specification:
):
"""Create a specification to be added in ids.
:param name:, defaults to "Specification"
:type name: str, optional
:param name: Name describing the specification to a contract reader
:type name: str
:param use: 'required'|'optional', defaults to "required"
:type use: str, optional
"""
@@ -287,17 +286,19 @@ class specification:
return spec
def add_applicability(self, facet):
"""Applicability specifies what conditions must be meet for an IFC object to be used for validation. Note, that at least one entity facet is required.
"""Applicability specifies a filter for IFC entities are to be validated.
:param facet: any of entity|classification|property|material
At least one filter must be added.
:param facet: any of entity|attribute|classification|property|material
:type facet: facet
Example::
i = ids.ids()
i.specifications.append(ids.specification(name="Test_Specification"))
e = ids.entity.create(name="Test_Name", predefinedType="Test_PredefinedType")
i.specifications[0].add_applicability(e)
specs = ids.ids()
spec = ids.specification(name="Test_Specification")
spec.add_applicability(ids.entity.create(name="IfcWall"))
specs.specifications.append(spec)
"""
if self.applicability:
self.applicability = boolean_and(self.applicability.terms + [facet])
@@ -305,9 +306,11 @@ class specification:
self.applicability = boolean_and([facet])
def add_requirement(self, facet):
"""Requirement is validated on all applicable IFC elements. Note, that at least one facet of any type is required.
"""A requirement specifies data to be checked for all applicable entities.
:param facet: any of entity|classification|property|material
At least one requirement must be added.
:param facet: any of entity|attribute|classification|property|material|partOf
:type facet: facet
"""
if self.requirements:
@@ -487,18 +490,17 @@ class entity(facet):
:return: Xmlschema compliant dictionary.
:rtype: dict
"""
fac_dict = {"name": parameter_asdict(self.name)}
if "predefinedType" in self:
if self.predefinedType:
fac_dict["predefinedType"] = parameter_asdict(self.predefinedType)
# try:
# fac_dict["predefinedType"] = parameter_asdict(self.predefinedType)
# except (RecursionError, UnboundLocalError) as e:
# print(e)
return fac_dict
results = {"name": parameter_asdict(self.name)}
if self.predefinedType:
results["predefinedType"] = parameter_asdict(self.predefinedType)
return results
def __call__(self, inst, logger):
"""Validate an ifc instance against that entity facet.
def __call__(self, inst, logger=None):
"""Validate an entity.
When a simple value is provided for the name, subclasses are also
treated as valid. PredefinedType checks support userdefined types for
both element and type elements.
:param inst: IFC entity element
:type inst: IFC entity
@@ -507,13 +509,12 @@ class entity(facet):
:return: result of the validation as bool and message
:rtype: facet_evaluation(bool, str)
"""
# @nb with inheritance
if self.predefinedType and hasattr(inst, "PredefinedType"):
if self.predefinedType:
predefined_type = ifcopenshell.util.element.get_predefined_type(inst)
self.message = "an entity name '%(name)s' of predefined type '%(predefinedType)s'"
return facet_evaluation(
inst.is_a(self.name) and inst.PredefinedType == self.predefinedType,
self.message % {"name": inst.is_a(), "predefinedType": inst.PredefinedType},
inst.is_a(self.name) and predefined_type == self.predefinedType,
self.message % {"name": inst.is_a(), "predefinedType": predefined_type},
)
else:
self.message = "an entity name '%(name)s'"
@@ -558,7 +559,7 @@ class attribute(facet):
fac_dict["@location"] = self.location
return fac_dict
def __call__(self, inst, logger):
def __call__(self, inst, logger=None):
"""Validate an ifc instance against that entity facet.
:param inst: IFC entity element
+27 -4
View File
@@ -263,10 +263,33 @@ class TestIdsAuthoring(unittest.TestCase):
i.specifications[0].add_requirement(m)
self.assertEqual(i.specifications[0].requirements.terms[1].value, "Test_Value")
def test_entity_create(self):
e = ids.entity.create(name="Test_Name", predefinedType="Test_PredefinedType")
self.assertEqual(e.name, "Test_Name")
self.assertEqual(e.predefinedType, "Test_PredefinedType")
def test_create_an_entity_facet(self):
facet = ids.entity.create(name="IfcName")
assert facet.asdict() == {"name": {"simpleValue": "IfcName"}}
facet = ids.entity.create(name="IfcName", predefinedType="predefinedType")
assert facet.asdict() == {
"name": {"simpleValue": "IfcName"},
"predefinedType": {"simpleValue": "predefinedType"},
}
def test_filtering_using_an_entity_facet(self):
ifc = ifcopenshell.file()
facet = ids.entity.create(name="IfcWall")
assert bool(facet(ifc.createIfcWall())) is True
assert bool(facet(ifc.createIfcWall(PredefinedType="SOLIDWALL"))) is True
assert bool(facet(ifc.createIfcSlab())) is False
facet = ids.entity.create(name="IfcWall", predefinedType="SOLIDWALL")
assert bool(facet(ifc.createIfcWall())) is False
assert bool(facet(ifc.createIfcWall(PredefinedType="SOLIDWALL"))) is True
assert bool(facet(ifc.createIfcWall(PredefinedType="PARTITIONING"))) is False
facet = ids.entity.create(name="IfcWall", predefinedType="WALDO")
assert bool(facet(ifc.createIfcWall(PredefinedType="USERDEFINED", ObjectType="WALDO"))) is True
facet = ids.entity.create(name="IfcWallType", predefinedType="WALDO")
assert bool(facet(ifc.createIfcWallType(PredefinedType="USERDEFINED", ElementType="WALDO"))) is True
def test_attribute_create(self):
attribute = ids.attribute.create(name="Name", value="Value")