mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 06:39:13 +00:00
Handle more restrictions, save validation results to JSON objects, modification to classification class, and partly handle material terms
This commit is contained in:
@@ -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:
|
||||||
@@ -230,14 +284,16 @@ class ids:
|
|||||||
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])
|
||||||
|
|||||||
Reference in New Issue
Block a user