Corp/backend/common/emails.py

125 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# backend/common/emails.py
import logging
from typing import Iterable, Mapping, Optional
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
logger = logging.getLogger(__name__)
def send_templated_mail(
subject: str,
to: Iterable[str],
template_base: str,
context: Mapping,
from_email: Optional[str] = None,
attachments: Optional[list[tuple[str, bytes, str]]] = None,
) -> int:
"""
Отправляет письмо в HTML + plain текстах по шаблонам:
templates/<template_base>.html
templates/<template_base>.txt (опционально)
attachments: список троек (name, content_bytes, mimetype)
"""
to_list = list(to)
logger.info(
"Email delivery started",
extra={
"event": "email.delivery.started",
"template": template_base,
"recipient_count": len(to_list),
"attachment_count": len(attachments or []),
},
)
try:
html_body = render_to_string(f"{template_base}.html", context)
logger.debug(
"Email HTML template rendered",
extra={
"event": "email.template.rendered",
"template": template_base,
"body_bytes": len(html_body.encode()),
},
)
except Exception:
logger.exception(
"Email HTML template rendering failed",
extra={
"event": "email.template.failed",
"template": template_base,
"format": "html",
},
)
raise
# .txt файл опционален — если его нет, берём strip_tags от html
try:
text_body = render_to_string(f"{template_base}.txt", context)
logger.debug(
"Email text template rendered",
extra={
"event": "email.template.rendered",
"template": template_base,
"format": "text",
},
)
except Exception:
text_body = strip_tags(html_body)
logger.debug(
"Email text body generated from HTML",
extra={
"event": "email.template.fallback",
"template": template_base,
"format": "text",
},
)
email = EmailMultiAlternatives(
subject=subject,
body=text_body,
from_email=from_email or settings.DEFAULT_FROM_EMAIL,
to=to_list,
)
email.attach_alternative(html_body, "text/html")
if attachments:
for name, data, mimetype in attachments:
email.attach(name, data, mimetype)
logger.debug(
"Email attachment added",
extra={
"event": "email.attachment.added",
"attachment_bytes": len(data),
"mime_type": mimetype,
},
)
try:
result = email.send(fail_silently=False)
logger.info(
"Email delivery completed",
extra={
"event": "email.delivery.completed",
"template": template_base,
"recipient_count": len(to_list),
"sent_count": result,
},
)
return result
except Exception as error:
logger.exception(
"Email delivery failed",
extra={
"event": "email.delivery.failed",
"template": template_base,
"recipient_count": len(to_list),
"error_type": type(error).__name__,
},
)
raise