diff --git a/MANIFEST.in b/MANIFEST.in index 04f196a..34ea49d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.md include LICENSE +recursive-include requirements * diff --git a/entity_emailer/interface.py b/entity_emailer/interface.py index dca68ba..cf83ff9 100644 --- a/entity_emailer/interface.py +++ b/entity_emailer/interface.py @@ -1,9 +1,12 @@ +import sys + from datetime import datetime from django.core import mail from entity_event import context_loader from entity_emailer.models import Email +from entity_emailer.signals import pre_send from entity_emailer.utils import get_medium, get_from_email_address, get_subscribed_email_addresses, \ create_email_message, extract_email_subject_from_html_content @@ -26,7 +29,7 @@ def send_unsent_scheduled_emails(): scheduled__lte=current_time, sent__isnull=True ).select_related( - 'event' + 'event__source' ).prefetch_related( 'recipients' ) @@ -38,7 +41,10 @@ def send_unsent_scheduled_emails(): for email in to_send: to_email_addresses = get_subscribed_email_addresses(email) if to_email_addresses: + # Render the email text_message, html_message = email.render(email_medium) + + # Create the email message = create_email_message( to_emails=to_email_addresses, from_email=email.from_address or get_from_email_address(), @@ -46,6 +52,17 @@ def send_unsent_scheduled_emails(): text=text_message, html=html_message, ) + + # Fire the pre send signal + pre_send.send( + sender=sys.intern(email.event.source.name), + email=email, + event=email.event, + context=email.event.context, + message=message, + ) + + # Add the email to the list of emails that need to be sent emails.append(message) connection = mail.get_connection() diff --git a/entity_emailer/signals.py b/entity_emailer/signals.py new file mode 100644 index 0000000..f6e307d --- /dev/null +++ b/entity_emailer/signals.py @@ -0,0 +1,5 @@ +from django.dispatch import Signal + + +# An event that will be fired prior to an email being sent +pre_send = Signal(providing_args=['email', 'event', 'context', 'message']) diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index 8b761ce..9867906 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -1,6 +1,7 @@ from datetime import datetime from django.core import mail +from django.core.mail import EmailMultiAlternatives from django.core.management import call_command from django.test import TestCase, SimpleTestCase from django.test.utils import override_settings @@ -343,6 +344,36 @@ def test_sends_all_scheduled_emails(self, render_mock, address_mock): EntityEmailerInterface.send_unsent_scheduled_emails() self.assertEqual(len(mail.outbox), 2) + @patch('entity_emailer.interface.pre_send') + @patch('entity_emailer.interface.get_subscribed_email_addresses') + @patch.object(Event, 'render', spec_set=True) + def test_send_signals(self, render_mock, address_mock, mock_pre_send): + """ + Test that we properly fire signals during the send process + """ + + # Setup the email + render_mock.return_value = ['

This is a test html email.

', 'This is a test text email.'] + address_mock.return_value = ['test1@example.com', 'test2@example.com'] + email = g_email(context={ + 'test': 'test' + }, scheduled=datetime.min) + EntityEmailerInterface.send_unsent_scheduled_emails() + + # Assert that we sent the email + self.assertEqual(len(mail.outbox), 1) + + # Assert that we called the pre send signal with the proper values + name, args, kwargs = mock_pre_send.send.mock_calls[0] + self.assertEqual(kwargs['sender'], email.event.source.name) + self.assertEqual(kwargs['email'], email) + self.assertEqual(kwargs['event'], email.event) + self.assertEqual(kwargs['context'], { + 'test': 'test', + 'entity_emailer_id': str(email.view_uid) + }) + self.assertIsInstance(kwargs['message'], EmailMultiAlternatives) + @patch('entity_emailer.interface.get_subscribed_email_addresses') @patch.object(Event, 'render', spec_set=True) def test_sends_email_with_specified_from_address(self, render_mock, address_mock): diff --git a/entity_emailer/version.py b/entity_emailer/version.py index 1a72d32..b3ddbc4 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '1.1.0' +__version__ = '1.1.1' diff --git a/release_notes.rst b/release_notes.rst index 6554420..3dc8faf 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,10 @@ Release Notes ============= +v1.1.1 +------ +* Add `pre_send` signal + v1.1.0 ------ * Python 3.7 diff --git a/requirements/requirements.txt b/requirements/requirements.txt new file mode 100644 index 0000000..19454c8 --- /dev/null +++ b/requirements/requirements.txt @@ -0,0 +1,6 @@ +beautifulsoup4>=4.3.2 +Django>=2.0 +django-db-mutex>=1.2.0 +django-entity>=4.2.0 +django-entity-event>=1.2.0 +ambition-django-uuidfield>=0.5.0 diff --git a/run_tests.py b/run_tests.py index a54addd..4da1704 100644 --- a/run_tests.py +++ b/run_tests.py @@ -13,7 +13,7 @@ from django_nose import NoseTestSuiteRunner -def run_tests(*test_args, **kwargs): +def run(*test_args, **kwargs): if not test_args: test_args = ['entity_emailer'] @@ -30,4 +30,4 @@ def run_tests(*test_args, **kwargs): parser.add_option('--verbosity', dest='verbosity', action='store', default=1, type=int) (options, args) = parser.parse_args() - run_tests(*args, **options.__dict__) + run(*args, **options.__dict__) diff --git a/setup.py b/setup.py index 2176f4c..02c32d2 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +from os import path + import re from setuptools import setup, find_packages @@ -18,6 +20,20 @@ def get_version(): raise RuntimeError('Unable to find version string in {0}.'.format(VERSION_FILE)) +def get_requirements(requirements_file): + """ + Gets a list of requirements from requirements.txt. + """ + with open(path.join(path.dirname(__file__), 'requirements', requirements_file)) as requirements_file: + requirements = requirements_file.readlines() + + requirements = [r.strip() for r in requirements if r.strip()] + + return [ + r for r in requirements + if not r.startswith('#') and not r.startswith('-') + ] + setup( name='django-entity-emailer', version=get_version(), @@ -41,20 +57,8 @@ def get_version(): 'Framework :: Django :: 2.2', ], license='MIT', - install_requires=[ - 'beautifulsoup4>=4.3.2', - 'Django>=2.0', - 'django-db-mutex>=1.2.0', - 'django-entity>=4.2.0', - 'django-entity-event>=1.2.0', - 'ambition-django-uuidfield>=0.5.0', - ], - tests_require=[ - 'django-dynamic-fixture', - 'django-nose>=1.4', - 'freezegun', - 'mock', - ], - test_suite='run_tests.run_tests', + install_requires=get_requirements('requirements.txt'), + tests_require=get_requirements('requirements-testing.txt'), + test_suite='run_tests.run', include_package_data=True, )