atr/templates/report-selected-path.html (257 lines of code) (raw):
{% extends "layouts/base.html" %}
{% block title %}
Report for {{ release.short_display_name }} {{ package.filename }} ~ ATR
{% endblock title %}
{% block description %}
Show the report for the {{ release.short_display_name }} {{ package.filename }} candidate draft file.
{% endblock description %}
{% block content %}
{% set phase = release.phase.value %}
<p class="d-flex justify-content-between align-items-center">
{% if phase == "release_candidate_draft" %}
<a href="{{ as_url(routes.root.index) }}" class="atr-back-link">← Back to Select a release</a>
{% else %}
<a href="{{ as_url(routes.vote.selected, project_name=release.project.name, version_name=release.version) }}"
class="atr-back-link">← Back to Vote on {{ release.project.short_display_name }} {{ release.version }}</a>
{% endif %}
<span>
{% if phase == "release_candidate_draft" %}
<strong class="atr-phase-one atr-phase-symbol">①</strong>
<span class="atr-phase-one atr-phase-label">COMPOSE</span>
<span class="atr-phase-arrow">→</span>
<span class="atr-phase-symbol-other">②</span>
{% else %}
<span class="atr-phase-symbol-other">①</span>
<span class="atr-phase-arrow">→</span>
<strong class="atr-phase-two atr-phase-symbol">②</strong>
<span class="atr-phase-two atr-phase-label">VOTE</span>
{% endif %}
<span class="atr-phase-arrow">→</span>
<span class="atr-phase-symbol-other">③</span>
</span>
</p>
<h1>
Report for <strong>{{ release.project.short_display_name }}</strong> <em>{{ release.version }}</em>:
</h1>
<div class="fs-3 bg-light p-3 border rounded mb-4">
<code>{{ package.filename }}</code>
</div>
<p>
<strong>Note:</strong> Refresh this page to see the latest status of the verification tasks.
</p>
<h2>Report results</h2>
{% if check_results %}
{# Define status_counts here so it's available globally in this block #}
{% set status_counts = {
"success": check_results|selectattr("status.value", "equalto", "success")|list|length,
"failure": check_results|selectattr("status.value", "equalto", "failure")|list|length,
"warning": check_results|selectattr("status.value", "equalto", "warning")|list|length,
"exception": check_results|selectattr("status.value", "equalto", "exception")|list|length
} %}
<div class="d-flex align-items-center p-3 mb-3 bg-light border rounded">
<span class="fw-bold me-3">Status summary:</span>
<div class="d-flex flex-wrap gap-3">
{% for status, count in status_counts.items() %}
{% if count > 0 %}
<div class="d-flex align-items-center gap-2 px-3 py-2 rounded fw-medium {% if status == "success" %}bg-success-subtle border border-success-subtle {% elif status == "failure" %}bg-danger-subtle border border-danger-subtle {% elif status == "warning" %}bg-warning-subtle border border-warning-subtle {% elif status == "exception" %}bg-danger-subtle border border-danger-subtle {% endif %}">
<span class="fs-5">{{ count }}</span>
<span>
{%- if status == "success" -%}
Passed
{%- elif status == "failure" -%}
Issues
{%- elif status == "warning" -%}
Warning
{%- elif status == "exception" -%}
Error
{%- else -%}
{{ status|title }}
{%- endif -%}
</span>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
<div class="d-flex gap-3 mb-3">
<button type="button" onclick="toggleAllDetails()" class="btn btn-secondary">Toggle all details</button>
{% if is_viewing_as_admin_fn(current_user.uid) and (release.phase.value == "release_candidate_draft") %}
<form method="post"
action="{{ as_url(routes.draft.fresh, project_name=release.project.name, version_name=release.version) }}"
class="mb-0">
<button type="submit" class="btn btn-primary">Restart all checks</button>
</form>
{% endif %}
</div>
<div class="mb-3">
{% if check_results %}
{% for check_result in check_results %}
<div class="border border-2 rounded p-3 mb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="fw-bold">{{ function_name_from_key(check_result.checker) }}</span>
<span class="badge rounded-pill {% if check_result.status.value == "success" %}bg-success {% elif check_result.status.value == "failure" %}bg-danger {% elif check_result.status.value == "warning" %}bg-warning {% elif check_result.status.value == "exception" %}bg-danger {% else %}bg-secondary{% endif %}">
{%- if check_result.status.value == "success" -%}
Passed
{%- elif check_result.status.value == "failure" -%}
{% if status_counts['failure'] == 1 %}
Issue
{% else %}
Issues
{% endif %}
{%- elif check_result.status.value == "warning" -%}
{% if status_counts['warning'] == 1 %}
Warning
{% else %}
Warnings
{% endif %}
{%- elif check_result.status.value == "exception" -%}
{% if status_counts['exception'] == 1 %}
Exception
{% else %}
Exceptions
{% endif %}
{%- else -%}
{{ check_result.status.value|title }}
{%- endif -%}
</span>
</div>
<div class="small">
<div>
Checked: {{ check_result.created.strftime("%Y-%m-%d %H:%M:%S UTC") if check_result.created else "Not checked" }}
</div>
{% if check_result.message %}<div class="mt-2">{{ check_result.message }}</div>{% endif %}
{% if (check_result.data is mapping) and (check_result.data.keys()|length > 0) %}
<details class="mt-2 p-2 bg-light rounded">
{% if check_result.status.value in ["failure", "exception"] %}
<summary class="atr-cursor-pointer user-select-none p-2">View details</summary>
{% elif check_result.status.value == "warning" %}
<summary class="atr-cursor-pointer user-select-none p-2">View details</summary>
{% else %}
<summary class="atr-cursor-pointer user-select-none p-2">View details</summary>
{% endif %}
{% if check_result.checker.endswith("rat.check_licenses") %}
<div class="d-flex gap-3 mb-2">
<span class="badge bg-success-subtle text-success-emphasis border border-success-subtle px-2 py-1">
<strong>{{ check_result.data.get('approved_licenses', 0) }}</strong> files with approved licenses
</span>
{% if check_result.data.get("unapproved_licenses", 0) > 0 %}
<span class="badge bg-danger-subtle text-danger-emphasis border border-danger-subtle px-2 py-1">
<strong>{{ check_result.data.get('unapproved_licenses', 0) }}</strong> files with unapproved licenses
</span>
{% endif %}
{% if check_result.data.get("unknown_licenses", 0) > 0 %}
<span class="badge bg-warning-subtle text-warning-emphasis border border-warning-subtle px-2 py-1">
<strong>{{ check_result.data.get('unknown_licenses', 0) }}</strong> files with unknown licenses
</span>
{% endif %}
</div>
{% endif %}
<table class="table table-bordered mt-2">
<tbody>
{% for key, value in check_result.data.items() %}
{% if key != "debug_info" %}
<tr>
<th class="bg-light fw-bold align-top">{{ key|replace('_', ' ') |title }}</th>
<td>
{% if value is boolean %}
{{ "Yes" if value else "No" }}
{% elif value is mapping %}
<table class="table table-sm mb-0">
{% for k, v in value.items() %}
<tr>
<th class="bg-light fw-bold">{{ k|replace('_', ' ') |title }}</th>
<td>{{ v }}</td>
</tr>
{% endfor %}
</table>
{% elif key == "unapproved_files" or key == "unknown_license_files" %}
{% if value|length > 0 %}
<table class="table table-sm table-bordered mb-0">
<thead>
<tr>
<th class="bg-light fw-bold">File</th>
<th class="bg-light fw-bold">License</th>
</tr>
</thead>
<tbody>
{% for file_info in value %}
<tr>
<td>{{ file_info.name }}</td>
<td>{{ file_info.license }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
None
{% endif %}
{% elif value is sequence and value is not string %}
{{ value|join(", ") }}
{% else %}
{{ value }}
{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
{% if check_result.data.get("debug_info") is mapping %}
{% for debug_key, debug_value in check_result.data.debug_info.items() %}
<tr>
<th class="bg-light fw-bold align-top">
<span class="text-muted">(Debug)</span> {{ debug_key|replace('_', ' ') |title }}
</th>
<td>
{% if debug_key == 'stderr' %}
<pre class="mb-0 small"><code class="small">{{ debug_value }}</code></pre>
{% elif debug_value is boolean %}
{{ "Yes" if debug_value else "No" }}
{% elif debug_value is none %}
<span class="text-muted">(None)</span>
{% else %}
{{ debug_value }}
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</details>
{% endif %}
</div>
</div>
{% endfor %}
{% else %}
<p>No verification tasks found for this file.</p>
{% endif %}
<h2>Metadata</h2>
<div class="p-3 mb-4 bg-light border rounded">
<p>
<strong>Filename:</strong> {{ package.filename }}
</p>
<p>
<strong>Size:</strong> {{ format_file_size(package.bytes_size) }}
</p>
<p class="mb-0">
<strong>Uploaded:</strong> {{ package.uploaded.strftime("%Y-%m-%d %H:%M:%S UTC") }}
</p>
</div>
</div>
{% endblock content %}
{% block javascripts %}
{{ super() }}
<script>
function toggleAllDetails() {
const details = document.querySelectorAll("details");
// Check if any are closed
const anyClosed = Array.from(details).some(detail => !detail.open);
// If any are closed, open all
// Otherwise, close all
details.forEach(detail => detail.open = anyClosed);
}
</script>
{% endblock javascripts %}
{% macro function_name_from_key(key) -%}
{{- key.removeprefix("atr.tasks.checks.").replace("_", " ").replace(".", " ") | title -}}
{%- endmacro %}