Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions entity_emailer/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import traceback

from datetime import datetime

from django.db import transaction
from django.core import mail
from entity_event import context_loader

Expand Down Expand Up @@ -94,7 +94,7 @@ def send_unsent_scheduled_emails(cls):
for email in emails_to_send:
try:
# Send mail
connection.send_message(email.get('message'))
connection.send_messages(email.get('message'))
except Exception as e:
cls.save_email_exception(email.get('model'), e)

Expand Down Expand Up @@ -123,6 +123,7 @@ def convert_events_to_emails():
Email.objects.create_email(event=event, from_address=from_address, recipients=targets)

@staticmethod
@transaction.atomic
def bulk_convert_events_to_emails():
"""
Converts unseen events to emails and marks them as seen. Uses the create_emails method to bulk create
Expand Down
16 changes: 11 additions & 5 deletions entity_emailer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,20 @@ def create_emails(self, email_params_list):

# Build list of recipient through relationships to create
recipients_to_create = []

# Keep track of unique pairs to avoid unique constraint on through relationship
email_entity_pairs = set()
for i, recipient_entities in enumerate(recipient_entities_per_email):
for recipient_entity in recipient_entities:
recipients_to_create.append(
Email.recipients.through(
email_id=emails[i].id,
entity_id=recipient_entity.id,
if (emails[i].id, recipient_entity.id) not in email_entity_pairs:
email_entity_pairs.add((emails[i].id, recipient_entity.id))

recipients_to_create.append(
Email.recipients.through(
email_id=emails[i].id,
entity_id=recipient_entity.id,
)
)
)

# Bulk create the recipient relationships
Email.recipients.through.objects.bulk_create(recipients_to_create)
Expand Down
42 changes: 38 additions & 4 deletions entity_emailer/tests/interface_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,15 @@ def test_multiple_events_only_following_false(self):

@freeze_time('2013-1-2')
def test_bulk_multiple_events_only_following_false(self):
"""
Handles bulk creating events and tests the unique constraint of the duplicated subscription which would cause
a bulk create error if it wasn't handled
"""
source = G(Source)
e = G(Entity)
other_e = G(Entity)

G(Subscription, entity=e, source=source, medium=self.email_medium, only_following=False)
G(Subscription, entity=e, source=source, medium=self.email_medium, only_following=False)
G(Subscription, entity=other_e, source=source, medium=self.email_medium, only_following=False)
email_context = {
Expand All @@ -320,6 +325,35 @@ def test_bulk_multiple_events_only_following_false(self):
self.assertEquals(email.subject, '')
self.assertEquals(email.scheduled, datetime(2013, 1, 2))

@freeze_time('2013-1-2')
def test_bulk_multiple_events_only_following_true(self):
"""
Handles bulk creating events and tests the unique constraint of the duplicated subscription which would cause
a bulk create error if it wasn't handled
"""
source = G(Source)
e = G(Entity)
other_e = G(Entity)

G(Subscription, entity=e, source=source, medium=self.email_medium, only_following=True)
G(Subscription, entity=e, source=source, medium=self.email_medium, only_following=True)
G(Subscription, entity=other_e, source=source, medium=self.email_medium, only_following=True)
email_context = {
'entity_emailer_template': 'template',
'entity_emailer_subject': 'hi',
}
G(Event, source=source, context=email_context)
event = G(Event, source=source, context=email_context)
G(EventActor, event=event, entity=e)

EntityEmailerInterface.bulk_convert_events_to_emails()

email = Email.objects.get()
self.assertEquals(set(email.recipients.all()), set([e]))
self.assertEquals(email.event.context, email_context)
self.assertEquals(email.subject, '')
self.assertEquals(email.scheduled, datetime(2013, 1, 2))

@freeze_time('2013-1-2')
def test_multiple_events_only_following_true(self):
source = G(Source)
Expand Down Expand Up @@ -371,7 +405,7 @@ def test_sends_all_scheduled_emails(self, render_mock, address_mock):
with patch(settings.EMAIL_BACKEND) as mock_connection:
EntityEmailerInterface.send_unsent_scheduled_emails()

self.assertEqual(2, mock_connection.return_value.__enter__.return_value.send_message.call_count)
self.assertEqual(2, mock_connection.return_value.__enter__.return_value.send_messages.call_count)

@patch('entity_emailer.interface.pre_send')
@patch('entity_emailer.interface.get_subscribed_email_addresses')
Expand All @@ -392,7 +426,7 @@ def test_send_signals(self, render_mock, address_mock, mock_pre_send):
EntityEmailerInterface.send_unsent_scheduled_emails()

# Assert that we sent the email
self.assertEqual(1, mock_connection.return_value.__enter__.return_value.send_message.call_count)
self.assertEqual(1, mock_connection.return_value.__enter__.return_value.send_messages.call_count)

# Assert that we called the pre send signal with the proper values
name, args, kwargs = mock_pre_send.send.mock_calls[0]
Expand All @@ -416,7 +450,7 @@ def test_sends_email_with_specified_from_address(self, render_mock, address_mock
with patch(settings.EMAIL_BACKEND) as mock_connection:
EntityEmailerInterface.send_unsent_scheduled_emails()

args = mock_connection.return_value.__enter__.return_value.send_message.call_args
args = mock_connection.return_value.__enter__.return_value.send_messages.call_args
self.assertEqual(args[0][0].from_email, from_address)

@patch('entity_emailer.interface.get_subscribed_email_addresses')
Expand Down Expand Up @@ -504,7 +538,7 @@ def to_dict(self):

with patch(settings.EMAIL_BACKEND) as mock_connection:
# Mock side effects for sending emails
mock_connection.return_value.__enter__.return_value.send_message.side_effect = [
mock_connection.return_value.__enter__.return_value.send_messages.side_effect = [
None,
TestEmailSendMessageException('test'),
]
Expand Down
2 changes: 1 addition & 1 deletion entity_emailer/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.0.1'
__version__ = '2.0.3'
12 changes: 12 additions & 0 deletions release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
Release Notes
=============

v2.0.3
------
* Merging v2 hotfixes

v2.0.1
------
* Fix for handling single failures in a batch of outgoing emails

v2.0.0.2
------
* Atomic decorator on event fetching

v2.0.0.1
------
* Fix unique constraint when bulk creating emails

v2.0.0
------
* Added bulk interface for converting to emails
Expand Down