mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 08:45: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):
|
def __init__(self, *parameters):
|
||||||
self.status = None
|
self.status = None
|
||||||
|
self.passed_entities: set[ifcopenshell.entity_instance] = set()
|
||||||
self.failures: list[FacetFailure] = []
|
self.failures: list[FacetFailure] = []
|
||||||
for i, name in enumerate(self.parameters):
|
for i, name in enumerate(self.parameters):
|
||||||
setattr(self, name.replace("@", ""), parameters[i])
|
setattr(self, name.replace("@", ""), parameters[i])
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ class Specification:
|
|||||||
self.instructions = instructions
|
self.instructions = instructions
|
||||||
|
|
||||||
self.applicable_entities: list[ifcopenshell.entity_instance] = []
|
self.applicable_entities: list[ifcopenshell.entity_instance] = []
|
||||||
|
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
|
||||||
|
|
||||||
@@ -238,6 +239,7 @@ class Specification:
|
|||||||
|
|
||||||
def reset_status(self):
|
def reset_status(self):
|
||||||
self.applicable_entities.clear()
|
self.applicable_entities.clear()
|
||||||
|
self.passed_entities: set[ifcopenshell.entity_instance] = set()
|
||||||
self.failed_entities: set[ifcopenshell.entity_instance] = set()
|
self.failed_entities: set[ifcopenshell.entity_instance] = set()
|
||||||
for facet in self.requirements:
|
for facet in self.requirements:
|
||||||
facet.status = None
|
facet.status = None
|
||||||
@@ -270,13 +272,19 @@ class Specification:
|
|||||||
result = facet(element)
|
result = facet(element)
|
||||||
is_pass = bool(result)
|
is_pass = bool(result)
|
||||||
if self.maxOccurs != 0: # This is a required or optional specification
|
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)
|
self.failed_entities.add(element)
|
||||||
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||||
else: # This is a prohibited specification
|
else: # This is a prohibited specification
|
||||||
if is_pass:
|
if is_pass:
|
||||||
self.failed_entities.add(element)
|
self.failed_entities.add(element)
|
||||||
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
facet.failures.append(FacetFailure(element=element, reason=str(result)))
|
||||||
|
else:
|
||||||
|
self.passed_entities.add(element)
|
||||||
|
facet.passed_entities.add(element)
|
||||||
|
|
||||||
self.status = True
|
self.status = True
|
||||||
for facet in self.requirements:
|
for facet in self.requirements:
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ class ResultsRequirement(TypedDict):
|
|||||||
value: str
|
value: str
|
||||||
description: str
|
description: str
|
||||||
status: bool
|
status: bool
|
||||||
failed_entities: list[ResultsFailedEntity]
|
passed_entities: list[ResultsEntity]
|
||||||
|
failed_entities: list[ResultsEntity]
|
||||||
total_applicable: int
|
total_applicable: int
|
||||||
total_pass: int
|
total_pass: int
|
||||||
total_fail: int
|
total_fail: int
|
||||||
@@ -104,8 +105,8 @@ class ResultsRequirement(TypedDict):
|
|||||||
|
|
||||||
|
|
||||||
# use different syntax because of the "class" key
|
# use different syntax because of the "class" key
|
||||||
ResultsFailedEntity = TypedDict(
|
ResultsEntity = TypedDict(
|
||||||
"ResultsFailedEntity",
|
"ResultsEntity",
|
||||||
{
|
{
|
||||||
"reason": str,
|
"reason": str,
|
||||||
"element": str,
|
"element": str,
|
||||||
@@ -339,6 +340,7 @@ class Json(Reporter):
|
|||||||
value=value,
|
value=value,
|
||||||
description=requirement.to_string("requirement", specification, requirement),
|
description=requirement.to_string("requirement", specification, requirement),
|
||||||
status=requirement.status,
|
status=requirement.status,
|
||||||
|
passed_entities=self.report_passed_entities(requirement),
|
||||||
failed_entities=self.report_failed_entities(requirement),
|
failed_entities=self.report_failed_entities(requirement),
|
||||||
total_applicable=total_applicable,
|
total_applicable=total_applicable,
|
||||||
total_pass=total_pass,
|
total_pass=total_pass,
|
||||||
@@ -370,9 +372,27 @@ class Json(Reporter):
|
|||||||
requirements=requirements,
|
requirements=requirements,
|
||||||
)
|
)
|
||||||
|
|
||||||
def report_failed_entities(self, requirement: Facet) -> list[ResultsFailedEntity]:
|
def report_passed_entities(self, requirement: Facet) -> list[ResultsEntity]:
|
||||||
return [
|
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"],
|
"reason": f["reason"],
|
||||||
"element": f["element"],
|
"element": f["element"],
|
||||||
@@ -413,11 +433,17 @@ class Html(Json):
|
|||||||
entity_limit = 100
|
entity_limit = 100
|
||||||
for spec in self.results["specifications"]:
|
for spec in self.results["specifications"]:
|
||||||
for requirement in spec["requirements"]:
|
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["failed_entities"] = requirement["failed_entities"][0:entity_limit]
|
||||||
requirement["has_omitted"] = total > entity_limit
|
requirement["total_failed_entities"] = total_failed_entities
|
||||||
requirement["total_entities"] = total
|
requirement["total_omitted_failures"] = total_failed_entities - entity_limit
|
||||||
requirement["total_omitted"] = total - 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:
|
def to_string(self) -> str:
|
||||||
import pystache
|
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; }
|
.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); }
|
.pass { background-color: var(--green); }
|
||||||
.fail { background-color: var(--red); }
|
.fail { background-color: var(--red); }
|
||||||
table { width: 100%; border-bottom: 2px solid var(--red); border-spacing: 0; border-radius: 5px; margin-top: 10px; }
|
table { width: 100%; border-spacing: 0; border-radius: 5px; margin-top: 10px; }
|
||||||
th, td { padding: 5px; }
|
th, td { padding: 5px; color: #000; }
|
||||||
thead>tr { background-color: var(--red); font-weight: bold; color: #fff; }
|
table.pass { border-bottom: 2px solid var(--green); }
|
||||||
tbody tr { border-bottom: 1px solid var(--red); }
|
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(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); }
|
||||||
@@ -130,8 +133,39 @@
|
|||||||
<summary>
|
<summary>
|
||||||
{{description}}
|
{{description}}
|
||||||
</summary>
|
</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}}
|
{{#total_fail}}
|
||||||
<table>
|
<table class="fail">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Class</th>
|
<th>Class</th>
|
||||||
@@ -155,11 +189,11 @@
|
|||||||
<td>{{tag}}</td>
|
<td>{{tag}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{/failed_entities}}
|
{{/failed_entities}}
|
||||||
{{#has_omitted}}
|
{{#has_omitted_failures}}
|
||||||
<tr>
|
<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>
|
</tr>
|
||||||
{{/has_omitted}}
|
{{/has_omitted_failures}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{{/total_fail}}
|
{{/total_fail}}
|
||||||
|
|||||||
Reference in New Issue
Block a user