from datetime import datetime from celery import shared_task from celery.utils.log import get_task_logger from django.core.mail import send_mail, EmailMultiAlternatives logger = get_task_logger(__name__) from account.models import CustomUser from django.conf import settings from django.template.loader import render_to_string @shared_task def send_notification_email_task(recipient_email, subject, template_path, context=None, attachments=None): send_email_with_context( recipients=recipient_email, subject=subject, template_path=template_path, context=context or {}, attachments=attachments, ) def send_email_with_context(recipients, subject, template_path=None, context=None, message: str | None = None, attachments=None): """ Send email using component-based template system. Uses base.html with header/footer components and includes the specified content template. """ if isinstance(recipients, str): recipients = [recipients] if not recipients: logger.warning("No recipients provided for email. Skipping sending email.") return "No recipients provided for email." ctx = dict(context or {}) # Add current_year to context if not present if "current_year" not in ctx: ctx["current_year"] = datetime.now().year # Add AppConfig data for footer if not already present if "company_name" not in ctx: try: from configuration.models import AppConfig config = AppConfig.objects.first() if config: ctx["company_name"] = config.company_name ctx["company_address"] = config.company_address ctx["company_ico"] = config.company_ico ctx["company_dic"] = config.company_dic ctx["contact_email"] = config.contact_email ctx["contact_phone"] = config.contact_phone except Exception: pass # Gracefully skip if AppConfig not available # Sanitize user if someone passes the model by mistake if "user" in ctx and not isinstance(ctx["user"], dict): try: ctx["user"] = _build_user_template_ctx(ctx["user"]) except Exception: ctx["user"] = {} # Render HTML using base template with content include html_message = None if template_path: ctx["content_template"] = template_path html_message = render_to_string("email/components/base.html", ctx) try: email = EmailMultiAlternatives( subject=subject, body=message or "Tento e-mail vyÅžaduje HTML podporu.", from_email=None, to=recipients if len(recipients) == 1 else [], bcc=recipients if len(recipients) > 1 else [], ) if html_message: email.attach_alternative(html_message, "text/html") if attachments: for filename, content, mimetype in attachments: email.attach(filename, content, mimetype) email.send(fail_silently=False) logger.info(f"Sent email to {recipients} with subject '{subject}'") if settings.EMAIL_BACKEND == 'django.core.mail.backends.console.EmailBackend': logger.debug(f"\nEMAIL HTML:\n{html_message}\nKONEC OBSAHU") return "Successfully sent email." except Exception as e: logger.error(f"E-mail se neodeslal: {e}") raise # Re-raise so Celery marks the task as FAILED and can retry def _build_user_template_ctx(user: CustomUser) -> dict: """ Return a plain dict for templates instead of passing the DB model. Provides aliases to avoid template errors (firstname vs first_name). Adds a backward-compatible key 'get_full_name' for templates using user.get_full_name. """ first_name = getattr(user, "first_name", "") or "" last_name = getattr(user, "last_name", "") or "" full_name = f"{first_name} {last_name}".strip() return { "id": user.pk, "email": getattr(user, "email", "") or "", "username": getattr(user, "username", "") or "", "first_name": first_name, "firstname": first_name, "last_name": last_name, "lastname": last_name, "full_name": full_name, "get_full_name": full_name, }