IfcTester webapp now shows prohibited more clearly

- is_skipped is calculated upstream instead of reimplementing logic
 - the viewer now shows cardinality (not just in edit mode) to make it
clear what the requirements are
 - prohibited specs are not allowed to have any requirements, so that
section is hidden
 - failed prohibited specs now shows list of failed (applicable)
entities
This commit is contained in:
Dion Moult
2026-01-22 14:36:46 +11:00
parent 4c47eb573a
commit c2cc6f80be
2 changed files with 124 additions and 20 deletions
+2 -2
View File
@@ -81,6 +81,7 @@ class ResultsSpecification(TypedDict):
description: str
instructions: str
status: bool
is_skipped: bool
is_ifc_version: bool
total_applicable: int
total_applicable_pass: int
@@ -380,6 +381,7 @@ class Json(Reporter):
description=specification.description,
instructions=specification.instructions,
status=specification.status,
is_skipped=cardinality == "optional" and total_checks == 0,
is_ifc_version=specification.is_ifc_version,
total_applicable=total_applicable,
total_applicable_pass=total_applicable_pass,
@@ -474,8 +476,6 @@ class Html(Json):
def report(self) -> None:
super().report()
for spec in self.results["specifications"]:
if spec["cardinality"] == "optional" and spec["total_checks"] == 0:
spec["is_skipped"] = True
spec["is_prohibited"] = spec["cardinality"] == "prohibited"
spec["cardinality"] = spec["cardinality"].capitalize()
spec["has_requirements"] = bool(spec["requirements"])
@@ -50,13 +50,7 @@
function getSpecificationStatus(specIndex, auditData) {
const spec = auditData.specifications[specIndex];
if (!spec) return null;
// If no applicable elements and no checks, and it passed, it's actually skipped
if (spec.total_applicable === 0 && spec.total_checks === 0 && spec.status === true) {
return 'skipped';
}
return spec.status;
return spec.is_skipped ? 'skipped' : spec.status;
}
function getSpecificationStats(specIndex, auditData) {
@@ -79,11 +73,13 @@
const status = getSpecificationStatus(specIndex, auditData);
if (status === 'skipped') {
return "Skipped because no applicable entities were found and the cardinality is OPTIONAL or PROHIBITED";
return "Skipped because no applicable entities were found and the cardinality is OPTIONAL";
}
if (status === false) { // Failed
if (spec.total_applicable === 0) {
if (spec.cardinality === 'prohibited') {
return `Failed because ${spec.total_applicable} prohibited entities were found`;
} else if (spec.total_applicable === 0) {
return "Failed because no applicable entities were found but the cardinality is REQUIRED";
} else {
const failedChecks = spec.total_checks - spec.total_checks_pass;
@@ -240,19 +236,30 @@
{#if "@description" in spec}
<p class="spec-description">{spec["@description"]}</p>
{/if}
<div class="spec-stats">
{#if spec.applicability["@minOccurs"] === 1 && spec.applicability["@maxOccurs"] === 'unbounded'}
<span class="stat-item">Required</span>
{/if}
{#if spec.applicability["@minOccurs"] === 0 && spec.applicability["@maxOccurs"] === 'unbounded'}
<span class="stat-item">Optional</span>
{/if}
{#if spec.applicability["@minOccurs"] === 0 && spec.applicability["@maxOccurs"] === 0}
<span class="stat-item">Prohibited</span>
{/if}
{#if auditReport}
{@const stats = getSpecificationStats(index, auditReport.data)}
{@const status = getSpecificationStatus(index, auditReport.data)}
{#if stats && spec.applicability["@maxOccurs"] !== 0 && status !== 'skipped'}
<span class="stat-item">Checks: {stats.checksPassed}/{stats.checksTotal}</span>
<span class="stat-item">Requirements: {stats.requirementsPassed}/{stats.requirements}</span>
{/if}
{/if}
</div>
{#if auditReport}
{@const reason = getSpecificationReason(index, auditReport.data)}
{#if reason}
<p class="spec-reason">{reason}</p>
{/if}
{@const stats = getSpecificationStats(index, auditReport.data)}
{@const status = getSpecificationStatus(index, auditReport.data)}
{#if stats && status !== 'skipped'}
<div class="spec-stats">
<span class="stat-item">Checks: {stats.checksPassed}/{stats.checksTotal}</span>
<span class="stat-item">Requirements: {stats.requirementsPassed}/{stats.requirements}</span>
</div>
{/if}
{/if}
</div>
<div class="spec-actions">
@@ -292,9 +299,105 @@
{/if}
{/each}
</div>
{#if auditReport}
{@const status = getSpecificationStatus(index, auditReport.data)}
{#if ! status && spec.applicability["@maxOccurs"] == 0}
{@const specReport = auditReport.data.specifications[index]}
<div class="entity-tables">
{#if specReport.applicable_entities && specReport.applicable_entities.length > 0}
<div class="entity-table-section fail">
<h4>Failed Elements ({specReport.applicable_entities.length})</h4>
<div class="entity-table-container">
<Tooltip.Provider>
<table class="entity-table">
<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>
{#each specReport.applicable_entities.slice(0, 10) as entity}
<tr>
<td>{entity.class}</td>
<td>{entity.predefined_type || '-'}</td>
<td>
<Tooltip.Root>
<Tooltip.Trigger>
<div class="truncated-text">{entity.name || '-'}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{entity.name || '-'}</p>
</Tooltip.Content>
</Tooltip.Root>
</td>
<td>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>
<div class="truncated-text">{entity.description || '-'}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{entity.description || '-'}</p>
</Tooltip.Content>
</Tooltip.Root>
</td>
<td>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>
<div class="truncated-text">{entity.reason || '-'}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{entity.reason || '-'}</p>
</Tooltip.Content>
</Tooltip.Root>
</td>
<td>
<Tooltip.Root>
<Tooltip.Trigger>
<div class="truncated-text">{entity.global_id || '-'}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{entity.global_id || '-'}</p>
</Tooltip.Content>
</Tooltip.Root>
</td>
<td>
<Tooltip.Root>
<Tooltip.Trigger>
<div class="truncated-text">{entity.tag || '-'}</div>
</Tooltip.Trigger>
<Tooltip.Content>
<p>{entity.tag || '-'}</p>
</Tooltip.Content>
</Tooltip.Root>
</td>
</tr>
{/each}
{#if specReport.applicable_entities.length > 10}
<tr class="more-row">
<td colspan="7">... {specReport.applicable_entities.length - 10} more failing elements not shown ...</td>
</tr>
{/if}
</tbody>
</table>
</Tooltip.Provider>
</div>
</div>
{/if}
</div>
{/if}
{/if}
</div>
<!-- Requirements Section -->
{#if Array.isArray(spec.requirements) && spec.requirements.length > 0}
<div class="facet-section">
<h3>Requirements</h3>
@@ -497,6 +600,7 @@
{/each}
</div>
</div>
{/if}
</div>
{/if}
</div>
@@ -1128,4 +1232,4 @@
white-space: nowrap;
cursor: pointer;
}
</style>
</style>