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
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/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 c38764a..0000000
--- a/entity_emailer/migrations/0001_initial.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-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 7c8cf28..0000000
--- a/entity_emailer/migrations/0002_auto_20170919_1653.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- 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
-
-
-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),
- ),
- ]
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/interface_tests.py b/entity_emailer/tests/interface_tests.py
index eda7dde..ff5ef27 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
@@ -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
)
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/entity_emailer/version.py b/entity_emailer/version.py
index f811561..04188a1 100644
--- a/entity_emailer/version.py
+++ b/entity_emailer/version.py
@@ -1 +1 @@
-__version__ = '2.1.2'
+__version__ = '2.2.0'
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
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/requirements/requirements-testing.txt b/requirements/requirements-testing.txt
index 1dd1fa8..a4462b1 100644
--- a/requirements/requirements-testing.txt
+++ b/requirements/requirements-testing.txt
@@ -1,7 +1,6 @@
-coverage==4.5.1
+coverage<7
django-dynamic-fixture
-django-nose==1.4.5
-flake8==3.5.0
-freezegun==0.3.12
-mock==2.0.0
-psycopg2>=2.7.7
+django-nose
+flake8
+freezegun
+psycopg2
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 4a4a735..6cf9685 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -1,9 +1,8 @@
-Django>=2.2
+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>=6.1.0
+django-entity-event>=3.1.0
+ambition-utils>=3.1.6
beautifulsoup4>=4.3.2
diff --git a/settings.py b/settings.py
index 18ffa74..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={
@@ -65,10 +70,13 @@ 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',
+ SECRET_KEY='*',
)
diff --git a/setup.py b/setup.py
index bee6cea..8f012ee 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(),
@@ -46,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'),