changeset 180:aef00df91165

Implement #63, add a queued email facility.
author Brian Neal <bgneal@gmail.com>
date Sun, 21 Mar 2010 20:33:33 +0000
parents 70b2e307c866
children 500e5875a306
files gpp/core/functions.py gpp/mailer/__init__.py gpp/mailer/admin.py gpp/mailer/management/__init__.py gpp/mailer/management/commands/__init__.py gpp/mailer/management/commands/send_mail.py gpp/mailer/models.py gpp/settings.py
diffstat 6 files changed, 128 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/gpp/core/functions.py	Wed Mar 17 03:18:53 2010 +0000
+++ b/gpp/core/functions.py	Sun Mar 21 20:33:33 2010 +0000
@@ -13,7 +13,9 @@
     log all emails.
     """
 
-    if settings.GPP_SEND_EMAIL:
+    if settings.MAILER_ENQUEUE_MAIL:
+        mailer.enqueue_mail(subject, message, from_email, recipient_list)
+    elif settings.GPP_SEND_EMAIL:
         django.core.mail.send_mail(subject, message, from_email, recipient_list,
                 fail_silently, auth_user, auth_password)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/mailer/__init__.py	Sun Mar 21 20:33:33 2010 +0000
@@ -0,0 +1,52 @@
+from socket import error as socket_error
+import smtplib
+import time
+
+import django.core.mail
+
+import mailer.models
+
+
+def enqueue_mail(subject, message, from_email, recipient_list):
+    """Creates mailer mail queue entries for the given email."""
+
+    if len(subject) > mailer.models.MAX_SUBJECT:
+        subject = u'%s...' % subject[:mailer.models.MAX_SUBJECT - 3]
+
+    for recipient in recipient_list:
+        mailer.models.Message(
+                from_address=from_email,
+                to_address=recipient,
+                subject=subject,
+                body=message).save()
+
+
+def send_queued_mail():
+    """Reads the queued messages from the database, sends them, and removes them
+    from the queue."""
+
+    sent, errors = 0, 0
+
+    import logging
+    logging.debug("Sending queued mail...")
+    start_time = time.time()
+
+    msgs = mailer.models.Message.objects.all()
+    for msg in msgs:
+        try:
+            django.core.mail.send_mail(
+                    msg.subject, 
+                    msg.body, 
+                    msg.from_address, 
+                    [msg.to_address],
+                    fail_silently=False)
+        except (socket_error, smtplib.SMTPException), e:
+            errors += 1
+            logging.error("Error sending queued mail: %s" % e)
+        else:
+        	sent += 1
+        	msg.delete()
+
+    end_time = time.time()
+    logging.debug("Sent queued mail: %d successful, %d error(s); elapsed time: %.2f" % (
+        sent, errors, end_time - start_time))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/mailer/admin.py	Sun Mar 21 20:33:33 2010 +0000
@@ -0,0 +1,13 @@
+"""This file contains the automatic admin site definitions for the mailer
+application."""
+from django.contrib import admin
+
+from mailer.models import Message
+
+
+class MessageAdmin(admin.ModelAdmin):
+    list_display = ('from_address', 'to_address', 'subject', 'creation_date')
+    list_display_links = ('subject', )
+
+
+admin.site.register(Message, MessageAdmin)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/mailer/management/commands/send_mail.py	Sun Mar 21 20:33:33 2010 +0000
@@ -0,0 +1,15 @@
+"""send_mail is a custom manage.py command for the mailer application.
+It is intended to be called from a cron job periodically to send out queued
+email in bulk. This avoids doing the mailing on the HTTP request processing.
+"""
+from django.core.management.base import NoArgsCommand
+
+import mailer
+
+
+class Command(NoArgsCommand):
+    help = "Run periodically to send out queued email."
+
+    def handle_noargs(self, **options):
+        mailer.send_queued_mail()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gpp/mailer/models.py	Sun Mar 21 20:33:33 2010 +0000
@@ -0,0 +1,27 @@
+"""Models for the mailer application."""
+import datetime
+
+from django.db import models
+
+
+MAX_SUBJECT = 120
+
+class Message(models.Model):
+    """The model to represent stored emails in the database."""
+    from_address = models.EmailField()
+    to_address = models.EmailField()
+    subject = models.CharField(max_length=MAX_SUBJECT)
+    body = models.TextField()
+    creation_date = models.DateTimeField()
+
+    class Meta:
+        ordering = ('creation_date', )
+
+    def __unicode__(self):
+        return u'From: %s, To: %s, Subj: %s' % (
+            self.from_address, self.to_address, self.subject)
+
+    def save(self, *args, **kwargs):
+        if self.id is None:
+            self.creation_date = datetime.datetime.now()
+        super(Message, self).save(*args, **kwargs)
--- a/gpp/settings.py	Wed Mar 17 03:18:53 2010 +0000
+++ b/gpp/settings.py	Sun Mar 21 20:33:33 2010 +0000
@@ -117,6 +117,7 @@
     'forums',
     'gcalendar',
     'irc',
+    'mailer',
     'membermap',
     'messages',
     'news',
@@ -141,6 +142,12 @@
 MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
 
 #######################################################################
+# Email
+#######################################################################
+EMAIL_HOST = local_settings.EMAIL_HOST
+EMAIL_PORT = local_settings.EMAIL_PORT
+
+#######################################################################
 # Caching
 #######################################################################
 if local_settings.USE_CACHE:
@@ -159,7 +166,7 @@
 # GPP Specific Settings
 #######################################################################
 GPP_LOG_LEVEL = 0
-GPP_SEND_EMAIL = local_settings.GPP_SEND_EMAIL
+GPP_SEND_EMAIL = local_settings.GPP_SEND_EMAIL  # see MAILER_ENQUEUE_MAIL
 GPP_NO_REPLY_EMAIL = 'no_reply'
 AVATAR_DIR = 'avatars'
 MAX_AVATAR_SIZE_BYTES = 2 * 1024 * 1024
@@ -176,6 +183,16 @@
 DONATIONS_ITEM_NUM = '500'          # donation w/name listed
 DONATIONS_ITEM_ANON_NUM = '501'     # donation listed as anonymous
 
+# If MAILER_ENQUEUE_MAIL is True, all emails will be stored in the
+# mailer application's mail queue (database table). It is then expected
+# that a daemon or cron job will actually send the mail out. If
+# MAILER_ENQUEUE_MAIL is False, then email will only be sent if
+# the setting GPP_SEND_EMAIL (above) is True. In any event, emails
+# will be logged via the Python logger if the Python logger filter
+# DEBUG is active.
+
+MAILER_ENQUEUE_MAIL = True
+
 #######################################################################
 # Configure Logging
 #######################################################################