Fix #4986. IfcTester now determines whether a spec is relevant to the ifc version

This commit is contained in:
Dion Moult
2024-08-02 22:02:56 +10:00
parent 8514a426bb
commit 15f6b3803f
3 changed files with 133 additions and 110 deletions
+12 -4
View File
@@ -139,7 +139,9 @@ class Ids:
ET.ElementTree(get_schema().encode(self.asdict())).write(filepath, encoding="utf-8", xml_declaration=True) ET.ElementTree(get_schema().encode(self.asdict())).write(filepath, encoding="utf-8", xml_declaration=True)
return get_schema().is_valid(filepath) return get_schema().is_valid(filepath)
def validate(self, ifc_file: ifcopenshell.file, filter_version=False, filepath: Optional[str] = None) -> None: def validate(
self, ifc_file: ifcopenshell.file, should_filter_version: bool = False, filepath: Optional[str] = None
) -> None:
if filepath: if filepath:
self.filepath = filepath self.filepath = filepath
self.filename = os.path.basename(filepath) self.filename = os.path.basename(filepath)
@@ -149,7 +151,8 @@ class Ids:
get_psets.cache_clear() get_psets.cache_clear()
for specification in self.specifications: for specification in self.specifications:
specification.reset_status() specification.reset_status()
specification.validate(ifc_file, filter_version=filter_version) specification.check_ifc_version(ifc_file)
specification.validate(ifc_file, should_filter_version=should_filter_version)
class Specification: class Specification:
@@ -177,6 +180,7 @@ class Specification:
self.passed_entities: set[ifcopenshell.entity_instance] = set() self.passed_entities: set[ifcopenshell.entity_instance] = set()
self.failed_entities: set[ifcopenshell.entity_instance] = set() self.failed_entities: set[ifcopenshell.entity_instance] = set()
self.status = None self.status = None
self.is_ifc_version = None
def asdict(self): def asdict(self):
results = { results = {
@@ -246,8 +250,12 @@ class Specification:
facet.failures.clear() facet.failures.clear()
self.status = None self.status = None
def validate(self, ifc_file: ifcopenshell.file, filter_version=False) -> None: def check_ifc_version(self, ifc_file: ifcopenshell.file) -> bool:
if filter_version and ifc_file.schema not in self.ifcVersion: self.is_ifc_version = ifc_file.schema_identifier in self.ifcVersion
return self.is_ifc_version
def validate(self, ifc_file: ifcopenshell.file, should_filter_version: bool = False) -> None:
if should_filter_version and not self.is_ifc_version:
return return
elements = None elements = None
+2
View File
@@ -76,6 +76,7 @@ class ResultsSpecification(TypedDict):
description: str description: str
instructions: str instructions: str
status: bool status: bool
is_ifc_version: bool
total_applicable: int total_applicable: int
total_applicable_pass: int total_applicable_pass: int
total_applicable_fail: int total_applicable_fail: int
@@ -359,6 +360,7 @@ class Json(Reporter):
description=specification.description, description=specification.description,
instructions=specification.instructions, instructions=specification.instructions,
status=specification.status, status=specification.status,
is_ifc_version=specification.is_ifc_version,
total_applicable=total_applicable, total_applicable=total_applicable,
total_applicable_pass=total_applicable_pass, total_applicable_pass=total_applicable_pass,
total_applicable_fail=total_applicable - total_applicable_pass, total_applicable_fail=total_applicable - total_applicable_pass,
+119 -106
View File
@@ -43,7 +43,7 @@
p.unspecified { background-color: #994f00; padding: 5px; border-radius: 5px; color: #fff; } p.unspecified { background-color: #994f00; padding: 5px; border-radius: 5px; color: #fff; }
p.skipped { background-color: #8b8d8f; padding: 5px; border-radius: 5px; color: #fff; } p.skipped { background-color: #8b8d8f; padding: 5px; border-radius: 5px; color: #fff; }
p.description { background-color: #eee; border-radius: 5px; padding: 20px; margin-left: auto; margin-right: auto; display: inline-block; font-weight: bold;} p.description { background-color: #eee; border-radius: 5px; padding: 20px; margin-left: auto; margin-right: auto; display: inline-block; font-weight: bold;}
li { padding: 10px; font-family: monospace; } li { padding: 10px; font-family: monospace; border-radius: 5px; margin-bottom: 5px; }
li.pass { background-color: var(--light-green); color: #333; } li.pass { background-color: var(--light-green); color: #333; }
li.fail { background-color: var(--light-red); color: #900; } li.fail { background-color: var(--light-red); color: #900; }
li.unspecified { background-color: #ffd37f; color: #a30; } li.unspecified { background-color: #ffd37f; color: #a30; }
@@ -69,6 +69,8 @@
tbody tr:nth-child(odd) { background-color: rgba(1, 1, 1, 0.05); } tbody tr:nth-child(odd) { background-color: rgba(1, 1, 1, 0.05); }
tbody tr:nth-child(even) { background-color: rgba(1, 1, 1, 0.1); } tbody tr:nth-child(even) { background-color: rgba(1, 1, 1, 0.1); }
tbody tr:hover { background-color: rgba(0, 0, 0, 0); } tbody tr:hover { background-color: rgba(0, 0, 0, 0); }
div.info { float: left; width: 30%; }
div.results { float: left; width: calc(70% - 20px); margin-left: 20px; padding: 20px; border-radius: 5px; background-color: #fafafa; }
</style> </style>
</head> </head>
<body> <body>
@@ -94,113 +96,124 @@
</p> </p>
<hr> <hr>
{{#specifications}} {{#specifications}}
<section> <section style="clear: both; overflow: hidden;">
<h2>{{name}}</h2> <div class="info">
{{#description}} <h2>{{name}}</h2>
<p>{{description}}</p> {{#description}}
{{/description}} <p>{{description}}</p>
{{#instructions}} {{/description}}
<p><em>{{instructions}}</em></p> {{#instructions}}
{{/instructions}} <p><em>{{instructions}}</em></p>
{{/instructions}}
<div class="container"> <div class="container">
<div class="{{#status}}pass{{/status}}{{^status}}fail{{/status}} percent" style="width: {{percent_checks_pass}}%;">{{percent_checks_pass}}%</div> <div class="{{#status}}pass{{/status}}{{^status}}fail{{/status}} percent" style="width: {{percent_checks_pass}}%;">{{percent_checks_pass}}%</div>
</div>
<p>
<span class="item {{#status}}pass{{/status}}{{^status}}fail{{/status}}">{{#status}}Pass{{/status}}{{^status}}Fail{{/status}}</span>
<span class="item">
Checks passed: <strong>{{total_checks_pass}}</strong> / <strong>{{total_checks}}</strong>
</span>
<span class="item">
Elements passed: <strong>{{total_applicable_pass}}</strong> / <strong>{{total_applicable}}</strong>
</span>
</p>
{{^is_ifc_version}}
<p>
<span class="item">
<strong>Warning:</strong> specification does not apply to this IFC version
</span>
</p>
{{/is_ifc_version}}
</div>
<div class="results">
<p>
<strong>Applicability</strong>
</p>
<ul>
{{#applicability}}
<li>{{.}}</li>
{{/applicability}}
</ul>
<p>
<strong>Requirements</strong>
</p>
<ol>
{{#requirements}}
<li class="{{#status}}pass{{/status}}{{^status}}fail{{/status}}">
<details>
<summary>
{{description}}
</summary>
{{#total_pass}}
<table class="pass">
<thead>
<tr>
<th>Class</th>
<th>PredefinedType</th>
<th>Name</th>
<th>Description</th>
<th>GlobalId</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{{#passed_entities}}
<tr>
<td>{{class}}</td>
<td>{{predefined_type}}</td>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{global_id}}</td>
<td>{{tag}}</td>
</tr>
{{/passed_entities}}
{{#has_omitted_passes}}
<tr>
<td colspan="7"> ... {{total_omitted_passes}} more passing elements not shown out of {{total_passed_entities}} total ...</td>
</tr>
{{/has_omitted_passes}}
</tbody>
</table>
{{/total_pass}}
{{#total_fail}}
<table class="fail">
<thead>
<tr>
<th>Class</th>
<th>PredefinedType</th>
<th>Name</th>
<th>Description</th>
<th>Warning</th>
<th>GlobalId</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{{#failed_entities}}
<tr>
<td>{{class}}</td>
<td>{{predefined_type}}</td>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{reason}}</td>
<td>{{global_id}}</td>
<td>{{tag}}</td>
</tr>
{{/failed_entities}}
{{#has_omitted_failures}}
<tr>
<td colspan="7"> ... {{total_omitted_failures}} more failing elements not shown out of {{total_failed_entities}} total ...</td>
</tr>
{{/has_omitted_failures}}
</tbody>
</table>
{{/total_fail}}
</details>
</li>
{{/requirements}}
</ol>
</div> </div>
<p>
<span class="item {{#status}}pass{{/status}}{{^status}}fail{{/status}}">{{#status}}Pass{{/status}}{{^status}}Fail{{/status}}</span>
<span class="item">
Checks passed: <strong>{{total_checks_pass}}</strong> / <strong>{{total_checks}}</strong>
</span>
<span class="item">
Elements passed: <strong>{{total_applicable_pass}}</strong> / <strong>{{total_applicable}}</strong>
</span>
</p>
<p>
<strong>Applicability</strong>
</p>
<ul>
{{#applicability}}
<li>{{.}}</li>
{{/applicability}}
</ul>
<p>
<strong>Requirements</strong>
</p>
<ol>
{{#requirements}}
<li class="{{#status}}pass{{/status}}{{^status}}fail{{/status}}">
<details>
<summary>
{{description}}
</summary>
{{#total_pass}}
<table class="pass">
<thead>
<tr>
<th>Class</th>
<th>PredefinedType</th>
<th>Name</th>
<th>Description</th>
<th>GlobalId</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{{#passed_entities}}
<tr>
<td>{{class}}</td>
<td>{{predefined_type}}</td>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{global_id}}</td>
<td>{{tag}}</td>
</tr>
{{/passed_entities}}
{{#has_omitted_passes}}
<tr>
<td colspan="7"> ... {{total_omitted_passes}} more passing elements not shown out of {{total_passed_entities}} total ...</td>
</tr>
{{/has_omitted_passes}}
</tbody>
</table>
{{/total_pass}}
{{#total_fail}}
<table class="fail">
<thead>
<tr>
<th>Class</th>
<th>PredefinedType</th>
<th>Name</th>
<th>Description</th>
<th>Warning</th>
<th>GlobalId</th>
<th>Tag</th>
</tr>
</thead>
<tbody>
{{#failed_entities}}
<tr>
<td>{{class}}</td>
<td>{{predefined_type}}</td>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{reason}}</td>
<td>{{global_id}}</td>
<td>{{tag}}</td>
</tr>
{{/failed_entities}}
{{#has_omitted_failures}}
<tr>
<td colspan="7"> ... {{total_omitted_failures}} more failing elements not shown out of {{total_failed_entities}} total ...</td>
</tr>
{{/has_omitted_failures}}
</tbody>
</table>
{{/total_fail}}
</details>
</li>
{{/requirements}}
</ol>
</section> </section>
{{/specifications}} {{/specifications}}
<hr> <hr>