Spaces:
Sleeping
Sleeping
File size: 4,003 Bytes
d618a6e c55abce d618a6e c55abce d618a6e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | """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()
|