This commit is contained in:
Iliyan Angelov
2025-11-24 08:18:18 +02:00
parent 366f28677a
commit 136f75a859
133 changed files with 14977 additions and 3350 deletions

View File

@@ -1,11 +1,49 @@
from django.core.mail import EmailMultiAlternatives
from django.core.mail import EmailMultiAlternatives, get_connection
from django.template.loader import render_to_string
from django.conf import settings
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 CareerEmailService:
"""Service for handling career-related emails"""
@@ -24,26 +62,31 @@ class CareerEmailService:
'job_title': application.job.title,
'job_location': application.job.location,
'application_date': application.applied_date,
'logo_url': f'{settings.SITE_URL}/images/logo.png',
'site_url': settings.SITE_URL,
}
# Render email templates
text_content = render_to_string('career/application_confirmation.txt', context)
html_content = render_to_string('career/application_confirmation.html', context)
# Create email
# Create email (uses DEFAULT_FROM_EMAIL from settings)
email = EmailMultiAlternatives(
subject=subject,
body=text_content,
from_email=from_email,
from_email=from_email, # Uses settings.DEFAULT_FROM_EMAIL
to=to_email
)
email.attach_alternative(html_content, "text/html")
# Send email
email.send(fail_silently=False)
# Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
success = _send_email_with_retry(email)
logger.info(f"Confirmation email sent to {application.email}")
return True
if success:
logger.info(f"Confirmation email sent to {application.email}")
else:
logger.error(f"Failed to send confirmation email to {application.email} after retries")
return success
except Exception as e:
logger.error(f"Failed to send confirmation email: {str(e)}", exc_info=True)
@@ -78,18 +121,20 @@ class CareerEmailService:
'notice_period': application.notice_period,
'application_date': application.applied_date,
'resume_url': application.resume.url if application.resume else None,
'logo_url': f'{settings.SITE_URL}/images/logo.png',
'site_url': settings.SITE_URL,
}
# Render email templates
text_content = render_to_string('career/application_notification.txt', context)
html_content = render_to_string('career/application_notification.html', context)
# Create email
# Create email (uses DEFAULT_FROM_EMAIL and COMPANY_EMAIL from settings)
email = EmailMultiAlternatives(
subject=subject,
body=text_content,
from_email=from_email,
to=to_email,
from_email=from_email, # Uses settings.DEFAULT_FROM_EMAIL
to=to_email, # Uses settings.COMPANY_EMAIL
reply_to=[application.email]
)
email.attach_alternative(html_content, "text/html")
@@ -98,11 +143,14 @@ class CareerEmailService:
if application.resume:
email.attach_file(application.resume.path)
# Send email
email.send(fail_silently=False)
# Send email with retry logic (uses EMAIL_BACKEND and credentials from settings)
success = _send_email_with_retry(email)
logger.info(f"Admin notification email sent for application from {application.email}")
return True
if success:
logger.info(f"Admin notification email sent for application from {application.email}")
else:
logger.error(f"Failed to send admin notification email for application from {application.email} after retries")
return success
except Exception as e:
logger.error(f"Failed to send admin notification email: {str(e)}", exc_info=True)