"""Email templates for job notifications.""" from __future__ import annotations from datetime import datetime, UTC from typing import Iterable, Mapping, Dict, Any DEFAULT_DATETIME_FORMAT = "%Y-%m-%d %H:%M" DEFAULT_JOB_ALERT_SUBJECT = "{count_label}{scope}" DEFAULT_JOB_ALERT_BODY = ( "Hi,\n\n{intro_line}{jobs_section}\n\nGenerated at {timestamp} UTC.\n" "You are receiving this message because job alerts are enabled.\n" ) class _SafeDict(dict): def __missing__(self, key: str) -> str: return "" def _format_template(template: str, context: Dict[str, Any]) -> str: safe_context = _SafeDict( {k: ("\n".join(str(v) for v in context[k]) if isinstance( context[k], list) else context[k]) for k in context} ) return template.format_map(safe_context) def render_job_alert_email( jobs: Iterable[Mapping[str, object]], *, region: str | None = None, keyword: str | None = None, generated_at: datetime | None = None, template_override: Mapping[str, str] | None = None, ) -> dict[str, Any]: """Render the subject/body for a job alert email. Returns a dict with subject/body strings and the context used to render them. """ job_list = list(jobs) generated_at = generated_at or datetime.now(UTC) timestamp = generated_at.strftime(DEFAULT_DATETIME_FORMAT) scope_parts = [] if region: scope_parts.append(f"region: {region}") if keyword: scope_parts.append(f"keyword: {keyword}") scope = " (" + ", ".join(scope_parts) + ")" if scope_parts else "" job_lines: list[str] = [] for index, job in enumerate(job_list, start=1): title = str(job.get("title", "Untitled")) company = str(job.get("company", "Unknown company")) location = str(job.get("location", "N/A")) url = str(job.get("url", "")) line = f"{index}. {title} — {company} ({location})" job_lines.append(line) if url: job_lines.append(f" {url}") if job_lines: jobs_section = "\n" + "\n".join(job_lines) else: jobs_section = "\nNo jobs matched this alert." jobs_message = jobs_section.strip() context: Dict[str, Any] = { "count": len(job_list), "count_label": "No new jobs" if not job_list else f"{len(job_list)} new jobs", "scope": scope, "region": region or "", "keyword": keyword or "", "timestamp": timestamp, "generated_at": generated_at, "intro_line": "Here are the latest jobs discovered by the scraper:", "jobs_message": jobs_message, "jobs_section": jobs_section, "jobs_lines": job_lines, "has_jobs": bool(job_list), } template = template_override if template is None: try: from web.db import get_email_template_by_slug template = get_email_template_by_slug("job-alert") except Exception: template = None template_subject = (template or {}).get( "subject") or DEFAULT_JOB_ALERT_SUBJECT template_body = (template or {}).get("body") or DEFAULT_JOB_ALERT_BODY subject = _format_template(template_subject, context) body = _format_template(template_body, context) result = { "subject": subject, "body": body, "context": context, "template_slug": (template or {}).get("slug", "job-alert"), } return result