Segment Breakdown
| Time | Audio (Whisper) | Subtitle | Score | Status |
|---|
No segments match this filter
""" Generates a standalone, beautiful HTML report of the mismatch analysis. The HTML embeds Tailwind CSS via CDN and requires no server to view. """ from __future__ import annotations import json import math from datetime import datetime from pathlib import Path from utils.text_utils import format_timestamp _STATUS_CONFIG = { "OK": {"badge": "bg-emerald-500/20 text-emerald-300 border-emerald-500/30", "row": "hover:bg-slate-700/30", "dot": "#10b981"}, "MARGINAL": {"badge": "bg-amber-500/20 text-amber-300 border-amber-500/30", "row": "hover:bg-amber-900/10 bg-amber-900/5", "dot": "#f59e0b"}, "REVIEW": {"badge": "bg-red-500/20 text-red-300 border-red-500/30", "row": "hover:bg-red-900/10 bg-red-900/5", "dot": "#ef4444"}, "MISSING": {"badge": "bg-slate-500/20 text-slate-400 border-slate-500/30", "row": "hover:bg-slate-700/30 bg-slate-800/50", "dot": "#6b7280"}, } class ReportGenerator: def generate( self, results: list[dict], video_url: str, output_path: str, ) -> dict: """ Generate an HTML report and write it to `output_path`. Also returns structured data (for JSON API response). """ stats = self._compute_stats(results) html = self._render_html(results, stats, video_url) Path(output_path).write_text(html, encoding="utf-8") return { "stats": stats, "segments": results, "video_url": video_url, "generated_at": datetime.now().isoformat(), "report_path": output_path, } # ------------------------------------------------------------------ # Stats # ------------------------------------------------------------------ def _compute_stats(self, results: list[dict]) -> dict: total = len(results) ok = sum(1 for r in results if r["status"] == "OK") marginal = sum(1 for r in results if r["status"] == "MARGINAL") review = sum(1 for r in results if r["status"] == "REVIEW") missing = sum(1 for r in results if r["status"] == "MISSING") flagged = marginal + review + missing avg_score = ( sum(r["score"] for r in results if r["has_subtitle"]) / max(1, sum(1 for r in results if r["has_subtitle"])) ) return { "total": total, "ok": ok, "marginal": marginal, "review": review, "missing": missing, "flagged": flagged, "avg_score": round(avg_score, 4), "match_rate": round(ok / max(1, total) * 100, 1), } # ------------------------------------------------------------------ # HTML rendering # ------------------------------------------------------------------ def _render_html(self, results: list[dict], stats: dict, video_url: str) -> str: rows_html = "".join(self._render_row(r) for r in results) results_json = json.dumps(results, ensure_ascii=False) return f"""
| Time | Audio (Whisper) | Subtitle | Score | Status |
|---|
No segments match this filter