From 05c10edc0032588fb727f0ba01ca02c43823c1c7 Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Fri, 28 Apr 2023 11:51:12 -0400 Subject: [PATCH 01/15] Fixed urls, signals, six, settings for Dj4 compatibility --- entity_emailer/signals.py | 6 ++++-- entity_emailer/tests/test_views.py | 10 +++------- entity_emailer/urls.py | 4 ++-- settings.py | 4 +++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/entity_emailer/signals.py b/entity_emailer/signals.py index 8283156..9a08a62 100644 --- a/entity_emailer/signals.py +++ b/entity_emailer/signals.py @@ -2,7 +2,9 @@ # An event that will be fired prior to an email being sent -pre_send = Signal(providing_args=['email', 'event', 'context', 'message']) +pre_send = Signal() +"""providing_args=['email', 'event', 'context', 'message']""" # An event that will be fired if an exception occurs when trying to send an email -email_exception = Signal(providing_args=['email', 'exception']) +email_exception = Signal() +"""providing_args=['email', 'exception']""" diff --git a/entity_emailer/tests/test_views.py b/entity_emailer/tests/test_views.py index f64c5e0..7bb3b3e 100644 --- a/entity_emailer/tests/test_views.py +++ b/entity_emailer/tests/test_views.py @@ -3,7 +3,6 @@ from django_dynamic_fixture import G from entity.models import Entity from entity_event.models import Medium, RenderingStyle, ContextRenderer, Source, Event -import six from entity_emailer.tests.utils import g_email @@ -30,8 +29,7 @@ def test_html_path(self): url = reverse('entity_emailer.email', args=[email.view_uid]) response = self.client.get(url) content = response.content - if six.PY3: # pragma: no cover - content = content.decode('utf8') + content = content.decode('utf8') self.assertEqual(content, 'Hi Swansonbot') @@ -50,8 +48,7 @@ def test_text_path(self): url = reverse('entity_emailer.email', args=[email.view_uid]) response = self.client.get(url) content = response.content - if six.PY3: # pragma: no cover - content = content.decode('utf8') + content = content.decode('utf8') self.assertEqual(content, 'Hi Swansonbot') def test_text_and_html_path(self): @@ -73,7 +70,6 @@ def test_text_and_html_path(self): url = reverse('entity_emailer.email', args=[email.view_uid]) response = self.client.get(url) content = response.content - if six.PY3: # pragma: no cover - content = content.decode('utf8') + content = content.decode('utf8') self.assertEqual(content, 'Hi Swansonbot') diff --git a/entity_emailer/urls.py b/entity_emailer/urls.py index 472a984..6cc3231 100644 --- a/entity_emailer/urls.py +++ b/entity_emailer/urls.py @@ -1,8 +1,8 @@ -from django.conf.urls import url +from django.urls import re_path from entity_emailer.views import EmailView urlpatterns = [ - url(r'^([0-9a-z\-]+)/$', EmailView.as_view(), name='entity_emailer.email'), + re_path(r'^([0-9a-z\-]+)/$', EmailView.as_view(), name='entity_emailer.email'), ] diff --git a/settings.py b/settings.py index 18ffa74..05b5934 100644 --- a/settings.py +++ b/settings.py @@ -65,10 +65,12 @@ def configure_settings(): 'OPTIONS': { 'context_processors': [ 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages' + 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.request', ] } }], TEST_RUNNER='django_nose.NoseTestSuiteRunner', NOSE_ARGS=['--nocapture', '--nologcapture', '--verbosity=1'], + DEFAULT_AUTO_FIELD='django.db.models.AutoField', ) From d46b17d1db2284cf38a0642ff21cdff51fc0abaa Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Fri, 28 Apr 2023 11:54:28 -0400 Subject: [PATCH 02/15] removed default_app_config; added warnings to manage.py --- entity_emailer/__init__.py | 2 -- manage.py | 7 +++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/entity_emailer/__init__.py b/entity_emailer/__init__.py index c9a8486..ed53594 100644 --- a/entity_emailer/__init__.py +++ b/entity_emailer/__init__.py @@ -1,4 +1,2 @@ # flake8: noqa from .version import __version__ - -default_app_config = 'entity_emailer.apps.EntityEmailerConfig' diff --git a/manage.py b/manage.py index e374933..b997ce1 100644 --- a/manage.py +++ b/manage.py @@ -1,6 +1,13 @@ #!/usr/bin/env python import sys +# Show warnings about django deprecations - uncomment for version upgrade testing +import warnings +from django.utils.deprecation import RemovedInNextVersionWarning +warnings.filterwarnings('always', category=DeprecationWarning) +warnings.filterwarnings('always', category=PendingDeprecationWarning) +warnings.filterwarnings('always', category=RemovedInNextVersionWarning) + from settings import configure_settings From eb4e7e492de8632e2a9a5e77e2cffc1e7581ea2f Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Fri, 28 Apr 2023 11:56:45 -0400 Subject: [PATCH 03/15] More cleanup --- entity_emailer/migrations/0001_initial.py | 1 - entity_emailer/migrations/0002_auto_20170919_1653.py | 1 - entity_emailer/tests/interface_tests.py | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/entity_emailer/migrations/0001_initial.py b/entity_emailer/migrations/0001_initial.py index c38764a..ea2e556 100644 --- a/entity_emailer/migrations/0001_initial.py +++ b/entity_emailer/migrations/0001_initial.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals from django.db import models, migrations import django.db.models.deletion diff --git a/entity_emailer/migrations/0002_auto_20170919_1653.py b/entity_emailer/migrations/0002_auto_20170919_1653.py index 7c8cf28..b2e6380 100644 --- a/entity_emailer/migrations/0002_auto_20170919_1653.py +++ b/entity_emailer/migrations/0002_auto_20170919_1653.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Generated by Django 1.9.11 on 2017-09-19 16:53 -from __future__ import unicode_literals from django.db import migrations, models import uuid diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index eda7dde..c994801 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -14,7 +14,7 @@ Medium, Source, Subscription, Unsubscription, Event, EventActor ) from freezegun import freeze_time -from mock import patch +from unittest.mock import patch from entity_emailer.interface import EntityEmailerInterface from entity_emailer.models import Email From 8d2d5ed9d2f51157f1ac04991d4d8e8edee8559d Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Fri, 28 Apr 2023 11:58:11 -0400 Subject: [PATCH 04/15] Bumped to beta version 2.1.3b1 --- 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 f811561..509f11c 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.1.2' +__version__ = '2.1.3b1' From bb06f9b40d9059def83fb4eddf32376d5967c130 Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Fri, 28 Apr 2023 12:03:06 -0400 Subject: [PATCH 05/15] Added secret key setting --- settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/settings.py b/settings.py index 05b5934..08bb400 100644 --- a/settings.py +++ b/settings.py @@ -73,4 +73,5 @@ def configure_settings(): TEST_RUNNER='django_nose.NoseTestSuiteRunner', NOSE_ARGS=['--nocapture', '--nologcapture', '--verbosity=1'], DEFAULT_AUTO_FIELD='django.db.models.AutoField', + SECRET_KEY='*', ) From 02e628e8554e19c2f67d86ea6746af02d258c008 Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 07:44:37 -0400 Subject: [PATCH 06/15] Updated assertEqual naming convention --- entity_emailer/tests/interface_tests.py | 128 ++++++++++++------------ 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/entity_emailer/tests/interface_tests.py b/entity_emailer/tests/interface_tests.py index c994801..ff5ef27 100644 --- a/entity_emailer/tests/interface_tests.py +++ b/entity_emailer/tests/interface_tests.py @@ -26,28 +26,28 @@ class ExtractEmailSubjectFromHtmlContentTest(SimpleTestCase): def test_blank(self): subject = extract_email_subject_from_html_content('') - self.assertEquals(subject, '') + self.assertEqual(subject, '') def test_with_title_block(self): subject = extract_email_subject_from_html_content(' Hello! ') - self.assertEquals(subject, 'Hello!') + self.assertEqual(subject, 'Hello!') def test_wo_title_block_under_40_chars_content(self): subject = extract_email_subject_from_html_content(' Small content ') - self.assertEquals(subject, 'Small content') + self.assertEqual(subject, 'Small content') def test_wo_title_block_under_40_chars_multiline_content(self): subject = extract_email_subject_from_html_content(( ' Small content \n' 'that spans multiple lines' )) - self.assertEquals(subject, 'Small content') + self.assertEqual(subject, 'Small content') def test_wo_title_block_gt_40_chars_content(self): subject = extract_email_subject_from_html_content(( ' This is reallly long content that is greater than 40 chars on the first line. It should have ...' )) - self.assertEquals(subject, 'This is reallly long content that is gre...') + self.assertEqual(subject, 'This is reallly long content that is gre...') class ConvertEventsToEmailsTest(TestCase): @@ -113,10 +113,10 @@ def test_basic_only_following_false_subscription(self): EntityEmailerInterface.convert_events_to_emails() email = Email.objects.get() - self.assertEquals(list(email.recipients.all()), [e]) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(list(email.recipients.all()), [e]) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_basic_only_following_false_subscription_marked_seen(self): @@ -134,10 +134,10 @@ def test_basic_only_following_false_subscription_marked_seen(self): EntityEmailerInterface.convert_events_to_emails() email = Email.objects.get() - self.assertEquals(list(email.recipients.all()), [e]) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(list(email.recipients.all()), [e]) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_basic_only_following_true_subscription(self): @@ -160,10 +160,10 @@ def test_basic_only_following_true_subscription(self): email = Email.objects.get() # Since the other_e entity does not follow the se entity, only the e entity receives an email - 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)) + self.assertEqual(set(email.recipients.all()), set([e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_super_entity_only_following_false_subscription(self): @@ -187,10 +187,10 @@ def test_super_entity_only_following_false_subscription(self): EntityEmailerInterface.convert_events_to_emails() email = Email.objects.get() - self.assertEquals(set(email.recipients.all()), set([e, other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([e, other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_basic_only_following_true_group_subscription(self): @@ -214,10 +214,10 @@ def test_basic_only_following_true_group_subscription(self): email = Email.objects.get() # Both entities are subscribed with a group subscription and are following the super entity of the event - self.assertEquals(set(email.recipients.all()), set([e, other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([e, other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_basic_only_following_false_group_subscription(self): @@ -241,10 +241,10 @@ def test_basic_only_following_false_group_subscription(self): email = Email.objects.get() # Both entities are subscribed with a group subscription and are following the super entity of the event - self.assertEquals(set(email.recipients.all()), set([e, other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([e, other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_basic_only_following_false_group_subscription_with_unsubscribed(self): @@ -268,10 +268,10 @@ def test_basic_only_following_false_group_subscription_with_unsubscribed(self): EntityEmailerInterface.convert_events_to_emails() email = Email.objects.get() - self.assertEquals(set(email.recipients.all()), set([other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_multiple_events_only_following_false(self): @@ -290,12 +290,12 @@ def test_multiple_events_only_following_false(self): EntityEmailerInterface.convert_events_to_emails() - self.assertEquals(Email.objects.count(), 2) + self.assertEqual(Email.objects.count(), 2) for email in Email.objects.all(): - self.assertEquals(set(email.recipients.all()), set([e, other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([e, other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_bulk_multiple_events_only_following_false(self): @@ -319,12 +319,12 @@ def test_bulk_multiple_events_only_following_false(self): EntityEmailerInterface.bulk_convert_events_to_emails() - self.assertEquals(Email.objects.count(), 2) + self.assertEqual(Email.objects.count(), 2) for email in Email.objects.all(): - self.assertEquals(set(email.recipients.all()), set([e, other_e])) - self.assertEquals(email.event.context, email_context) - self.assertEquals(email.subject, '') - self.assertEquals(email.scheduled, datetime(2013, 1, 2)) + self.assertEqual(set(email.recipients.all()), set([e, other_e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_bulk_multiple_events_only_following_true(self): @@ -350,10 +350,10 @@ def test_bulk_multiple_events_only_following_true(self): 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)) + self.assertEqual(set(email.recipients.all()), set([e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2013-1-2') def test_multiple_events_only_following_true(self): @@ -374,10 +374,10 @@ def test_multiple_events_only_following_true(self): EntityEmailerInterface.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)) + self.assertEqual(set(email.recipients.all()), set([e])) + self.assertEqual(email.event.context, email_context) + self.assertEqual(email.subject, '') + self.assertEqual(email.scheduled, datetime(2013, 1, 2)) @freeze_time('2014-01-05') @@ -531,7 +531,7 @@ def test_exceptions(self, render_mock, address_mock, mock_email_exception): self.assertEqual(Email.objects.filter(sent__isnull=False).count(), 1) # Assert that only one email is actually sent through backend - self.assertEquals(1, mock_connection.call_count) + self.assertEqual(1, mock_connection.call_count) # Assert that one email raised an exception and that the tries count was updated exception_email = Email.objects.get(exception__isnull=False, num_tries=1) self.assertIsNotNone(exception_email) @@ -558,7 +558,7 @@ def test_send_exceptions_and_retry(self, mock_get_subscribed_addresses, mock_ren mock_render.return_value = ('foo', 'bar',) # Verify baseline, namely that both emails are not marked as sent and that neither has an exception saved - self.assertEquals(2, Email.objects.filter(sent__isnull=True).count()) + self.assertEqual(2, Email.objects.filter(sent__isnull=True).count()) class TestEmailSendMessageException(Exception): @property @@ -575,11 +575,11 @@ def to_dict(self): EntityEmailerInterface.send_unsent_scheduled_emails() # Verify that only one email is marked as sent - self.assertEquals(1, Email.objects.filter(sent__isnull=False).count()) + self.assertEqual(1, Email.objects.filter(sent__isnull=False).count()) # Verify that the failed email was saved with the exception actual_failed_email = Email.objects.get(exception__isnull=False, num_tries=1) - self.assertEquals(failed_email.id, actual_failed_email.id) - self.assertEquals( + self.assertEqual(failed_email.id, actual_failed_email.id) + self.assertEqual( 'test: {}'.format(json.dumps({'message': 'test'})), actual_failed_email.exception ) @@ -594,11 +594,11 @@ def to_dict(self): EntityEmailerInterface.send_unsent_scheduled_emails() # Verify only called once, with the failed email - self.assertEquals(1, mock_connection.return_value.__enter__.return_value.send_messages.call_count) + self.assertEqual(1, mock_connection.return_value.__enter__.return_value.send_messages.call_count) # Verify num tries updated actual_failed_email = Email.objects.get(exception__isnull=False, num_tries=2) - self.assertEquals(failed_email.id, actual_failed_email.id) + self.assertEqual(failed_email.id, actual_failed_email.id) # Verify that a subsequent attempt to send unscheduled emails will find no emails to send with patch(settings.EMAIL_BACKEND) as mock_connection: @@ -606,11 +606,11 @@ def to_dict(self): EntityEmailerInterface.send_unsent_scheduled_emails() # Verify only called once, with the failed email - self.assertEquals(0, mock_connection.return_value.__enter__.return_value.send_messages.call_count) + self.assertEqual(0, mock_connection.return_value.__enter__.return_value.send_messages.call_count) # Verify num tries not updated actual_failed_email = Email.objects.get(exception__isnull=False, num_tries=2) - self.assertEquals(failed_email.id, actual_failed_email.id) + self.assertEqual(failed_email.id, actual_failed_email.id) @override_settings(DISABLE_DURABILITY_CHECKING=True, ENTITY_EMAILER_MAX_SEND_MESSAGE_TRIES=2) @patch.object(Event, 'render', spec_set=True) @@ -627,7 +627,7 @@ def test_send_exception_with_to_dict_method(self, mock_get_subscribed_addresses, mock_render.return_value = ('foo', 'bar',) # Verify baseline, namely that both emails are not marked as sent and that neither has an exception saved - self.assertEquals(2, Email.objects.filter(sent__isnull=True).count()) + self.assertEqual(2, Email.objects.filter(sent__isnull=True).count()) class TestEmailSendMessageException(Exception): def to_dict(self): @@ -643,11 +643,11 @@ def to_dict(self): EntityEmailerInterface.send_unsent_scheduled_emails() # Verify that only one email is marked as sent - self.assertEquals(1, Email.objects.filter(sent__isnull=False).count()) + self.assertEqual(1, Email.objects.filter(sent__isnull=False).count()) # Verify that the failed email was saved with the exception actual_failed_email = Email.objects.get(exception__isnull=False, num_tries=1) - self.assertEquals(failed_email.id, actual_failed_email.id) - self.assertEquals( + self.assertEqual(failed_email.id, actual_failed_email.id) + self.assertEqual( 'test: {}'.format(json.dumps({'message': 'test'})), actual_failed_email.exception ) From c53914136ccfc20c1cf687ff40c4648007ccf19c Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 08:55:56 -0400 Subject: [PATCH 07/15] Added github actions --- .github/workflows/tests.yml | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..6a125b4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,84 @@ +# copied from django-cte +name: entity_emailer tests +on: + push: + branches: [master] + pull_request: + branches: [master,develop] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python: ['3.7', '3.8', '3.9'] + # Time to switch to pytest or nose2?? + # nosetests is broken on 3.10 + # AttributeError: module 'collections' has no attribute 'Callable' + # https://github.com/nose-devs/nose/issues/1099 + django: + - 'Django~=3.2.0' + - 'Django~=4.0.0' + - 'Django~=4.1.0' + - 'Django~=4.2.0' + experimental: [false] +# include: +# - python: '3.9' +# django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' +# experimental: true +# # NOTE this job will appear to pass even when it fails because of +# # `continue-on-error: true`. Github Actions apparently does not +# # have this feature, similar to Travis' allow-failure, yet. +# # https://github.com/actions/toolkit/issues/399 + exclude: + - python: '3.7' + django: 'Django~=4.0.0' + - python: '3.7' + django: 'Django~=4.1.0' + - python: '3.7' + django: 'Django~=4.2.0' + services: + postgres: + image: postgres:latest + env: + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python }} + - name: Setup + run: | + python --version + pip install --upgrade pip wheel + pip install -r requirements/requirements.txt + pip install -r requirements/requirements-testing.txt + pip install "${{ matrix.django }}" + pip freeze + - name: Run tests + env: + DB_SETTINGS: >- + { + "ENGINE":"django.db.backends.postgresql_psycopg2", + "NAME":"entity_emailer", + "USER":"postgres", + "PASSWORD":"postgres", + "HOST":"localhost", + "PORT":"5432" + } + run: | + coverage run manage.py test entity_emailer + coverage report --fail-under=99 + continue-on-error: ${{ matrix.experimental }} + - name: Check style + run: flake8 entity_emailer From 2dbcac964d5fd04781135e48d075dc6b9c1384dd Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 08:56:14 -0400 Subject: [PATCH 08/15] Loosened testing requirements --- requirements/requirements-testing.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 1dd1fa8..3456aed 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,7 +1,6 @@ coverage==4.5.1 django-dynamic-fixture -django-nose==1.4.5 -flake8==3.5.0 +django-nose +flake8 freezegun==0.3.12 -mock==2.0.0 -psycopg2>=2.7.7 +psycopg2 From 474024a9917e9d7ba5e1ff4a277bf17e36a7683c Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 09:01:13 -0400 Subject: [PATCH 09/15] Settings tweak for testing --- settings.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index 08bb400..617ca85 100644 --- a/settings.py +++ b/settings.py @@ -1,4 +1,5 @@ import os +import json from django.conf import settings @@ -16,7 +17,7 @@ def configure_settings(): if test_db is None: db_config = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'ambition', 'USER': 'postgres', 'PASSWORD': '', @@ -27,13 +28,17 @@ def configure_settings(): } elif test_db == 'postgres': db_config = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'ENGINE': 'django.db.backends.postgresql', 'USER': 'postgres', 'NAME': 'entity', } else: raise RuntimeError('Unsupported test DB {0}'.format(test_db)) + # Check env for db override (used for github actions) + if os.environ.get('DB_SETTINGS'): + db_config = json.loads(os.environ.get('DB_SETTINGS')) + settings.configure( ENTITY_EMAILER_MAX_SEND_MESSAGE_TRIES=3, DATABASES={ From 62b1e8eb08a043ae3dc777887675c83f2cdc9ce6 Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 09:06:12 -0400 Subject: [PATCH 10/15] More testing requirements bumps to fix coverage with Py38 --- requirements/requirements-testing.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 3456aed..a4462b1 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -1,6 +1,6 @@ -coverage==4.5.1 +coverage<7 django-dynamic-fixture django-nose flake8 -freezegun==0.3.12 +freezegun psycopg2 From 68a39db58773751d69a0b5df11789149d5819521 Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 09:13:28 -0400 Subject: [PATCH 11/15] Bump beta version; last little tweaks --- entity_emailer/version.py | 2 +- requirements/requirements.txt | 2 +- setup.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/entity_emailer/version.py b/entity_emailer/version.py index 509f11c..eef80f0 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.1.3b1' +__version__ = '2.1.3b2' diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 4a4a735..e59af8a 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,4 @@ -Django>=2.2 +Django>=3.2 django-db-mutex>=2.0.0 django-entity>=5.0.0 diff --git a/setup.py b/setup.py index bee6cea..b40388e 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ def get_requirements(requirements_file): if not r.startswith('#') and not r.startswith('-') ] + setup( name='django-entity-emailer', version=get_version(), From 336e6be6833343d351ba8f6a5bd9ad82ed66316d Mon Sep 17 00:00:00 2001 From: Jeffrey Cross Date: Thu, 1 Jun 2023 09:14:11 -0400 Subject: [PATCH 12/15] Squashed migrations. Such clean! --- .../migrations/0001_0004_squashed.py | 37 +++++++++++++++++ entity_emailer/migrations/0001_initial.py | 41 ------------------- .../migrations/0002_auto_20170919_1653.py | 20 --------- .../migrations/0003_email_exception.py | 18 -------- .../migrations/0004_email_num_tries.py | 18 -------- 5 files changed, 37 insertions(+), 97 deletions(-) create mode 100644 entity_emailer/migrations/0001_0004_squashed.py delete mode 100644 entity_emailer/migrations/0001_initial.py delete mode 100644 entity_emailer/migrations/0002_auto_20170919_1653.py delete mode 100644 entity_emailer/migrations/0003_email_exception.py delete mode 100644 entity_emailer/migrations/0004_email_num_tries.py diff --git a/entity_emailer/migrations/0001_0004_squashed.py b/entity_emailer/migrations/0001_0004_squashed.py new file mode 100644 index 0000000..71ceffb --- /dev/null +++ b/entity_emailer/migrations/0001_0004_squashed.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.19 on 2023-06-01 13:10 + +import datetime +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + replaces = [('entity_emailer', '0001_initial'), + ('entity_emailer', '0002_auto_20170919_1653'), + ('entity_emailer', '0003_email_exception'), + ('entity_emailer', '0004_email_num_tries')] + + dependencies = [ + ('entity_event', '0001_initial'), + ('entity', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Email', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('view_uid', models.UUIDField(default=uuid.uuid4, editable=False)), + ('subject', models.CharField(max_length=256)), + ('from_address', models.CharField(default='', max_length=256)), + ('uid', models.CharField(default=None, max_length=100, null=True, unique=True)), + ('scheduled', models.DateTimeField(default=datetime.datetime.utcnow, null=True)), + ('sent', models.DateTimeField(default=None, null=True)), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entity_event.event')), + ('recipients', models.ManyToManyField(to='entity.Entity')), + ('exception', models.TextField(default=None, null=True)), + ('num_tries', models.IntegerField(default=0)), + ], + ), + ] diff --git a/entity_emailer/migrations/0001_initial.py b/entity_emailer/migrations/0001_initial.py deleted file mode 100644 index ea2e556..0000000 --- a/entity_emailer/migrations/0001_initial.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- - -from django.db import models, migrations -import django.db.models.deletion -import datetime -import uuid - - -if hasattr(models, 'UUIDField'): - uuid_field = ('view_uid', models.UUIDField(default=uuid.uuid4, editable=False)) -else: - import uuidfield.fields - uuid_field = ('view_uid', uuidfield.fields.UUIDField(editable=False, unique=True, max_length=32, blank=True)), - - -class Migration(migrations.Migration): - - dependencies = [ - ('entity_event', '0001_initial'), - ('entity', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Email', - fields=[ - ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), - uuid_field, - ('subject', models.CharField(max_length=256)), - ('from_address', models.CharField(default='', max_length=256)), - ('uid', models.CharField(null=True, default=None, unique=True, max_length=100)), - ('scheduled', models.DateTimeField(null=True, default=datetime.datetime.utcnow)), - ('sent', models.DateTimeField(null=True, default=None)), - ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='entity_event.Event')), - ('recipients', models.ManyToManyField(to='entity.Entity')), - ], - options={ - }, - bases=(models.Model,), - ), - ] diff --git a/entity_emailer/migrations/0002_auto_20170919_1653.py b/entity_emailer/migrations/0002_auto_20170919_1653.py deleted file mode 100644 index b2e6380..0000000 --- a/entity_emailer/migrations/0002_auto_20170919_1653.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.11 on 2017-09-19 16:53 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('entity_emailer', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='email', - name='view_uid', - field=models.UUIDField(default=uuid.uuid4, editable=False), - ), - ] diff --git a/entity_emailer/migrations/0003_email_exception.py b/entity_emailer/migrations/0003_email_exception.py deleted file mode 100644 index c11943e..0000000 --- a/entity_emailer/migrations/0003_email_exception.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-02-10 18:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('entity_emailer', '0002_auto_20170919_1653'), - ] - - operations = [ - migrations.AddField( - model_name='email', - name='exception', - field=models.TextField(default=None, null=True), - ), - ] diff --git a/entity_emailer/migrations/0004_email_num_tries.py b/entity_emailer/migrations/0004_email_num_tries.py deleted file mode 100644 index 1d693d6..0000000 --- a/entity_emailer/migrations/0004_email_num_tries.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.13 on 2021-03-02 21:05 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('entity_emailer', '0003_email_exception'), - ] - - operations = [ - migrations.AddField( - model_name='email', - name='num_tries', - field=models.IntegerField(default=0), - ), - ] From 5840d17c43c71b19dd96ea4ff2c93aa5db2fbea4 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Wed, 28 Jun 2023 17:11:27 -0400 Subject: [PATCH 13/15] version and notes --- entity_emailer/version.py | 2 +- release_notes.rst | 6 ++++++ setup.py | 9 +++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/entity_emailer/version.py b/entity_emailer/version.py index eef80f0..04188a1 100644 --- a/entity_emailer/version.py +++ b/entity_emailer/version.py @@ -1 +1 @@ -__version__ = '2.1.3b2' +__version__ = '2.2.0' diff --git a/release_notes.rst b/release_notes.rst index c17705e..aa6cd27 100644 --- a/release_notes.rst +++ b/release_notes.rst @@ -1,6 +1,12 @@ Release Notes ============= +v2.2.0 +------ +* Django 4.2 support +* Drop django 2 support +* Remove external uuid field dependency + v2.1.2 ------ * Fix issue for not marking email as sent when no email addresses exist diff --git a/setup.py b/setup.py index b40388e..8f012ee 100644 --- a/setup.py +++ b/setup.py @@ -47,16 +47,17 @@ def get_requirements(requirements_file): packages=find_packages(), classifiers=[ 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Framework :: Django', - 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.0', - 'Framework :: Django :: 3.1', + 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', + 'Framework :: Django :: 4.2', ], license='MIT', install_requires=get_requirements('requirements.txt'), From bcc3c4eea7e4691fdbc58781b95e00fcde2b47ef Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Wed, 28 Jun 2023 17:11:39 -0400 Subject: [PATCH 14/15] remove old uuid dep and bump versions --- requirements/requirements.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index e59af8a..9aa7c31 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,9 +1,8 @@ Django>=3.2 -django-db-mutex>=2.0.0 -django-entity>=5.0.0 -django-entity-event>=2.0.0 -ambition-django-uuidfield>=0.5.0 -ambition-utils>=2.2.0 +django-db-mutex>=3.1.0 +django-entity>=5.1.1 +django-entity-event>=2.2.1 +ambition-utils>=3.1.5 beautifulsoup4>=4.3.2 From 2cb39c17bb9c9ea622328955692d9488365df7c6 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Thu, 29 Jun 2023 12:23:38 -0400 Subject: [PATCH 15/15] bump versions --- requirements/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 9aa7c31..6cf9685 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,8 +1,8 @@ Django>=3.2 django-db-mutex>=3.1.0 -django-entity>=5.1.1 -django-entity-event>=2.2.1 -ambition-utils>=3.1.5 +django-entity>=6.1.0 +django-entity-event>=3.1.0 +ambition-utils>=3.1.6 beautifulsoup4>=4.3.2