mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-27 23:35:01 +00:00
Email the requester the export link, best-effort 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e825b58c13
commit
5ee8b2df50
1 changed files with 51 additions and 0 deletions
|
|
@ -96,9 +96,60 @@ class ScenarioExportOrchestrator:
|
|||
self._exports.put_object(key, data)
|
||||
url = self._exports.generate_presigned_url(key, self._url_ttl_seconds)
|
||||
|
||||
subject, body, html_body = self._compose_email(
|
||||
url=url, sheets=len(named_sheets), rows=row_count
|
||||
)
|
||||
try:
|
||||
self._email.send(
|
||||
to=recipient_email, subject=subject, body=body, html_body=html_body
|
||||
)
|
||||
logger.info("scenario_export: emailed %s", recipient_email)
|
||||
except Exception:
|
||||
# Best-effort delivery (ADR-0065): the link is also written to
|
||||
# sub_task.outputs, so an email/transport failure must not lose an
|
||||
# export that was already built and uploaded.
|
||||
logger.warning(
|
||||
"scenario_export: email delivery to %s failed; the link is still "
|
||||
"recorded on sub_task.outputs",
|
||||
recipient_email,
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
return ScenarioExportResult(
|
||||
presigned_url=url,
|
||||
export_s3_key=key,
|
||||
sheet_count=len(named_sheets),
|
||||
row_count=row_count,
|
||||
)
|
||||
|
||||
def _compose_email(
|
||||
self, *, url: str, sheets: int, rows: int
|
||||
) -> tuple[str, str, str]:
|
||||
"""The delivery email (ADR-0059/0065): a subject, a plain-text body
|
||||
carrying the raw link, and an HTML alternative with a clean download
|
||||
button so the long presigned URL isn't the visible content."""
|
||||
minutes = self._url_ttl_seconds // 60
|
||||
sheet_noun = "scenario" if sheets == 1 else "scenarios"
|
||||
subject = f"Your scenario export is ready ({sheets} {sheet_noun})"
|
||||
body = (
|
||||
"Your scenario export is ready.\n\n"
|
||||
f"It covers {sheets} {sheet_noun} across {rows} property row(s).\n\n"
|
||||
f"Download it here (link valid for {minutes} minutes):\n{url}\n"
|
||||
)
|
||||
safe_url = html.escape(url, quote=True)
|
||||
html_body = (
|
||||
'<div style="font-family:Arial,Helvetica,sans-serif;font-size:15px;'
|
||||
'color:#1a1a1a;line-height:1.5;">'
|
||||
"<p>Your scenario export is ready.</p>"
|
||||
f"<p>It covers <strong>{sheets}</strong> {sheet_noun} across "
|
||||
f"<strong>{rows}</strong> property row(s).</p>"
|
||||
'<p style="margin:24px 0;">'
|
||||
f'<a href="{safe_url}" style="display:inline-block;padding:12px 22px;'
|
||||
"background:#6C6CFF;color:#ffffff;text-decoration:none;border-radius:6px;"
|
||||
'font-weight:bold;">Download export</a></p>'
|
||||
f'<p style="color:#666;font-size:13px;">This link expires in {minutes} '
|
||||
"minutes. If the button doesn't work, paste this URL into your browser:"
|
||||
f'<br><span style="word-break:break-all;">{safe_url}</span></p>'
|
||||
"</div>"
|
||||
)
|
||||
return subject, body, html_body
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue