From 5a021e95db1c0475fff3e594633baf9efaba1b48 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 11 Feb 2021 15:48:18 -0500 Subject: [PATCH 1/7] bulk create fix --- entity_emailer/models.py | 16 ++++++++---- entity_emailer/tests/interface_tests.py | 34 +++++++++++++++++++++++++ entity_emailer/version.py | 2 +- release_notes.rst | 5 ++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/entity_emailer/models.py b/entity_emailer/models.py index c61a593..e46c4ba 100644 --- a/entity_emailer/models.py +++ b/entity_emailer/models.py @@ -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) diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index 79a4ff2..08a93ca 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -296,10 +296,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 = { @@ -318,6 +323,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) diff --git a/entity_emailer/version.py b/entity_emailer/version.py index afced14..668c344 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.0.0' +__version__ = '2.0.2' diff --git a/release_notes.rst b/release_notes.rst index 30d5525..1581a84 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,11 @@ Release Notes ============= +v2.0.2 +------ +* Fix unique constraint when bulk creating emails +* This wipes 2.0.1, which needs to be added back later + v2.0.0 ------ * Added bulk interface for converting to emails From 18c04f0f68ac7ada628d1628aa095f5a52759f5a Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 11 Feb 2021 16:00:40 -0500 Subject: [PATCH 2/7] vresion --- release_notes.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/release_notes.rst b/release_notes.rst index 1581a84..7937202 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,10 +1,9 @@ Release Notes ============= -v2.0.2 +v2.0.0.1 ------ * Fix unique constraint when bulk creating emails -* This wipes 2.0.1, which needs to be added back later v2.0.0 ------ From 0574c8e246a6941e48ec0b73d309f6b150d17c2e Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 11 Feb 2021 16:01:41 -0500 Subject: [PATCH 3/7] version --- entity_emailer/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entity_emailer/version.py b/entity_emailer/version.py index 668c344..cdfc74d 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.0.2' +__version__ = '2.0.0.1' From 12eb49bd6e0c965dfa9aa8dce3c2a9c99bac9c18 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 11 Feb 2021 16:45:04 -0500 Subject: [PATCH 4/7] atomic --- entity_emailer/interface.py | 3 ++- entity_emailer/version.py | 2 +- release_notes.rst | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/entity_emailer/interface.py b/entity_emailer/interface.py index b0a071e..e9256fb 100644 --- a/entity_emailer/interface.py +++ b/entity_emailer/interface.py @@ -2,7 +2,7 @@ import traceback from datetime import datetime - +from django.db import transaction from django.core import mail from entity_event import context_loader @@ -121,6 +121,7 @@ def convert_events_to_emails(): # Create the emails Email.objects.create_email(event=event, from_address=from_address, recipients=targets) + @transaction.atomic @staticmethod def bulk_convert_events_to_emails(): """ diff --git a/entity_emailer/version.py b/entity_emailer/version.py index cdfc74d..990636f 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.0.0.1' +__version__ = '2.0.0.2' diff --git a/release_notes.rst b/release_notes.rst index 7937202..5493bfa 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v2.0.0.2 +------ +* Atomic decorator on event fetching + v2.0.0.1 ------ * Fix unique constraint when bulk creating emails From e85dc6163fb9d2b8f305247064b4237937413264 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 11 Feb 2021 16:47:47 -0500 Subject: [PATCH 5/7] fix order of wrap --- entity_emailer/interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entity_emailer/interface.py b/entity_emailer/interface.py index e9256fb..4408e1b 100644 --- a/entity_emailer/interface.py +++ b/entity_emailer/interface.py @@ -121,8 +121,8 @@ def convert_events_to_emails(): # Create the emails Email.objects.create_email(event=event, from_address=from_address, recipients=targets) - @transaction.atomic @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 From 4026c659766f16c1c294cf83ee17cc15373f88a4 Mon Sep 17 00:00:00 2001 From: Ryan Bales Date: Fri, 12 Feb 2021 15:06:33 -0500 Subject: [PATCH 6/7] fixing tests --- entity_emailer/tests/interface_tests.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index 5ece311..3f672cf 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -405,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') @@ -426,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] @@ -450,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') @@ -538,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'), ] From 1e199fc25aadb4485779613bea6f0fc1480718de Mon Sep 17 00:00:00 2001 From: Ryan Bales Date: Mon, 15 Feb 2021 14:25:10 -0500 Subject: [PATCH 7/7] bugfix for interface changes --- entity_emailer/interface.py | 2 +- entity_emailer/tests/interface_tests.py | 2 +- entity_emailer/version.py | 2 +- release_notes.rst | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/entity_emailer/interface.py b/entity_emailer/interface.py index 03d9530..07b58ec 100644 --- a/entity_emailer/interface.py +++ b/entity_emailer/interface.py @@ -94,7 +94,7 @@ def send_unsent_scheduled_emails(cls): for email in emails_to_send: try: # Send mail - connection.send_messages(email.get('message')) + connection.send_messages([email.get('message')]) except Exception as e: cls.save_email_exception(email.get('model'), e) diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index 3f672cf..1b92a01 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -451,7 +451,7 @@ def test_sends_email_with_specified_from_address(self, render_mock, address_mock EntityEmailerInterface.send_unsent_scheduled_emails() args = mock_connection.return_value.__enter__.return_value.send_messages.call_args - self.assertEqual(args[0][0].from_email, from_address) + 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) diff --git a/entity_emailer/version.py b/entity_emailer/version.py index e7c12d2..f593cd5 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.0.3' +__version__ = '2.0.4' diff --git a/release_notes.rst b/release_notes.rst index 3546d95..e508775 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v2.0.4 +------ +* Bugfix for updated interface + v2.0.3 ------ * Merging v2 hotfixes