"""Daily/weekly digest email rendering. Pure prose → HTML/text rendering. SMTP transport stays in ``email_service.send_email``; this module only assembles the message body, subject, and a text-only fallback for clients without HTML rendering. Split from email_service.py during the Tier 2 cleanup pass — the SMTP/OTP/welcome surface and the digest renderer changed at very different cadences and made the file noisy to navigate. """ from __future__ import annotations import html as _html_lib import re as _re from app import branding _DIGEST_HTML_TEMPLATE = """\ {brand} — {label}
▰ {brand_upper} · {label_upper}
 
{content_html}
 
 
""" def _strip_html_to_text(html_body: str) -> str: """Best-effort HTML → plain text for the multipart fallback. We don't need perfection — just readable prose for clients that won't render HTML.""" text = _re.sub(r"(?i)<(/(p|h[1-6]|li|ul|ol)|br\s*/?)>", "\n", html_body) text = _re.sub(r"<[^>]+>", "", text) text = _html_lib.unescape(text) text = _re.sub(r"\n{3,}", "\n\n", text) return text.strip() def render_digest_email( *, kind: str, date_str: str, content_html: str, unsubscribe_url: str, settings_url: str, ) -> tuple[str, str, str]: """Returns (subject, text_body, html_body) for a digest email. `kind` is "daily" or "weekly". Anything else raises ValueError.""" if kind == "daily": label = "Daily" subject = f"{branding.BRAND_NAME} · Daily — {date_str}" elif kind == "weekly": label = "Weekly recap" subject = f"{branding.BRAND_NAME} · Weekly recap — {date_str}" else: raise ValueError(f"unknown digest kind: {kind!r}") html_body = _DIGEST_HTML_TEMPLATE.format( brand=branding.BRAND_NAME, brand_upper=branding.BRAND_NAME.upper(), label=label, label_upper=label.upper(), FONT_MONO=branding.FONT_MONO, content_html=content_html, unsubscribe_url=unsubscribe_url, settings_url=settings_url, **{f"L_{k.replace('-', '_')}": v for k, v in branding.LIGHT.items()}, **{f"D_{k.replace('-', '_')}": v for k, v in branding.DARK.items()}, ) text_lines = [ f"{branding.BRAND_NAME} — {label}", date_str, "", _strip_html_to_text(content_html), "", f"Unsubscribe: {unsubscribe_url}", f"Manage preferences: {settings_url}", ] text_body = "\n".join(text_lines) return subject, text_body, html_body