Commit 4319a828 by Attiya Ishaque Committed by GitHub

Merge pull request #13816 from edx/atiya/celery-email-sending-on-registration

ECOM-5281 Adding retries and celery task for sending activation email.
parents 82345810 0ffde1a6
......@@ -1208,3 +1208,7 @@ COMPREHENSIVE_THEME_LOCALE_PATHS = []
# to generating test databases will discover and try to create all tables
# and this setting needs to be present
OAUTH2_PROVIDER_APPLICATION_MODEL = 'oauth2_provider.Application'
# Used with Email sending
RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5
RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5
"""
This file contains celery tasks for sending email
"""
import logging
from django.conf import settings
from django.core import mail
from celery.task import task # pylint: disable=no-name-in-module, import-error
from celery.exceptions import MaxRetriesExceededError
from boto.exception import NoAuthHandlerFound
log = logging.getLogger('edx.celery.task')
@task(bind=True)
def send_activation_email(self, subject, message, from_address, dest_addr):
"""
Sending an activation email to the users.
"""
max_retries = settings.RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS
retries = self.request.retries
try:
mail.send_mail(subject, message, from_address, [dest_addr], fail_silently=False)
# Log that the Activation Email has been sent to user without an exception
log.info("Activation Email has been sent to User {user_email}".format(
user_email=dest_addr
))
except NoAuthHandlerFound: # pylint: disable=broad-except
log.info('Retrying sending email to user {dest_addr}, attempt # {attempt} of {max_attempts}'. format(
dest_addr=dest_addr,
attempt=retries,
max_attempts=max_retries
))
try:
self.retry(countdown=settings.RETRY_ACTIVATION_EMAIL_TIMEOUT, max_retries=max_retries)
except MaxRetriesExceededError:
log.error(
'Unable to send activation email to user from "%s" to "%s"',
from_address,
dest_addr,
exc_info=True
)
except Exception: # pylint: disable=bare-except
log.exception(
'Unable to send activation email to user from "%s" to "%s"',
from_address,
dest_addr,
exc_info=True
)
raise Exception
......@@ -233,7 +233,7 @@ class TestCreateAccount(TestCase):
request.user = AnonymousUser()
with mock.patch('edxmako.request_context.get_current_request', return_value=request):
with mock.patch('django.contrib.auth.models.User.email_user') as mock_send_mail:
with mock.patch('django.core.mail.send_mail') as mock_send_mail:
student.views.create_account(request)
# check that send_mail is called
......
"""
Tests for the Sending activation email celery tasks
"""
import mock
from django.test import TestCase
from django.conf import settings
from student.tasks import send_activation_email
from boto.exception import NoAuthHandlerFound
from lms.djangoapps.courseware.tests.factories import UserFactory
class SendActivationEmailTestCase(TestCase):
"""
Test for send activation email to user
"""
def setUp(self):
""" Setup components used by each test."""
super(SendActivationEmailTestCase, self).setUp()
self.student = UserFactory()
@mock.patch('time.sleep', mock.Mock(return_value=None))
@mock.patch('student.tasks.log')
@mock.patch('django.core.mail.send_mail', mock.Mock(side_effect=NoAuthHandlerFound))
def test_send_email(self, mock_log):
"""
Tests retries when the activation email doesn't send
"""
from_address = 'task_testing@example.com'
email_max_attempts = settings.RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS
# pylint: disable=no-member
send_activation_email.delay('Task_test', 'Task_test_message', from_address, self.student.email)
# Asserts sending email retry logging.
for attempt in range(email_max_attempts):
mock_log.info.assert_any_call(
'Retrying sending email to user {dest_addr}, attempt # {attempt} of {max_attempts}'.format(
dest_addr=self.student.email,
attempt=attempt,
max_attempts=email_max_attempts
))
self.assertEquals(mock_log.info.call_count, 6)
# Asserts that the error was logged on crossing max retry attempts.
mock_log.error.assert_called_with(
'Unable to send activation email to user from "%s" to "%s"',
from_address,
self.student.email,
exc_info=True
)
self.assertEquals(mock_log.error.call_count, 1)
......@@ -56,6 +56,7 @@ from student.models import (
DashboardConfiguration, LinkedInAddToProfileConfiguration, ManualEnrollmentAudit, ALLOWEDTOENROLL_TO_ENROLLED,
LogoutViewConfiguration, RegistrationCookieConfiguration)
from student.forms import AccountCreationForm, PasswordResetFormNoActive, get_registration_extension_form
from student.tasks import send_activation_email
from lms.djangoapps.commerce.utils import EcommerceService # pylint: disable=import-error
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error
from bulk_email.models import Optout, BulkEmailFlag # pylint: disable=import-error
......@@ -1812,21 +1813,11 @@ def create_account_with_params(request, params):
'email_from_address',
settings.DEFAULT_FROM_EMAIL
)
try:
if settings.FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
mail.send_mail(subject, message, from_address, [dest_addr], fail_silently=False)
else:
user.email_user(subject, message, from_address)
except Exception: # pylint: disable=broad-except
log.error(
u'Unable to send activation email to user from "%s" to "%s"',
from_address,
dest_addr,
exc_info=True
)
send_activation_email.delay(subject, message, from_address, dest_addr)
else:
registration.activate()
_enroll_user_in_pending_courses(user) # Enroll student in any pending courses
......
......@@ -375,6 +375,9 @@ GENERATE_PROFILE_SCORES = False
# Used with XQueue
XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5 # seconds
# Used with Email sending
RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5
RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5
############################# SET PATH INFORMATION #############################
PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /edx-platform/lms
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment