From 60d064940ed58f74761cd6a55edbd8a25e68e4d3 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Tue, 31 Jul 2018 16:40:07 +0100 Subject: [PATCH] Move the emailing into a celery task --- opentech/apply/activity/messaging.py | 32 ++++++++++----------- opentech/apply/activity/models.py | 10 +++++++ opentech/apply/activity/tasks.py | 28 ++++++++++++++++++ opentech/apply/activity/tests/test_tasks.py | 13 +++++++++ opentech/settings/base.py | 7 +++++ requirements.txt | 2 ++ 6 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 opentech/apply/activity/tasks.py create mode 100644 opentech/apply/activity/tests/test_tasks.py diff --git a/opentech/apply/activity/messaging.py b/opentech/apply/activity/messaging.py index c52bd74b3..1d973043d 100644 --- a/opentech/apply/activity/messaging.py +++ b/opentech/apply/activity/messaging.py @@ -4,9 +4,10 @@ import requests from django.conf import settings from django.contrib import messages -from django.core.mail import send_mail from django.template.loader import render_to_string +from .tasks import send_mail, update_message_status + def link_to(target, request): return request.scheme + '://' + request.get_host() + target.get_absolute_url() @@ -69,13 +70,13 @@ class AdapterBase: return for recipient in self.recipients(message_type, **kwargs): + message_log = self.create_log(message, recipient, event) if settings.SEND_MESSAGES or self.always_send: - status = self.send_message(message, recipient=recipient, **kwargs) + status = self.send_message(message, recipient=recipient, message_log=message_log, **kwargs) else: status = 'Message not sent as SEND_MESSAGES==FALSE' - if status: - self.log_message(message, recipient, event, status) + message_log.update_status(status) if not settings.SEND_MESSAGES: if recipient: @@ -84,14 +85,13 @@ class AdapterBase: message = '{}: {}'.format(self.adapter_type, message) messages.add_message(kwargs['request'], messages.INFO, message) - def log_message(self, message, recipient, event, status): - from.models import Message - Message.objects.create( + def create_log(self, message, recipient, event): + from .models import Message + return Message.objects.create( type=self.adapter_type, content=message, recipient=recipient, event=event, - status=status, ) def send_message(self, message, **kwargs): @@ -253,18 +253,18 @@ class EmailAdapter(AdapterBase): def send_message(self, message, submission, subject, recipient, **kwargs): try: - emails_sent = send_mail( - subject, - message, - submission.page.specific.from_address, - [recipient], - fail_silently=False, + emails_sent = send_mail.apply_async( + ( + subject, + message, + submission.page.specific.from_address, + [recipient], + ), + link=update_message_status.s(kwargs['message_log'].id), ) except Exception as e: return 'Error: ' + str(e) - return 'Emails sent: ' + str(emails_sent) - class MessengerBackend: def __init__(self, *adpaters): diff --git a/opentech/apply/activity/models.py b/opentech/apply/activity/models.py index 7970cf9eb..caa8cd54a 100644 --- a/opentech/apply/activity/models.py +++ b/opentech/apply/activity/models.py @@ -1,5 +1,7 @@ from django.conf import settings from django.db import models +from django.db.models import Case, When, Value +from django.db.models.functions import Concat from .messaging import MESSAGES @@ -121,3 +123,11 @@ class Message(models.Model): recipient = models.CharField(max_length=250) event = models.ForeignKey(Event, on_delete=models.CASCADE) status = models.TextField() + + def update_status(self, status): + if status: + self.status = Case( + When(status='', then=Value(status)), + default=Concat('status', Value('<br />' + status)) + ) + self.save() diff --git a/opentech/apply/activity/tasks.py b/opentech/apply/activity/tasks.py new file mode 100644 index 000000000..53a16f845 --- /dev/null +++ b/opentech/apply/activity/tasks.py @@ -0,0 +1,28 @@ +from celery import Celery + +from django.conf import settings +from django.core.mail import send_mail as dj_send_mail + +app = Celery('tasks') + +app.config_from_object(settings, namespace='CELERY', force=True) + + +@app.task +def send_mail(*args, **kwargs): + try: + emails_sent = dj_send_mail( + *args, + fail_silently=False, + **kwargs, + ) + except Exception as e: + return 'Error: ' + str(e) + + return 'Emails sent: ' + str(emails_sent) + + +@app.task +def update_message_status(status, message): + from .models import Message + Message.objects.get(id=message).update_status(status) diff --git a/opentech/apply/activity/tests/test_tasks.py b/opentech/apply/activity/tests/test_tasks.py new file mode 100644 index 000000000..b9c73a965 --- /dev/null +++ b/opentech/apply/activity/tests/test_tasks.py @@ -0,0 +1,13 @@ +from unittest.mock import patch + +from django.test import TestCase + +from ..tasks import send_mail + + +class TestSendEmail(TestCase): + @patch('opentech.apply.activity.tasks.dj_send_mail') + def test_args_passed_to_django(self, dj_send_mail): + args = ['subject', 'message', 'from', ['to@to.com']] + send_mail(*args) + dj_send_mail.assert_called_once_with(*args, fail_silently=False) diff --git a/opentech/settings/base.py b/opentech/settings/base.py index c4da25f92..6d4b5579f 100644 --- a/opentech/settings/base.py +++ b/opentech/settings/base.py @@ -356,3 +356,10 @@ HIJACK_DECORATOR = 'opentech.apply.users.decorators.superuser_decorator' SEND_MESSAGES = env.get('SEND_MESSAGES', 'false').lower() == 'true' SLACK_DESTINATION_URL = env.get('SLACK_DESTINATION_URL', None) SLACK_DESTINATION_ROOM = env.get('SLACK_DESTINATION_ROOM', None) + + +# Celery config +if 'REDIS_URL' in env: + CELERY_BROKER_URL = env.get('REDIS_URL') +else: + CELERY_TASK_ALWAYS_EAGER = True diff --git a/requirements.txt b/requirements.txt index beb571c64..0ea9b0b33 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,6 +12,8 @@ stellar==0.4.3 django-tinymce4-lite==1.7.0 uwsgidecorators==1.1.0 django-hijack==2.1.9 +django-anymail==3.0 +celery==4.2.1 factory_boy==2.9.2 # wagtail_factories - waiting on merge and release form master branch -- GitLab