Handle more restrictions, save validation results to JSON objects, modification to classification class, and partly handle material terms

This commit is contained in:
johltn
2021-04-09 10:16:08 +02:00
committed by Thomas Krijnen
parent 4b637c7049
commit 0c9d555693
+80 -24
View File
@@ -64,7 +64,11 @@ class facet(metaclass=meta_facet):
yield k, getattr(self, k) yield k, getattr(self, k)
def __str__(self): def __str__(self):
return self.message % dict(list(self)) di = dict(list(self))
for k, v in di.items():
if isinstance(v, str) and not len(v):
di[k] = "not specified"
return self.message % di
class entity(facet): class entity(facet):
@@ -88,19 +92,19 @@ class classification(facet):
""" """
parameters = ["system", "value"] parameters = ["system", "value"]
message = "a classification reference to '%(value)s' from '%(system)s'" message = "a classification reference '%(value)s' from '%(system)s'"
def __call__(self, inst, logger): def __call__(self, inst, logger):
refs = [] refs = []
for association in inst.HasAssociations: for association in inst.HasAssociations:
if association.is_a("IfcRelAssociatesClassification"): if association.is_a("IfcRelAssociatesClassification"):
cref = association.RelatingClassification cref = association.RelatingClassification
refs.append((cref.ReferencedSource, cref.Name)) refs.append((cref.ReferencedSource.Name, cref.ItemReference))
return facet_evaluation( return facet_evaluation(
(self.system, self.value) in refs, (self.system, self.value) in refs,
# @todo # @todo
"", "[classification_eval_todo]",
) )
@@ -109,17 +113,19 @@ class property(facet):
The IDS property facet implenented using `ifcopenshell.util.element` The IDS property facet implenented using `ifcopenshell.util.element`
""" """
parameters = ["property", "propertyset", "value"] parameters = ["name", "propertyset", "value"]
message = "a property '%(property)s' in '%(propertyset)s' with value '%(value)s'"
# import pdb;pdb.set_trace()
message = "a property '%(name)s' in '%(propertyset)s' with value '%(value)s'"
def __call__(self, inst, logger): def __call__(self, inst, logger):
props = ifcopenshell.util.element.get_psets(inst) props = ifcopenshell.util.element.get_psets(inst)
pset = props.get(self.propertyset) pset = props.get(self.propertyset)
val = pset.get(self.property) if pset else None val = pset.get(self.name) if pset else None
logger.debug("Testing %s == %s", val, self.value) logger.debug("Testing %s == %s", val, self.value)
di = { di = {
"property": self.property, "name": self.name,
"propertyset": self.propertyset, "propertyset": self.propertyset,
"value": val, "value": val,
} }
@@ -128,13 +134,38 @@ class property(facet):
msg = self.message % di msg = self.message % di
else: else:
if pset: if pset:
msg = "a set '%(propertyset)s', but no property '%(property)'" % di msg = "a set '%(propertyset)s', but no property '%(name)'" % di
else: else:
msg = "no set '%(propertyset)s'" % di msg = "no set '%(propertyset)s'" % di
return facet_evaluation(val == self.value, msg) return facet_evaluation(val == self.value, msg)
class material(facet):
"""
The IDS material facet
"""
parameters = ["name", "value"]
message = "a material '%(name)s with value '%(value)s'"
def __call__(self, inst, logger):
material_relations = [rel for rel in inst.HasAssociations if rel.is_a("IfcRelAssociatesMaterial")]
names = []
for rel in material_relations:
if rel.RelatingMaterial.is_a() == "IfcMaterialLayerSetUsage":
layers = rel.RelatingMaterial.ForLayerSet.MaterialLayers
names = [layer.Material.Name for layer in layers]
elif rel.RelatingMaterial.is_a() == "IfcMaterial":
names.append(rel.RelatingMaterial.Name)
return facet_evaluation(
0,
# @todo
"[material_eval_todo]",
)
class boolean_logic: class boolean_logic:
""" """
Boolean conjunction over a collection of functions Boolean conjunction over a collection of functions
@@ -166,17 +197,39 @@ class restriction:
""" """
def __init__(self, node): def __init__(self, node):
self.options = [
n.getAttribute("value")
for n in node.childNodes
if n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("enumeration")
]
self.restriction_on = node.getAttribute("base")
self.options = []
self.type = []
for n in node.childNodes:
if n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("enumeration"):
self.options.append(n.getAttribute("value"))
self.type = "enumeration"
elif n.nodeType == n.ELEMENT_NODE and (n.tagName.endswith("Inclusive") or n.tagName.endswith("Exclusive")):
self.options.append(n.getAttribute("value"))
self.type = "bounds"
elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("length"):
self.options.append(n.getAttribute("value"))
self.type = "length"
elif n.nodeType == n.ELEMENT_NODE and n.tagName.endswith("pattern"):
self.options.append(n.getAttribute("value"))
self.type = "pattern"
# "Given an instance with %(applicabiliy)s\nWe expect %(requirements)s" % self.__dict__
def __eq__(self, other): def __eq__(self, other):
return other in self.options return other in self.options
def __repr__(self): def __repr__(self):
return " or ".join(self.options) if self.type == "enumeration":
return " or ".join(self.options)
elif self.type == "bounds":
self.options.sort()
return "of type %s, having a value between %s and %s" % (self.restriction_on, self.options[0], self.options[1])
elif self.type == "length":
return "of type %s with a length of %s" % (self.restriction_on, self.options[0])
elif self.type == "pattern":
return "of type %s respecting pattern %s" % (self.restriction_on, self.options[0])
class specification: class specification:
@@ -197,18 +250,19 @@ class specification:
phrases[0].tagName == "applicability" or error("expected <applicability>") phrases[0].tagName == "applicability" or error("expected <applicability>")
phrases[1].tagName == "requirements" or error("expected <requirements>") phrases[1].tagName == "requirements" or error("expected <requirements>")
self.applicability, self.requirements = (boolean_and(parse_rules(phrase)) for phrase in phrases) self.applicabiliy, self.requirements = (boolean_and(parse_rules(phrase)) for phrase in phrases)
def __call__(self, inst, logger): def __call__(self, inst, logger):
if self.applicability(inst, logger): if self.applicabiliy(inst, logger):
valid = self.requirements(inst, logger) valid = self.requirements(inst, logger)
if valid: if valid:
logger.info(str(self) + "\n%s has" % inst + " " + str(valid) + " so is compliant") logger.info({'guid':inst.GlobalId, 'result':valid.success,'sentence':str(self) + "\n%s has" % inst + " " + str(valid) + " so is compliant"})
else: else:
logger.error(str(self) + "\n%s has" % inst + " " + str(valid) + " so is not compliant") logger.error({'guid':inst.GlobalId, 'result':valid.success, 'sentence':str(self) + "\n%s has" % inst + " " + str(valid) + " so is not compliant"})
def __str__(self): def __str__(self):
return "Given an instance with %(applicability)s\nWe expect %(requirements)s" % self.__dict__ return "Given an instance with %(applicabiliy)s\nWe expect %(requirements)s" % self.__dict__
class ids: class ids:
@@ -229,15 +283,17 @@ class ids:
for spec in self.specifications: for spec in self.specifications:
for elem in ifc_file.by_type("IfcObject"): for elem in ifc_file.by_type("IfcObject"):
spec(elem, logger) spec(elem, logger)
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys, os
import logging import logging
import ifcopenshell import ifcopenshell
filename = os.path.join(os.getcwd(), "ids.txt")
logger = logging.getLogger("IDS") logger = logging.getLogger("IDS")
logging.basicConfig(level=logging.INFO, format="%(message)s") logging.basicConfig(filename=filename, level=logging.INFO, format="%(message)s")
logging.FileHandler(filename, mode='w')
ids_file = ids(sys.argv[1]) ids_file = ids(sys.argv[1])
ifc_file = ifcopenshell.open(sys.argv[2]) ifc_file = ifcopenshell.open(sys.argv[2])