mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Fix #3970. Show passing elements in IDS test report.
This commit is contained in:
@@ -81,6 +81,7 @@ class Facet:
|
||||
|
||||
def __init__(self, *parameters):
|
||||
self.status = None
|
||||
self.passed_entities: set[ifcopenshell.entity_instance] = set()
|
||||
self.failures: list[FacetFailure] = []
|
||||
for i, name in enumerate(self.parameters):
|
||||
setattr(self, name.replace("@", ""), parameters[i])
|
||||
|
||||
@@ -174,6 +174,7 @@ class Specification:
|
||||
self.instructions = instructions
|
||||
|
||||
self.applicable_entities: list[ifcopenshell.entity_instance] = []
|
||||
self.passed_entities: set[ifcopenshell.entity_instance] = set()
|
||||
self.failed_entities: set[ifcopenshell.entity_instance] = set()
|
||||
self.status = None
|
||||
|
||||
@@ -238,6 +239,7 @@ class Specification:
|
||||
|
||||
def reset_status(self):
|
||||
self.applicable_entities.clear()
|
||||
self.passed_entities: set[ifcopenshell.entity_instance] = set()
|
||||
self.failed_entities: set[ifcopenshell.entity_instance] = set()
|
||||
for facet in self.requirements:
|
||||
facet.status = None
|
||||
@@ -270,13 +272,19 @@ class Specification:
|
||||
result = facet(element)
|
||||
is_pass = bool(result)
|
||||
if self.maxOccurs != 0: # This is a required or optional specification
|
||||
if not is_pass:
|
||||
if is_pass:
|
||||
self.passed_entities.add(element)
|
||||
facet.passed_entities.add(element)
|
||||
else:
|
||||
self.failed_entities.add(element)
|
||||
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||
else: # This is a prohibited specification
|
||||
if is_pass:
|
||||
self.failed_entities.add(element)
|
||||
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||
else:
|
||||
self.passed_entities.add(element)
|
||||
facet.passed_entities.add(element)
|
||||
|
||||
self.status = True
|
||||
for facet in self.requirements:
|
||||
|
||||
@@ -96,7 +96,8 @@ class ResultsRequirement(TypedDict):
|
||||
value: str
|
||||
description: str
|
||||
status: bool
|
||||
failed_entities: list[ResultsFailedEntity]
|
||||
passed_entities: list[ResultsEntity]
|
||||
failed_entities: list[ResultsEntity]
|
||||
total_applicable: int
|
||||
total_pass: int
|
||||
total_fail: int
|
||||
@@ -104,8 +105,8 @@ class ResultsRequirement(TypedDict):
|
||||
|
||||
|
||||
# use different syntax because of the "class" key
|
||||
ResultsFailedEntity = TypedDict(
|
||||
"ResultsFailedEntity",
|
||||
ResultsEntity = TypedDict(
|
||||
"ResultsEntity",
|
||||
{
|
||||
"reason": str,
|
||||
"element": str,
|
||||
@@ -339,6 +340,7 @@ class Json(Reporter):
|
||||
value=value,
|
||||
description=requirement.to_string("requirement", specification, requirement),
|
||||
status=requirement.status,
|
||||
passed_entities=self.report_passed_entities(requirement),
|
||||
failed_entities=self.report_failed_entities(requirement),
|
||||
total_applicable=total_applicable,
|
||||
total_pass=total_pass,
|
||||
@@ -370,9 +372,27 @@ class Json(Reporter):
|
||||
requirements=requirements,
|
||||
)
|
||||
|
||||
def report_failed_entities(self, requirement: Facet) -> list[ResultsFailedEntity]:
|
||||
def report_passed_entities(self, requirement: Facet) -> list[ResultsEntity]:
|
||||
return [
|
||||
ResultsFailedEntity(
|
||||
ResultsEntity(
|
||||
{
|
||||
"element": e,
|
||||
"element_type": ifcopenshell.util.element.get_type(e),
|
||||
"class": e.is_a(),
|
||||
"predefined_type": ifcopenshell.util.element.get_predefined_type(e),
|
||||
"name": getattr(e, "Name", None),
|
||||
"description": getattr(e, "Description", None),
|
||||
"id": e.id(),
|
||||
"global_id": getattr(e, "GlobalId", None),
|
||||
"tag": getattr(e, "Tag", None),
|
||||
}
|
||||
)
|
||||
for e in requirement.passed_entities
|
||||
]
|
||||
|
||||
def report_failed_entities(self, requirement: Facet) -> list[ResultsEntity]:
|
||||
return [
|
||||
ResultsEntity(
|
||||
{
|
||||
"reason": f["reason"],
|
||||
"element": f["element"],
|
||||
@@ -413,11 +433,17 @@ class Html(Json):
|
||||
entity_limit = 100
|
||||
for spec in self.results["specifications"]:
|
||||
for requirement in spec["requirements"]:
|
||||
total = len(requirement["failed_entities"])
|
||||
total_passed_entities = len(requirement["passed_entities"])
|
||||
total_failed_entities = len(requirement["failed_entities"])
|
||||
requirement["passed_entities"] = requirement["passed_entities"][0:entity_limit]
|
||||
requirement["failed_entities"] = requirement["failed_entities"][0:entity_limit]
|
||||
requirement["has_omitted"] = total > entity_limit
|
||||
requirement["total_entities"] = total
|
||||
requirement["total_omitted"] = total - entity_limit
|
||||
requirement["total_failed_entities"] = total_failed_entities
|
||||
requirement["total_omitted_failures"] = total_failed_entities - entity_limit
|
||||
requirement["has_omitted_failures"] = total_failed_entities > entity_limit
|
||||
requirement["total_passed_entities"] = total_passed_entities
|
||||
requirement["total_omitted_passes"] = total_passed_entities - entity_limit
|
||||
requirement["has_omitted_passes"] = total_passed_entities > entity_limit
|
||||
|
||||
|
||||
def to_string(self) -> str:
|
||||
import pystache
|
||||
|
||||
@@ -59,10 +59,13 @@
|
||||
.percent { text-align: left; padding-top: 5px; padding-left: 5px; padding-bottom: 5px; color: white; border-radius: 5px; white-space: nowrap; }
|
||||
.pass { background-color: var(--green); }
|
||||
.fail { background-color: var(--red); }
|
||||
table { width: 100%; border-bottom: 2px solid var(--red); border-spacing: 0; border-radius: 5px; margin-top: 10px; }
|
||||
th, td { padding: 5px; }
|
||||
thead>tr { background-color: var(--red); font-weight: bold; color: #fff; }
|
||||
tbody tr { border-bottom: 1px solid var(--red); }
|
||||
table { width: 100%; border-spacing: 0; border-radius: 5px; margin-top: 10px; }
|
||||
th, td { padding: 5px; color: #000; }
|
||||
table.pass { border-bottom: 2px solid var(--green); }
|
||||
table.fail { border-bottom: 2px solid var(--red); }
|
||||
table thead>tr { font-weight: bold; color: #000; }
|
||||
table.pass tbody tr { border-bottom: 1px solid var(--green); }
|
||||
table.fail tbody tr { border-bottom: 1px solid var(--red); }
|
||||
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:hover { background-color: rgba(0, 0, 0, 0); }
|
||||
@@ -130,8 +133,39 @@
|
||||
<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>
|
||||
<table class="fail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
@@ -155,11 +189,11 @@
|
||||
<td>{{tag}}</td>
|
||||
</tr>
|
||||
{{/failed_entities}}
|
||||
{{#has_omitted}}
|
||||
{{#has_omitted_failures}}
|
||||
<tr>
|
||||
<td colspan="7"> ... {{total_omitted}} more elements not shown out of {{total_entities}} total ...</td>
|
||||
<td colspan="7"> ... {{total_omitted_failures}} more failing elements not shown out of {{total_failed_entities}} total ...</td>
|
||||
</tr>
|
||||
{{/has_omitted}}
|
||||
{{/has_omitted_failures}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{/total_fail}}
|
||||
|
||||
Reference in New Issue
Block a user