From c1f201a60b11b98b742bbd0fd75364b643cffbcc Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 17:59:11 -0400 Subject: [PATCH 1/9] Flake8 --- postmark/models.py | 59 ++++++++++++++++++++++++++-------------------- setup.py | 22 ++++++++--------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/postmark/models.py b/postmark/models.py index 23997b6..1df7cde 100755 --- a/postmark/models.py +++ b/postmark/models.py @@ -1,10 +1,11 @@ -from django.utils.translation import ugettext_lazy as _ -from django.dispatch import receiver +import pytz + +from datetime import datetime from django.db import models +from django.dispatch import receiver +from django.utils.translation import ugettext_lazy as _ from itertools import izip_longest -from datetime import datetime from pytz import timezone -import pytz from postmark.signals import post_send @@ -37,77 +38,83 @@ ("Blocked", _("Blocked")), ) + class EmailMessage(models.Model): message_id = models.CharField(_("Message ID"), max_length=40) submitted_at = models.DateTimeField(_("Submitted At")) status = models.CharField(_("Status"), max_length=150) - + to = models.CharField(_("To"), max_length=150) to_type = models.CharField(_("Type"), max_length=3, choices=TO_CHOICES) - + sender = models.CharField(_("Sender"), max_length=150) reply_to = models.CharField(_("Reply To"), max_length=150) subject = models.CharField(_("Subject"), max_length=150) tag = models.CharField(_("Tag"), max_length=150) text_body = models.TextField(_("Text Body")) html_body = models.TextField(_("HTML Body")) - + headers = models.TextField(_("Headers")) attachments = models.TextField(_("Attachments")) - + def __unicode__(self): return u"%s" % (self.message_id,) - + class Meta: verbose_name = _("email message") verbose_name_plural = _("email messages") - + get_latest_by = "submitted_at" ordering = ["-submitted_at"] + class EmailBounce(models.Model): id = models.PositiveIntegerField(primary_key=True) - message = models.ForeignKey(EmailMessage, related_name="bounces", verbose_name=_("Message")) - + message = models.ForeignKey( + EmailMessage, related_name="bounces", verbose_name=_("Message")) + inactive = models.BooleanField(_("Inactive")) can_activate = models.BooleanField(_("Can Activate")) - + type = models.CharField(_("Type"), max_length=100, choices=BOUNCE_TYPES) description = models.TextField(_("Description")) details = models.TextField(_("Details")) - + bounced_at = models.DateTimeField(_("Bounced At")) - + def __unicode__(self): return u"Bounce: %s" % (self.message.to,) - + class Meta: verbose_name = _("email bounce") verbose_name_plural = _("email bounces") - + order_with_respect_to = "message" get_latest_by = "bounced_at" ordering = ["-bounced_at"] + @receiver(post_send) def sent_message(sender, **kwargs): msg = kwargs["message"] resp = kwargs["response"] - + for recipient in ( list(izip_longest(msg["To"].split(","), [], fillvalue='to')) + list(izip_longest(msg.get("Cc", "").split(","), [], fillvalue='cc')) + - list(izip_longest(msg.get("Bcc", "").split(","), [], fillvalue='bcc'))): - + list(izip_longest(msg.get("Bcc", "").split(","), [], fillvalue='bcc')) + ): + if not recipient[0]: continue - + timestamp, tz = resp["SubmittedAt"].rsplit("+", 1) tz_offset = int(tz.split(":", 1)[0]) - tz = timezone("Etc/GMT%s%d" % ("+" if tz_offset >= 0 else "-", tz_offset)) - submitted_at = tz.localize(datetime.strptime(timestamp[:26], POSTMARK_DATETIME_STRING)).astimezone(pytz.utc) - - + tz = timezone("Etc/GMT%s%d" % ( + "+" if tz_offset >= 0 else "-", tz_offset)) + submitted_at = tz.localize(datetime.strptime( + timestamp[:26], POSTMARK_DATETIME_STRING)).astimezone(pytz.utc) + emsg = EmailMessage( message_id=resp["MessageID"], submitted_at=submitted_at, @@ -123,4 +130,4 @@ def sent_message(sender, **kwargs): headers=msg.get("Headers", ""), attachments=msg.get("Attachments", "") ) - emsg.save() \ No newline at end of file + emsg.save() diff --git a/setup.py b/setup.py index 32eb52f..f2a85cf 100644 --- a/setup.py +++ b/setup.py @@ -1,23 +1,23 @@ from setuptools import setup setup( - name = "django-postmark", - version = __import__("postmark").__version__, - author = "Donald Stufft", - author_email = "donald@e.vilgeni.us", - description = "A Django reusable app to send email with postmark, as well as models and views to handle bounce integration.", - long_description = open("README.rst").read(), - url = "http://github.com/dstufft/django-postmark/", - license = "BSD", - install_requires = [ + name="django-postmark", + version=__import__("postmark").__version__, + author="Donald Stufft", + author_email="donald@e.vilgeni.us", + description="A Django reusable app to send email with postmark, as well as models and views to handle bounce integration.", + long_description=open("README.rst").read(), + url="http://github.com/dstufft/django-postmark/", + license="BSD", + install_requires=[ "httplib2", "pytz", ], - packages = [ + packages=[ "postmark", "postmark.migrations", ], - classifiers = [ + classifiers=[ "Development Status :: 4 - Beta", "Environment :: Web Environment", "Intended Audience :: Developers", From d271b0325f2f37b965647a3b1e94aad5e4f7f888 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:04:04 -0400 Subject: [PATCH 2/9] Added python-dateutil to setup.install_requires. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f2a85cf..de0d136 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,7 @@ install_requires=[ "httplib2", "pytz", + "python-dateutil", ], packages=[ "postmark", From a8f4393c3af2402c1fa96b15fc174be7d4e8fcb0 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:04:19 -0400 Subject: [PATCH 3/9] Using dateutil to parse datetime strings provided by Postmark as the format appears to have changed and this is just easier. --- postmark/models.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/postmark/models.py b/postmark/models.py index 1df7cde..a816dce 100755 --- a/postmark/models.py +++ b/postmark/models.py @@ -1,11 +1,8 @@ -import pytz - -from datetime import datetime +from dateutil.parser import parse as parsedatetime from django.db import models from django.dispatch import receiver from django.utils.translation import ugettext_lazy as _ from itertools import izip_longest -from pytz import timezone from postmark.signals import post_send @@ -108,12 +105,7 @@ def sent_message(sender, **kwargs): if not recipient[0]: continue - timestamp, tz = resp["SubmittedAt"].rsplit("+", 1) - tz_offset = int(tz.split(":", 1)[0]) - tz = timezone("Etc/GMT%s%d" % ( - "+" if tz_offset >= 0 else "-", tz_offset)) - submitted_at = tz.localize(datetime.strptime( - timestamp[:26], POSTMARK_DATETIME_STRING)).astimezone(pytz.utc) + submitted_at = parsedatetime(resp['SubmittedAt']) emsg = EmailMessage( message_id=resp["MessageID"], From 16ee04787c639ada1c9c84722880b5c9a83a6b20 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:11:01 -0400 Subject: [PATCH 4/9] Moved migrations to south_migrations so that we can support both South and Django migrations. --- postmark/{migrations => south_migrations}/0001_initial.py | 0 .../0002_auto__chg_field_emailmessage_tag.py | 0 postmark/{migrations => south_migrations}/__init__.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename postmark/{migrations => south_migrations}/0001_initial.py (100%) rename postmark/{migrations => south_migrations}/0002_auto__chg_field_emailmessage_tag.py (100%) rename postmark/{migrations => south_migrations}/__init__.py (100%) diff --git a/postmark/migrations/0001_initial.py b/postmark/south_migrations/0001_initial.py similarity index 100% rename from postmark/migrations/0001_initial.py rename to postmark/south_migrations/0001_initial.py diff --git a/postmark/migrations/0002_auto__chg_field_emailmessage_tag.py b/postmark/south_migrations/0002_auto__chg_field_emailmessage_tag.py similarity index 100% rename from postmark/migrations/0002_auto__chg_field_emailmessage_tag.py rename to postmark/south_migrations/0002_auto__chg_field_emailmessage_tag.py diff --git a/postmark/migrations/__init__.py b/postmark/south_migrations/__init__.py similarity index 100% rename from postmark/migrations/__init__.py rename to postmark/south_migrations/__init__.py From e4010fb0f09e53f38d6f7e542e7c3fe5354e900d Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:11:33 -0400 Subject: [PATCH 5/9] Added initial migration for Django migrations. --- postmark/migrations/0001_initial.py | 68 +++++++++++++++++++++++++++++ postmark/migrations/__init__.py | 0 2 files changed, 68 insertions(+) create mode 100644 postmark/migrations/0001_initial.py create mode 100644 postmark/migrations/__init__.py diff --git a/postmark/migrations/0001_initial.py b/postmark/migrations/0001_initial.py new file mode 100644 index 0000000..24c11ef --- /dev/null +++ b/postmark/migrations/0001_initial.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='EmailBounce', + fields=[ + ('id', models.PositiveIntegerField(serialize=False, primary_key=True)), + ('inactive', models.BooleanField(verbose_name='Inactive')), + ('can_activate', models.BooleanField(verbose_name='Can Activate')), + ('type', models.CharField(max_length=100, verbose_name='Type', choices=[(b'HardBounce', 'Hard Bounce'), (b'Transient', 'Transient'), (b'Unsubscribe', 'Unsubscribe'), (b'Subscribe', 'Subscribe'), (b'AutoResponder', 'Auto Responder'), (b'AddressChange', 'Address Change'), (b'DnsError', 'DNS Error'), (b'SpamNotification', 'Spam Notification'), (b'OpenRelayTest', 'Open Relay Test'), (b'Unknown', 'Unknown'), (b'SoftBounce', 'Soft Bounce'), (b'VirusNotification', 'Virus Notification'), (b'ChallengeVerification', 'Challenge Verification'), (b'BadEmailAddress', 'Bad Email Address'), (b'SpamComplaint', 'Spam Complaint'), (b'ManuallyDeactivated', 'Manually Deactivated'), (b'Unconfirmed', 'Unconfirmed'), (b'Blocked', 'Blocked')])), + ('description', models.TextField(verbose_name='Description')), + ('details', models.TextField(verbose_name='Details')), + ('bounced_at', models.DateTimeField(verbose_name='Bounced At')), + ], + options={ + 'ordering': ['-bounced_at'], + 'get_latest_by': 'bounced_at', + 'verbose_name': 'email bounce', + 'verbose_name_plural': 'email bounces', + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='EmailMessage', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('message_id', models.CharField(max_length=40, verbose_name='Message ID')), + ('submitted_at', models.DateTimeField(verbose_name='Submitted At')), + ('status', models.CharField(max_length=150, verbose_name='Status')), + ('to', models.CharField(max_length=150, verbose_name='To')), + ('to_type', models.CharField(max_length=3, verbose_name='Type', choices=[(b'to', 'Recipient'), (b'cc', 'Carbon Copy'), (b'bcc', 'Blind Carbon Copy')])), + ('sender', models.CharField(max_length=150, verbose_name='Sender')), + ('reply_to', models.CharField(max_length=150, verbose_name='Reply To')), + ('subject', models.CharField(max_length=150, verbose_name='Subject')), + ('tag', models.CharField(max_length=150, verbose_name='Tag')), + ('text_body', models.TextField(verbose_name='Text Body')), + ('html_body', models.TextField(verbose_name='HTML Body')), + ('headers', models.TextField(verbose_name='Headers')), + ('attachments', models.TextField(verbose_name='Attachments')), + ], + options={ + 'ordering': ['-submitted_at'], + 'get_latest_by': 'submitted_at', + 'verbose_name': 'email message', + 'verbose_name_plural': 'email messages', + }, + bases=(models.Model,), + ), + migrations.AddField( + model_name='emailbounce', + name='message', + field=models.ForeignKey(related_name='bounces', verbose_name='Message', to='postmark.EmailMessage'), + preserve_default=True, + ), + migrations.AlterOrderWithRespectTo( + name='emailbounce', + order_with_respect_to='message', + ), + ] diff --git a/postmark/migrations/__init__.py b/postmark/migrations/__init__.py new file mode 100644 index 0000000..e69de29 From 3e619186b356a36becb13e9e890221e6514df848 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:12:30 -0400 Subject: [PATCH 6/9] Set default value for BooleanFields as per Django 1.6; w/ migration. --- .../migrations/0002_auto_20141028_2212.py | 26 +++++++++++++++++++ postmark/models.py | 4 +-- 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 postmark/migrations/0002_auto_20141028_2212.py diff --git a/postmark/migrations/0002_auto_20141028_2212.py b/postmark/migrations/0002_auto_20141028_2212.py new file mode 100644 index 0000000..fbcc8be --- /dev/null +++ b/postmark/migrations/0002_auto_20141028_2212.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('postmark', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='emailbounce', + name='can_activate', + field=models.BooleanField(default=False, verbose_name='Can Activate'), + preserve_default=True, + ), + migrations.AlterField( + model_name='emailbounce', + name='inactive', + field=models.BooleanField(default=False, verbose_name='Inactive'), + preserve_default=True, + ), + ] diff --git a/postmark/models.py b/postmark/models.py index a816dce..b1eab94 100755 --- a/postmark/models.py +++ b/postmark/models.py @@ -70,8 +70,8 @@ class EmailBounce(models.Model): message = models.ForeignKey( EmailMessage, related_name="bounces", verbose_name=_("Message")) - inactive = models.BooleanField(_("Inactive")) - can_activate = models.BooleanField(_("Can Activate")) + inactive = models.BooleanField(_("Inactive"), default=False) + can_activate = models.BooleanField(_("Can Activate"), default=False) type = models.CharField(_("Type"), max_length=100, choices=BOUNCE_TYPES) description = models.TextField(_("Description")) From 4ab6f3eceaf70d7608e0ab573cd29b74b7f32631 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Tue, 28 Oct 2014 18:28:02 -0400 Subject: [PATCH 7/9] Upped version. --- postmark/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postmark/__init__.py b/postmark/__init__.py index 8b72993..48ec8dd 100755 --- a/postmark/__init__.py +++ b/postmark/__init__.py @@ -1,4 +1,4 @@ -VERSION = (0, 1, 6, "final", 0) +VERSION = (0, 2, 0, "final", 0) def get_version(): From 9c9c031ce90c144342201b5e7ac1c1be88814796 Mon Sep 17 00:00:00 2001 From: Wouter de Vries Date: Tue, 17 Nov 2015 21:34:56 +0100 Subject: [PATCH 8/9] Adds south_migrations to installed packages --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index de0d136..7ee39a0 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ packages=[ "postmark", "postmark.migrations", + "postmark.south_migrations", ], classifiers=[ "Development Status :: 4 - Beta", From 5b585deb2b82388a3e7b69eecc9a5cbca83aede2 Mon Sep 17 00:00:00 2001 From: Josh Ourisman Date: Mon, 23 Nov 2015 16:17:44 -0500 Subject: [PATCH 9/9] Upped version. --- postmark/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postmark/__init__.py b/postmark/__init__.py index 48ec8dd..49ad45d 100755 --- a/postmark/__init__.py +++ b/postmark/__init__.py @@ -1,4 +1,4 @@ -VERSION = (0, 2, 0, "final", 0) +VERSION = (0, 2, 1, "final", 0) def get_version():