From 304acdccefda1a64fc1e161c3a0ea700ec0df4c7 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Tue, 28 Nov 2023 17:20:20 +1100 Subject: [PATCH] Fix #3059. Implement IfcTester prohibited string templates (taking into account double negatives) --- .../blenderbim/bim/module/style/ui.py | 2 +- .../blenderbim/bim/module/tester/prop.py | 2 +- src/ifctester/ifctester/facet.py | 36 +++++++++++++++++-- src/ifctester/ifctester/reporter.py | 4 +-- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/style/ui.py b/src/blenderbim/blenderbim/bim/module/style/ui.py index a02fb78d17..d4eea94b07 100644 --- a/src/blenderbim/blenderbim/bim/module/style/ui.py +++ b/src/blenderbim/blenderbim/bim/module/style/ui.py @@ -82,7 +82,7 @@ class BIM_PT_styles(Panel): row.operator("bim.remove_style", text="", icon="X").style = style.ifc_definition_id if self.props.style_type == "IfcSurfaceStyle": - self.layout.label(text="Choose current style's aspect to edit:") + self.layout.label(text="Surface Style Element:") col = self.layout.column(align=True) row = col.row(align=True) diff --git a/src/blenderbim/blenderbim/bim/module/tester/prop.py b/src/blenderbim/blenderbim/bim/module/tester/prop.py index d2b919a64e..05bb2054ad 100644 --- a/src/blenderbim/blenderbim/bim/module/tester/prop.py +++ b/src/blenderbim/blenderbim/bim/module/tester/prop.py @@ -52,7 +52,7 @@ class IfcTesterProperties(PropertyGroup): ifc_file: StringProperty(default="", name="IFC File") should_load_from_memory: BoolProperty(default=False, name="Load from Memory") generate_html_report: BoolProperty(default=False, name="Generate HTML report") - flag : BoolProperty(default=False, name="Flag failed entities") + flag: BoolProperty(default=False, name="Flag Failed Entities") active_specification_index: IntProperty(name="Active Specification Index", update=update_active_specification_index) old_index: IntProperty(name="", default=0) active_failed_entity_index: IntProperty(name="Active Failed Entity Index") diff --git a/src/ifctester/ifctester/facet.py b/src/ifctester/ifctester/facet.py index f068ca4400..29e767825b 100644 --- a/src/ifctester/ifctester/facet.py +++ b/src/ifctester/ifctester/facet.py @@ -85,11 +85,16 @@ class Facet: def filter(self, ifc_file, elements): return [e for e in elements if self(e)] - def to_string(self, clause_type): + def to_string(self, clause_type, specification=None, requirement=None): if clause_type == "applicability": templates = self.applicability_templates elif clause_type == "requirement": - templates = self.requirement_templates + is_prohibited = False + if specification.maxOccurs == 0: + is_prohibited = not is_prohibited + if requirement.maxOccurs == 0: + is_prohibited = not is_prohibited + templates = self.prohibited_templates if is_prohibited else self.requirement_templates for template in templates: total_variables = len(template) - len(template.replace("{", "")) @@ -139,6 +144,10 @@ class Entity(Facet): "Shall be {name} data of type {predefinedType}", "Shall be {name} data", ] + self.prohibited_templates = [ + "Shall not be {name} data of type {predefinedType}", + "Shall not be {name} data", + ] super().__init__(name, predefinedType, instructions) def filter(self, ifc_file, elements): @@ -192,6 +201,10 @@ class Attribute(Facet): "The {name} shall be {value}", "The {name} shall be provided", ] + self.prohibited_templates = [ + "The {name} shall not be {value}", + "The {name} shall not be provided", + ] super().__init__(name, value, minOccurs, maxOccurs, instructions) def filter(self, ifc_file, elements): @@ -311,6 +324,13 @@ class Classification(Facet): "Shall be classified as {value}", "Shall be classified", ] + self.prohibited_templates = [ + "Shall not have a {system} reference of {value}", + "Shall not be classified using {system}", + "Shall not be classified as {value}", + "Shall not be classified", + ] + super().__init__(value, system, uri, minOccurs, maxOccurs, instructions) def filter(self, ifc_file, elements): @@ -370,6 +390,10 @@ class PartOf(Facet): "An element must have an {relation} relationship with an {name}", "An element must have an {relation} relationship", ] + self.prohibited_templates = [ + "An element must not have an {relation} relationship with an {name}", + "An element must not have an {relation} relationship", + ] super().__init__(name, predefinedType, relation, minOccurs, maxOccurs, instructions) def filter(self, ifc_file, elements): @@ -584,6 +608,10 @@ class Property(Facet): "{name} data shall be {value} and in the dataset {propertySet}", "{name} data shall be provided in the dataset {propertySet}", ] + self.prohibited_templates = [ + "{name} data shall not be {value} and in the dataset {propertySet}", + "{name} data shall not be provided in the dataset {propertySet}", + ] super().__init__(propertySet, name, value, datatype, uri, minOccurs, maxOccurs, instructions) def filter(self, ifc_file, elements): @@ -845,6 +873,10 @@ class Material(Facet): "Shall have a material of {value}", "Shall have a material", ] + self.prohibited_templates = [ + "Shall not have a material of {value}", + "Shall not have a material", + ] super().__init__(value, uri, minOccurs, maxOccurs, instructions) def filter(self, ifc_file, elements): diff --git a/src/ifctester/ifctester/reporter.py b/src/ifctester/ifctester/reporter.py index 5b2e24a448..0b7e62cb3b 100644 --- a/src/ifctester/ifctester/reporter.py +++ b/src/ifctester/ifctester/reporter.py @@ -106,7 +106,7 @@ class Console(Reporter): for requirement in specification.requirements: self.set_style("reset") self.set_style("red") if requirement.failed_entities else self.set_style("green") - self.print(" " * 8 + requirement.to_string("requirement")) + self.print(" " * 8 + requirement.to_string("requirement", specification, requirement)) self.set_style("reset") for i, element in enumerate(requirement.failed_entities[0:10]): self.print(" " * 12, end="") @@ -217,7 +217,7 @@ class Json(Reporter): total_checks_pass += total_pass requirements.append( { - "description": requirement.to_string("requirement") if specification.maxOccurs != 0 else requirement.to_string("requirement").replace('shall', 'shall not'), + "description": requirement.to_string("requirement", specification, requirement), "status": requirement.status, "failed_entities": self.report_failed_entities(requirement), "total_applicable": total_applicable,