feat: TSCM sweep metadata, cleared devices, and examiner ignore list

Four new features requested by TSCM users:

- Site/Location and Examiner name fields appear at the top of the
  sweep config; both are embedded in HTML and PDF/annex reports.
- Mark Cleared button on every live device item dims the entry with a
  CLEARED badge and excludes it from generated reports. Cleared state
  resets at the start of each new sweep. The report executive summary
  shows a count of cleared devices.
- Ignore List stores the examiner's own devices persistently in
  localStorage. Ignored devices are filtered from the live display and
  all report exports. An Ignore button appears on every device item;
  the sidebar Examiner Ignore List section shows current entries with
  per-item removal and a clear-all button.
- Site/examiner params forwarded to PDF and annex server routes so
  the text report header includes them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-07-05 14:37:08 +01:00
parent 5d87656909
commit 065b4778a9
5 changed files with 246 additions and 25 deletions
+15
View File
@@ -75,6 +75,7 @@ class TSCMReport:
# Location and context
location: str | None = None
examiner_name: str = ''
baseline_id: int | None = None
baseline_name: str | None = None
@@ -322,6 +323,10 @@ def generate_pdf_content(report: TSCMReport) -> str:
sections.append(f"Report ID: {report.report_id}")
sections.append(f"Generated: {report.generated_at.strftime('%Y-%m-%d %H:%M:%S')}")
sections.append(f"Sweep ID: {report.sweep_id}")
if report.location:
sections.append(f"Site / Location: {report.location}")
if report.examiner_name:
sections.append(f"Examiner: {report.examiner_name}")
sections.append("")
# Executive Summary
@@ -614,6 +619,10 @@ class TSCMReportBuilder:
self.report.location = location
return self
def set_examiner(self, examiner_name: str) -> TSCMReportBuilder:
self.report.examiner_name = examiner_name
return self
def set_baseline(self, baseline_id: int, baseline_name: str) -> TSCMReportBuilder:
self.report.baseline_id = baseline_id
self.report.baseline_name = baseline_name
@@ -848,6 +857,8 @@ def generate_report(
meeting_summaries: list[dict] | None = None,
correlations: list[dict] | None = None,
categories: list[str] | None = None,
site_name: str = '',
examiner_name: str = '',
) -> TSCMReport:
"""
Generate a complete TSCM report from sweep data.
@@ -869,6 +880,10 @@ def generate_report(
# Basic info
builder.set_sweep_type(sweep_data.get('sweep_type', 'standard'))
if site_name:
builder.set_location(site_name)
if examiner_name:
builder.set_examiner(examiner_name)
# Parse times
started_at = sweep_data.get('started_at')