v20260104-14-reports-stats-total-runs-success-rate-fix #28

Merged
ivooskamp merged 22 commits from v20260104-14-reports-stats-total-runs-success-rate-fix into main 2026-01-13 10:59:39 +01:00
3 changed files with 116 additions and 7 deletions
Showing only changes of commit 843e01e1e6 - Show all commits

View File

@ -1 +1 @@
v20260104-03-reports-html-view-selection-fix
v20260104-04-reports-html-jobs-table-fix

View File

@ -1096,18 +1096,117 @@ def _export_html_response(report: ReportDefinition, report_id: int, view: str):
# - when html_content is not set, pick a sensible default based on scope + view
scope = (getattr(report, "customer_scope", None) or "all").strip().lower()
if not html_content:
if scope == "single" and (view or "summary").strip().lower() == "summary":
html_content = "customers"
else:
html_content = "jobs" if scope == "single" else "customers"
# For a single-customer report, the most useful summary is a job list.
# For all-customer reports, the summary defaults to performance by customer.
html_content = "jobs" if scope == "single" else "customers"
include_customers = html_content in ("customers", "both")
include_jobs = html_content in ("jobs", "both")
# Snapshot preview table can be requested either explicitly via view=snapshot
# or via the report config (include_jobs).
# Snapshot preview table can be requested explicitly via view=snapshot.
want_snapshot_table = (view or "summary").strip().lower() == "snapshot"
jobs_table_html = ""
if include_jobs and not want_snapshot_table:
# Job table (latest snapshot per job/object) for a single-customer report.
jobs_rows = []
with db.engine.connect() as conn:
rows = conn.execute(
text(
"""
SELECT DISTINCT ON (COALESCE(object_name,''), COALESCE(job_name,''), COALESCE(backup_software,''), COALESCE(backup_type,''))
COALESCE(object_name,'') AS object_name,
COALESCE(customer_name,'') AS customer_name,
COALESCE(job_name,'') AS job_name,
COALESCE(backup_software,'') AS backup_software,
COALESCE(backup_type,'') AS backup_type,
run_at,
COALESCE(status,'') AS status,
missed,
override_applied,
COALESCE(ticket_number,'') AS ticket_number,
COALESCE(remark,'') AS remark
FROM report_object_snapshots
WHERE report_id = :rid
ORDER BY
COALESCE(object_name,''),
COALESCE(job_name,''),
COALESCE(backup_software,''),
COALESCE(backup_type,''),
run_at DESC NULLS LAST
LIMIT 1000
"""
),
{"rid": report_id},
).fetchall()
for r in rows or []:
jobs_rows.append(
{
"object_name": r.object_name or "",
"customer_name": r.customer_name or "",
"job_name": r.job_name or "",
"backup_software": r.backup_software or "",
"backup_type": r.backup_type or "",
"run_at": r.run_at.isoformat() if r.run_at else "",
"status": r.status or "",
"missed": bool(r.missed),
"override_applied": bool(r.override_applied),
"ticket_number": r.ticket_number or "",
"remark": (r.remark or "").replace("\r", " ").replace("\n", " ").strip(),
}
)
job_row_html = []
for j in jobs_rows:
job_row_html.append(
"<tr>"
f"<td>{_esc(j['object_name'])}</td>"
f"<td>{_esc(j['job_name'])}</td>"
f"<td>{_esc(j['backup_software'])}</td>"
f"<td>{_esc(j['backup_type'])}</td>"
f"<td class='text-muted small'>{_esc(j['run_at'])}</td>"
f"<td>{_esc(j['status'])}</td>"
f"<td class='text-end'>{'1' if j['missed'] else '0'}</td>"
f"<td class='text-end'>{'1' if j['override_applied'] else '0'}</td>"
f"<td>{_esc(j['ticket_number'])}</td>"
f"<td>{_esc(j['remark'])}</td>"
"</tr>"
)
jobs_table_html = """<div class="row g-3 mb-3">
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-white">
<div class="fw-semibold">Jobs</div>
<div class="small-muted">Latest snapshot per job</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead>
<tr>
<th>Object</th>
<th>Job</th>
<th>Software</th>
<th>Type</th>
<th>Last run</th>
<th>Status</th>
<th class="text-end">Missed</th>
<th class="text-end">Override</th>
<th>Ticket</th>
<th>Remark</th>
</tr>
</thead>
<tbody>
""" + "\n".join(job_row_html) + """</tbody>
</table>
</div>
</div>
</div>
</div>
</div>"""
snapshot_table_html = ""
if want_snapshot_table:
snap_rows = []
@ -1343,6 +1442,8 @@ def _export_html_response(report: ReportDefinition, report_id: int, view: str):
{perf_table_html}
{jobs_table_html}
{snapshot_table_html}
</div>

View File

@ -209,6 +209,14 @@
- Prevented unintended fallback to Snapshot view when generating HTML reports.
- Improved default behavior for single-customer summary reports to ensure a meaningful summary is displayed.
---
## v20260104-04-reports-html-jobs-table-fix
- Fixed HTML report rendering where only charts were shown and no data rows appeared.
- Restored the jobs table below the charts in HTML reports for single-customer selections.
- Ensured the latest snapshot per job is displayed correctly in the HTML output.
================================================================================================================================================
## v0.1.15