"""Render a sample Daily Brief email for client sign-off. Runs the production `_build_brief_html()` with realistic synthetic data covering all three section types: ambiguous routings, MTRs missing heat codes, and processing errors. Usage: python -m scripts.preview_brief [output_path] If output_path is omitted, writes to ./daily_brief_preview.html relative to the repo root. """ from __future__ import annotations import json import sys from pathlib import Path # Make repo root importable when run via `python scripts/preview_brief.py` sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from app.lib.utils.brief_html import build_brief_html # Synthetic items modeled on real `daily_brief` rows. Confidence scores are # JSON-encoded strings because that's how they're stored in SQLite. SAMPLE_ITEMS: list[dict] = [ # Two ambiguous routings — multiple doc types crossed the threshold. { "id": 1, "sender_email": "ap@westlake.com", "subject": "RCM - Westlake PO 4502953154 and quote", "attachment_filename": "Westlake_PO_4502953154.pdf", "reason": "ambiguous", "confidence_scores": json.dumps({ "po": 0.81, "invoice": 0.12, "mtr": 0.03, "quote": 0.74, }), }, { "id": 2, "sender_email": "billing@halliburton.com", "subject": "RCM - Halliburton invoice 759085", "attachment_filename": "Halliburton_invoice_759085.pdf", "reason": "ambiguous", "confidence_scores": json.dumps({ "po": 0.71, "invoice": 0.78, "mtr": 0.01, "quote": 0.05, }), }, # Two MTRs where heat code couldn't be extracted. { "id": 3, "sender_email": "qa@woi-houston.com", "subject": "RCM - MTR for WOI order W000009810", "attachment_filename": "MTR W000009810.pdf", "reason": "mtr_no_heat_code", "confidence_scores": json.dumps({ "po": 0.04, "invoice": 0.02, "mtr": 0.82, "quote": 0.00, }), }, { "id": 4, "sender_email": "certs@flotite.com", "subject": "RCM - Material Test Report S3000CSPGGL015MP", "attachment_filename": "0.5_Full Port_Part No S3000CSPGGL015MP.pdf", "reason": "mtr_no_heat_code", "confidence_scores": json.dumps({ "po": 0.03, "invoice": 0.01, "mtr": 0.79, "quote": 0.00, }), }, # Two unmatched sender domains. { "id": 5, "sender_email": "purchasing@newvendor-inc.com", "subject": "RCM - PO from a vendor we haven't seen before", "attachment_filename": "NewVendor_PO_2026-04-22.pdf", "reason": "client_unmatched", "confidence_scores": json.dumps({ "po": 0.86, "invoice": 0.08, "mtr": 0.02, "quote": 0.04, }), }, { "id": 6, "sender_email": "billing@small-shop.com", "subject": "RCM - Invoice March 2026", "attachment_filename": "Invoice_March_2026.pdf", "reason": "client_unmatched", "confidence_scores": json.dumps({ "po": 0.11, "invoice": 0.84, "mtr": 0.01, "quote": 0.03, }), }, # One processing error. { "id": 7, "sender_email": "noreply@example-vendor.com", "subject": "RCM - corrupted attachment", "attachment_filename": "scan_2026-05-10_corrupted.pdf", "reason": "error", "error_message": ( "PDFSyntaxError: No /Root object! - Is this really a PDF? " "(stack trace in /logs/processor.log.2026-05-10)" ), "confidence_scores": None, }, ] def main() -> None: out = Path(sys.argv[1]) if len(sys.argv) > 1 else ( Path(__file__).resolve().parent.parent / "daily_brief_preview.html" ) html = build_brief_html(SAMPLE_ITEMS) out.write_text(html, encoding="utf-8") print(f"Wrote {len(html)} bytes to {out}") print("Open the file in a browser to preview.") if __name__ == "__main__": main()