From 1c4bd243e6e885b43ba1905b3e3194946a091123 Mon Sep 17 00:00:00 2001 From: ayishanishana21 Date: Thu, 11 Jun 2026 01:03:32 +0530 Subject: [PATCH 1/3] added logging for emails --- cms/views.py | 73 ++++++++++++++++++++++++++++++++++++++++------- donate/views.py | 14 +++++++++ training/views.py | 63 ++++++++++++++++++++++++++++++++++------ 3 files changed, 131 insertions(+), 19 deletions(-) diff --git a/cms/views.py b/cms/views.py index 029b1faa..8745dad6 100644 --- a/cms/views.py +++ b/cms/views.py @@ -36,6 +36,13 @@ from django.core.cache import cache +import logging + +logger = logging.getLogger("mail_logs") + +cache = caches['default'] + + def dispatcher(request, permalink=''): if permalink == '': return HttpResponseRedirect('/') @@ -154,8 +161,22 @@ def send_registration_confirmation(user): #email.attach_alternative(message, "text/html") try: result = email.send(fail_silently=False) + logger.warning( + "Registration email sent | username=%s | user_id=%s | email=%s | subject=%s", + user.username, + user.id, + user.email, + subject, + ) return code except Exception as e: + logger.warning( + "Registration email failed | username=%s | user_id=%s | email=%s | error=%s", + user.username, + user.id, + user.email, + str(e), + ) return None def email_otp(user, password): @@ -196,7 +217,20 @@ def email_otp(user, password): #email.attach_alternative(message, "text/html") try: result = email.send(fail_silently=False) - except: + logger.warning( + "OTP email sent | username=%s | user_id=%s | email=%s", + user.username, + user.id, + user.email, + ) + except Exception as e: + logger.warning( + "OTP email failed | username=%s | user_id=%s | email=%s | error=%s", + user.username, + user.id, + user.email, + str(e), + ) pass @@ -419,15 +453,34 @@ def password_reset(request): to = to, bcc = [], cc = [], headers={'Reply-To': 'no-reply@spoken-tutorial.org', "Content-type":"text/html;charset=iso-8859-1"} ) - - result = email.send(fail_silently=False) - # redirect to next url if there or redirect to login page - # use for forum password rest form - redirectNext = request.GET.get('next', False) - if redirectNext: - return HttpResponseRedirect(redirectNext) - messages.success(request, "New password sent to your email "+user.email) - return HttpResponseRedirect('/accounts/change-password/') + try: + result = email.send(fail_silently=False) + logger.warning( + "Password reset email sent | user=%s | user_id=%s | email=%s | ip=%s | path=%s", + getattr(user, "username", None), + getattr(user, "id", None), + getattr(user, "email", None), + request.META.get("REMOTE_ADDR"), + request.path, + ) + # redirect to next url if there or redirect to login page + # use for forum password rest form + redirectNext = request.GET.get('next', False) + if redirectNext: + return HttpResponseRedirect(redirectNext) + messages.success(request, "New password sent to your email "+user.email) + return HttpResponseRedirect('/accounts/change-password/') + except Exception as e: + logger.warning( + "Password reset email failed | user=%s | user_id=%s | email=%s | ip=%s | path=%s | error=%s", + getattr(user, "username", None), + getattr(user, "id", None), + getattr(user, "email", None), + request.META.get("REMOTE_ADDR"), + request.path, + str(e), + ) + messages.error(request, "Failed to send password reset email. Please try again.") context = { diff --git a/donate/views.py b/donate/views.py index f6fc6b83..ba13da95 100644 --- a/donate/views.py +++ b/donate/views.py @@ -40,6 +40,10 @@ from donate.payment import save_ilw_hdfc_success_data, save_ilw_hdfc_error_data, get_ilw_session_payload, make_hdfc_session_request from training.models import TrainingEvents from decimal import Decimal, InvalidOperation + +import logging +logger = logging.getLogger(__name__) + # @csrf_exempt # def donatehome(request): # form = PayeeForm(initial={'country': 'India'}) @@ -299,6 +303,11 @@ def send_onetime(request): if user.is_active: context['message'] = "active_user" else: + logger.warning( + "Registration email triggered | user=%s | email=%s", + getattr(user, "username", None), + getattr(user, "email", None), + ) send_registration_confirmation(user) context['message'] = "inactive_user" @@ -311,6 +320,11 @@ def send_onetime(request): user.is_active = False user.save() create_profile(user, '') + logger.warning( + "OTP email triggered | user=%s | email=%s", + getattr(user, "username", None), + getattr(user, "email", None), + ) email_otp(user, password) context['message'] = "new" diff --git a/training/views.py b/training/views.py index 52859938..d0b29260 100644 --- a/training/views.py +++ b/training/views.py @@ -59,6 +59,23 @@ import csv import requests +import logging + +logger = logging.getLogger("mail_logs") + +def ensure_username_equals_email(user): + """ + Ensure Django username always matches email. + This is required for ILW Moodle authentication. + """ + email = (user.email or "").strip().lower() + + if email and user.username != email: + # Prevent duplicate username conflicts + if not User.objects.filter(username=email).exclude(id=user.id).exists(): + user.username = email + user.save(update_fields=["username"]) + class TrainingEventCreateView(CreateView): form_class = CreateTrainingEventForm @@ -808,6 +825,11 @@ def get_create_user(self, row): try: user = User(username=email, email=email, first_name=first, last_name=last) user.set_password(password) + logger.warning( + "Registration email triggered | user=%s | email=%s", + email, + email, + ) confirmation_code = send_registration_confirmation(user) if confirmation_code: user.save() @@ -917,15 +939,38 @@ def ajax_check_college(request): def get_create_user(row): - try: - return User.objects.get(email=row[2].strip()) - except User.DoesNotExist: - user = User(username=row[2], email=row[2].strip(), first_name=row[0], last_name=row[1]) - user.set_password(row[0]+'@ST'+str(random.random()).split('.')[1][:5]) - user.save() - create_profile(user, '') - send_registration_confirmation(user) - return user + email = row[2].strip().lower() + + try: + user = User.objects.get(email=email) + ensure_username_equals_email(user) + return user + + except User.DoesNotExist: + user = User( + username=email, + email=email, + first_name=row[0], + last_name=row[1] + ) + + user.set_password(row[0] + '@ST' + str(random.random()).split('.')[1][:5]) + user.save() + + ensure_username_equals_email(user) + + create_profile(user, '') + + logger.warning( + "Registration email triggered | user=%s | user_id=%s | email=%s", + getattr(user, "username", None), + getattr(user, "id", None), + getattr(user, "email", None), + ) + + send_registration_confirmation(user) + + return user from io import TextIOWrapper from django.contrib.auth.decorators import login_required From c1edfd2ab3ca6e43fce0e2aac9aa2f19bdb77356 Mon Sep 17 00:00:00 2001 From: ayishanishana21 Date: Thu, 11 Jun 2026 01:15:17 +0530 Subject: [PATCH 2/3] add mail logging configuration --- spoken/settings.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spoken/settings.py b/spoken/settings.py index 3ad2073b..96b7db12 100644 --- a/spoken/settings.py +++ b/spoken/settings.py @@ -459,7 +459,13 @@ "class": "logging.FileHandler", "filename": ALLOWED_LOGS['training'], "formatter": "simple", - } + }, + "mail_file": { + "level": "WARNING", + "class": "logging.FileHandler", + "filename": ALLOWED_LOGS['spoken-mails.log'], + "formatter": "simple", + }, }, "loggers": { @@ -478,5 +484,10 @@ 'level': 'ERROR', 'propagate': False, }, + "mail_logs": { + "handlers": ["mail_file"], + "level": "WARNING", + "propagate": False, + }, }, } From c0a360ec841c7794578e2341152da79ca9eda259 Mon Sep 17 00:00:00 2001 From: ayishanishana21 Date: Sun, 14 Jun 2026 23:02:43 +0530 Subject: [PATCH 3/3] added logger into info and error format --- cms/views.py | 14 +++++++------- donate/views.py | 6 +++--- training/views.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cms/views.py b/cms/views.py index 8745dad6..90d2a287 100644 --- a/cms/views.py +++ b/cms/views.py @@ -38,7 +38,7 @@ import logging -logger = logging.getLogger("mail_logs") +logger = logging.getLogger("mail_logs.cms") cache = caches['default'] @@ -161,7 +161,7 @@ def send_registration_confirmation(user): #email.attach_alternative(message, "text/html") try: result = email.send(fail_silently=False) - logger.warning( + logger.info( "Registration email sent | username=%s | user_id=%s | email=%s | subject=%s", user.username, user.id, @@ -170,7 +170,7 @@ def send_registration_confirmation(user): ) return code except Exception as e: - logger.warning( + logger.error( "Registration email failed | username=%s | user_id=%s | email=%s | error=%s", user.username, user.id, @@ -217,14 +217,14 @@ def email_otp(user, password): #email.attach_alternative(message, "text/html") try: result = email.send(fail_silently=False) - logger.warning( + logger.info( "OTP email sent | username=%s | user_id=%s | email=%s", user.username, user.id, user.email, ) except Exception as e: - logger.warning( + logger.error( "OTP email failed | username=%s | user_id=%s | email=%s | error=%s", user.username, user.id, @@ -455,7 +455,7 @@ def password_reset(request): ) try: result = email.send(fail_silently=False) - logger.warning( + logger.info( "Password reset email sent | user=%s | user_id=%s | email=%s | ip=%s | path=%s", getattr(user, "username", None), getattr(user, "id", None), @@ -471,7 +471,7 @@ def password_reset(request): messages.success(request, "New password sent to your email "+user.email) return HttpResponseRedirect('/accounts/change-password/') except Exception as e: - logger.warning( + logger.error( "Password reset email failed | user=%s | user_id=%s | email=%s | ip=%s | path=%s | error=%s", getattr(user, "username", None), getattr(user, "id", None), diff --git a/donate/views.py b/donate/views.py index ba13da95..466009cd 100644 --- a/donate/views.py +++ b/donate/views.py @@ -42,7 +42,7 @@ from decimal import Decimal, InvalidOperation import logging -logger = logging.getLogger(__name__) +logger = logging.getLogger("mail_logs.donate") # @csrf_exempt # def donatehome(request): @@ -303,7 +303,7 @@ def send_onetime(request): if user.is_active: context['message'] = "active_user" else: - logger.warning( + logger.info( "Registration email triggered | user=%s | email=%s", getattr(user, "username", None), getattr(user, "email", None), @@ -320,7 +320,7 @@ def send_onetime(request): user.is_active = False user.save() create_profile(user, '') - logger.warning( + logger.info( "OTP email triggered | user=%s | email=%s", getattr(user, "username", None), getattr(user, "email", None), diff --git a/training/views.py b/training/views.py index d0b29260..666ae045 100644 --- a/training/views.py +++ b/training/views.py @@ -61,7 +61,7 @@ import requests import logging -logger = logging.getLogger("mail_logs") +logger = logging.getLogger("mail_logs.training") def ensure_username_equals_email(user): """ @@ -825,7 +825,7 @@ def get_create_user(self, row): try: user = User(username=email, email=email, first_name=first, last_name=last) user.set_password(password) - logger.warning( + logger.info( "Registration email triggered | user=%s | email=%s", email, email, @@ -961,7 +961,7 @@ def get_create_user(row): create_profile(user, '') - logger.warning( + logger.info( "Registration email triggered | user=%s | user_id=%s | email=%s", getattr(user, "username", None), getattr(user, "id", None),