Spaces:
Sleeping
Sleeping
| """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() | |