from collections.abc import Sequence from typing import Any, ClassVar, TypeAlias from django.core import mail from django.template import loader import mdformat from markdownify import markdownify Context: TypeAlias = dict[str, Any] class EmailBase: subject: ClassVar[str] from_email: ClassVar[str | None] = None reply_to: ClassVar[Sequence[str] | None] = None context: Context def __init__(self, context: Context) -> None: self.context = context def render_body(self) -> str: raise NotImplementedError @classmethod def render( cls, context: Context, to: list[str] | None = None, cc: list[str] | None = None, bcc: list[str] | None = None, ) -> mail.EmailMessage: self = cls(context) body = self.render_body() return mail.EmailMessage( self.subject, body, from_email=self.from_email, reply_to=self.reply_to, to=to, cc=cc, bcc=bcc, ) class TemplatedMultipartEmail(EmailBase): template: ClassVar[str] def render_html_body(self) -> str: template = loader.get_template(self.template) return template.render(self.context) def render_body(self, html_body: str) -> str: return mdformat.text(markdownify(html_body), extensions={"tables"}) @classmethod def render( cls, context: Context, to: list[str] | None = None, cc: list[str] | None = None, bcc: list[str] | None = None, ) -> mail.EmailMessage: self = cls(context) html_body = self.render_html_body() plain_body = self.render_body(html_body) return mail.EmailMultiAlternatives( self.subject, plain_body, from_email=self.from_email, reply_to=self.reply_to, to=to, cc=cc, bcc=bcc, alternatives=[(html_body, "text/html")], )