From 843e01e1e63cb0c3457e9fc4868ed5e4c7906912 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Sun, 4 Jan 2026 12:17:21 +0100 Subject: [PATCH] Auto-commit local changes before build (2026-01-04 12:17:21) --- .last-branch | 2 +- .../backend/app/main/routes_reporting_api.py | 113 +++++++++++++++++- docs/changelog.md | 8 ++ 3 files changed, 116 insertions(+), 7 deletions(-) diff --git a/.last-branch b/.last-branch index 7f51ecc..563c3b1 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260104-03-reports-html-view-selection-fix +v20260104-04-reports-html-jobs-table-fix diff --git a/containers/backupchecks/src/backend/app/main/routes_reporting_api.py b/containers/backupchecks/src/backend/app/main/routes_reporting_api.py index 8eca7bf..208fcc1 100644 --- a/containers/backupchecks/src/backend/app/main/routes_reporting_api.py +++ b/containers/backupchecks/src/backend/app/main/routes_reporting_api.py @@ -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( + "" + f"{_esc(j['object_name'])}" + f"{_esc(j['job_name'])}" + f"{_esc(j['backup_software'])}" + f"{_esc(j['backup_type'])}" + f"{_esc(j['run_at'])}" + f"{_esc(j['status'])}" + f"{'1' if j['missed'] else '0'}" + f"{'1' if j['override_applied'] else '0'}" + f"{_esc(j['ticket_number'])}" + f"{_esc(j['remark'])}" + "" + ) + + jobs_table_html = """
+
+
+
+
Jobs
+
Latest snapshot per job
+
+
+
+ + + + + + + + + + + + + + + + +""" + "\n".join(job_row_html) + """ +
ObjectJobSoftwareTypeLast runStatusMissedOverrideTicketRemark
+
+
+
+
+
""" + 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} diff --git a/docs/changelog.md b/docs/changelog.md index 6997087..3841936 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -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