""" Email Service for Support Tickets Handles sending email notifications for ticket creation and updates """ from django.core.mail import EmailMultiAlternatives, get_connection from django.template.loader import render_to_string from django.conf import settings from django.utils.html import strip_tags import logging import time logger = logging.getLogger(__name__) def _send_email_with_retry(email_message, max_retries: int = 3, delay: float = 1.0) -> bool: """ Send email with retry logic for production reliability. Uses Django settings from .env file. Args: email_message: EmailMultiAlternatives instance max_retries: Maximum number of retry attempts delay: Delay between retries in seconds Returns: bool: True if email was sent successfully, False otherwise """ for attempt in range(max_retries + 1): try: # Test connection before sending (uses EMAIL_BACKEND from settings) connection = get_connection() connection.open() connection.close() # Send the email (uses EMAIL_BACKEND and credentials from settings) email_message.send() return True except Exception as e: logger.warning(f"Email send attempt {attempt + 1} failed: {str(e)}") if attempt < max_retries: time.sleep(delay * (2 ** attempt)) # Exponential backoff continue else: logger.error(f"Failed to send email after {max_retries + 1} attempts: {str(e)}") return False return False class SupportEmailService: """Service for sending support ticket related emails""" @staticmethod def send_ticket_confirmation_to_user(ticket): """ Send ticket confirmation email to the user who created the ticket Args: ticket: SupportTicket instance """ try: subject = f'Ticket Created: {ticket.ticket_number}' # Context for email template context = { 'ticket': ticket, 'ticket_number': ticket.ticket_number, 'user_name': ticket.user_name, 'title': ticket.title, 'description': ticket.description, 'ticket_type': ticket.get_ticket_type_display(), 'category': ticket.category.name if ticket.category else 'General', 'priority': ticket.priority.name if ticket.priority else 'Medium', 'status': ticket.status.name if ticket.status else 'Open', 'created_at': ticket.created_at.strftime('%B %d, %Y at %I:%M %p'), 'support_url': f'{settings.SITE_URL}/support-center', 'logo_url': f'{settings.SITE_URL}/images/logo.png', 'site_url': settings.SITE_URL, } # Render HTML email html_message = render_to_string( 'support/ticket_confirmation_user.html', context ) # Create plain text version text_message = render_to_string( 'support/ticket_confirmation_user.txt', context ) # Create email (uses DEFAULT_FROM_EMAIL from settings) email = EmailMultiAlternatives( subject=subject, body=text_message, from_email=settings.DEFAULT_FROM_EMAIL, # From .env file to=[ticket.user_email], ) # Attach HTML version email.attach_alternative(html_message, "text/html") # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings) success = _send_email_with_retry(email) if success: logger.info(f'Ticket confirmation email sent to user: {ticket.user_email} for ticket {ticket.ticket_number}') else: logger.error(f'Failed to send ticket confirmation email to user: {ticket.user_email} for ticket {ticket.ticket_number} after retries') return success except Exception as e: logger.error(f'Failed to send ticket confirmation email to user: {str(e)}') return False @staticmethod def send_ticket_notification_to_company(ticket): """ Send ticket notification email to company/support team Args: ticket: SupportTicket instance """ try: subject = f'New Support Ticket: {ticket.ticket_number} - {ticket.title}' # Get company email from settings company_email = getattr(settings, 'SUPPORT_EMAIL', settings.DEFAULT_FROM_EMAIL) # Context for email template context = { 'ticket': ticket, 'ticket_number': ticket.ticket_number, 'user_name': ticket.user_name, 'user_email': ticket.user_email, 'user_phone': ticket.user_phone, 'company': ticket.company, 'title': ticket.title, 'description': ticket.description, 'ticket_type': ticket.get_ticket_type_display(), 'category': ticket.category.name if ticket.category else 'General', 'priority': ticket.priority.name if ticket.priority else 'Medium', 'status': ticket.status.name if ticket.status else 'Open', 'created_at': ticket.created_at.strftime('%B %d, %Y at %I:%M %p'), 'admin_url': f'{settings.SITE_URL}/admin/support/supportticket/{ticket.id}/change/', 'logo_url': f'{settings.SITE_URL}/images/logo.png', 'site_url': settings.SITE_URL, } # Render HTML email html_message = render_to_string( 'support/ticket_notification_company.html', context ) # Create plain text version text_message = render_to_string( 'support/ticket_notification_company.txt', context ) # Create email (uses DEFAULT_FROM_EMAIL and SUPPORT_EMAIL from settings) email = EmailMultiAlternatives( subject=subject, body=text_message, from_email=settings.DEFAULT_FROM_EMAIL, # From .env file to=[company_email], # Uses settings.SUPPORT_EMAIL or settings.DEFAULT_FROM_EMAIL reply_to=[ticket.user_email], ) # Attach HTML version email.attach_alternative(html_message, "text/html") # Send email with retry logic (uses EMAIL_BACKEND and credentials from settings) success = _send_email_with_retry(email) if success: logger.info(f'Ticket notification email sent to company: {company_email} for ticket {ticket.ticket_number}') else: logger.error(f'Failed to send ticket notification email to company: {company_email} for ticket {ticket.ticket_number} after retries') return success except Exception as e: logger.error(f'Failed to send ticket notification email to company: {str(e)}') return False @staticmethod def send_ticket_created_emails(ticket): """ Send both user confirmation and company notification emails Args: ticket: SupportTicket instance Returns: dict with status of both emails """ user_email_sent = SupportEmailService.send_ticket_confirmation_to_user(ticket) company_email_sent = SupportEmailService.send_ticket_notification_to_company(ticket) return { 'user_email_sent': user_email_sent, 'company_email_sent': company_email_sent, }