Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Completely untested at this point, but this is the gist of what is ne…
…eded to get this running with celery. More to come tomorrow.
- Loading branch information
Greg Taylor
committed
Apr 13, 2011
1 parent
d1e80a3
commit 8b9321c
Showing
2 changed files
with
83 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,84 +1,24 @@ | |||
from django.core.mail.backends.base import BaseEmailBackend | from django.core.mail.backends.base import BaseEmailBackend | ||
from django.conf import settings | |||
|
|
||
import threading | from seacucumber.tasks import SendEmailTask | ||
|
|||
from boto.ses import SESConnection | |||
|
|
||
class SESBackend(BaseEmailBackend): | class SESBackend(BaseEmailBackend): | ||
""" | """ | ||
A Django Email backend that uses Amazon's Simple Email Service. | A Django Email backend that uses Amazon's Simple Email Service. | ||
""" | """ | ||
def __init__(self, fail_silently=False, *args, **kwargs): | |||
super(SESBackend, self).__init__(fail_silently=fail_silently, *args, | |||
**kwargs) | |||
|
|||
self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) | |||
self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) | |||
self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST', | |||
SESConnection.DefaultHost) | |||
|
|||
self.connection = None | |||
|
|||
def open(self): | |||
"""Create a connection to the AWS API server. This can be reused for | |||
sending multiple emails. | |||
""" | |||
if self.connection: | |||
return False | |||
|
|||
try: | |||
self.connection = SESConnection( | |||
aws_access_key_id=self._access_key_id, | |||
aws_secret_access_key=self._access_key, | |||
host=self._api_endpoint, | |||
) | |||
except: | |||
if not self.fail_silently: | |||
raise | |||
|
|||
def close(self): | |||
"""Close any open HTTP connections to the API server. | |||
""" | |||
try: | |||
self.connection.close() | |||
self.connection = None | |||
except: | |||
if not self.fail_silently: | |||
raise | |||
|
|||
def send_messages(self, email_messages): | def send_messages(self, email_messages): | ||
"""Sends one or more EmailMessage objects and returns the number of | """ | ||
Sends one or more EmailMessage objects and returns the number of | |||
email messages sent. | email messages sent. | ||
""" | """ | ||
if not email_messages: | |||
return | |||
|
|||
new_conn_created = self.open() | |||
if not self.connection: | |||
# Failed silently | |||
return | |||
|
|||
num_sent = 0 | num_sent = 0 | ||
for message in email_messages: | for message in email_messages: | ||
try: | # Hand this off to a celery task. | ||
response = self.connection.send_raw_email( | SendEmailTask.delay( | ||
source=message.from_email, | message.from_email, | ||
destinations=message.recipients(), | message.recipients(), | ||
raw_message=message.message().as_string(), | message.message().as_string(), | ||
) | ) | ||
send_response = response['SendRawEmailResponse'] | num_sent += 1 | ||
send_result = send_response['SendRawEmailResult'] | |||
message.extra_headers['Message-Id'] = send_result['MessageId'] | |||
|
|||
num_sent += 1 | |||
except SESConnection.ResponseError: | |||
if not self.fail_silently: | |||
raise | |||
pass | |||
|
|||
if new_conn_created: | |||
self.close() | |||
|
|||
return num_sent | return num_sent | ||
|
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,73 @@ | |||
from django.conf import settings | |||
from boto.ses import SESConnection | |||
from celery.task import Task | |||
|
|||
class SendEmailTask(Task): | |||
""" | |||
Sends an email through Boto's SES API module. | |||
""" | |||
# TODO: Make this a setting. | |||
max_retries = 60 | |||
# TODO: Make this a setting. | |||
default_retry_delay = 60 | |||
|
|||
def __init__(self, *args, **kwargs): | |||
super(SendEmailTask, self).__init__(*args, **kwargs) | |||
|
|||
self.connection = None | |||
self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None) | |||
self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) | |||
self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST', | |||
SESConnection.DefaultHost) | |||
|
|||
def run(self, from_email, recipients, message): | |||
""" | |||
This does the dirty work. | |||
TOOD: Document params. | |||
""" | |||
self._open_ses_conn() | |||
try: | |||
self.connection.send_raw_email( | |||
source=from_email, | |||
destinations=recipients, | |||
raw_message=message, | |||
) | |||
except SESConnection.ResponseError: | |||
self.retry( | |||
countdown=self.default_retry_delay, | |||
exc=SESConnection.ResponseError, | |||
) | |||
self._close_ses_conn() | |||
|
|||
|
|||
def _open_ses_conn(self): | |||
""" | |||
Create a connection to the AWS API server. This can be reused for | |||
sending multiple emails. | |||
""" | |||
if self.connection: | |||
return | |||
|
|||
try: | |||
self.connection = SESConnection( | |||
aws_access_key_id=self._access_key_id, | |||
aws_secret_access_key=self._access_key, | |||
host=self._api_endpoint, | |||
) | |||
# TODO: Get more specific with this exception block. | |||
except: | |||
self.retry( | |||
countdown=self.default_retry_delay, | |||
) | |||
|
|||
def _close_ses_conn(self): | |||
""" | |||
Close any open HTTP connections to the API server. | |||
""" | |||
try: | |||
self.connection.close() | |||
self.connection = None | |||
except: | |||
# It doesn't really matter at this point. | |||
pass |