diff --git a/entity_emailer/interface.py b/entity_emailer/interface.py index 00df989..07b58ec 100644 --- a/entity_emailer/interface.py +++ b/entity_emailer/interface.py @@ -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 @@ -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) @@ -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 diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index 031b900..c44d628 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -325,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) @@ -405,7 +434,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') @@ -426,7 +455,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] @@ -450,8 +479,8 @@ 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 - self.assertEqual(args[0][0].from_email, from_address) + args = mock_connection.return_value.__enter__.return_value.send_messages.call_args + self.assertEqual(args[0][0][0].from_email, from_address) @patch('entity_emailer.interface.get_subscribed_email_addresses') @patch.object(Event, 'render', spec_set=True) @@ -538,7 +567,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'), ] diff --git a/entity_emailer/version.py b/entity_emailer/version.py index 668c344..f593cd5 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.0.2' +__version__ = '2.0.4' diff --git a/release_notes.rst b/release_notes.rst index fd8c695..c0fbdb5 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v2.0.4 +------ +* Bugfix for updated interface + v2.0.2 ------ * Fix unique constraint when bulk creating emails @@ -9,6 +13,14 @@ 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