work on package prep

git-svn-id: https://vault.zeeksgeeks.com/svn/django_aws_ses/trunk@5 ed966f06-d3d6-432b-bc91-693151a5c6b4
This commit is contained in:
Raymond Jessop 2021-02-23 00:11:04 +00:00
parent 6329154ffa
commit 54b5c97ce9
8 changed files with 259 additions and 86 deletions

View File

@ -11,7 +11,7 @@ from .models import (
from . import settings
logger = settings.logger
#logger = settings.logger
class AwsSesSettingsAdmin(admin.ModelAdmin):
model = AwsSesSettings
@ -44,7 +44,7 @@ admin.site.register(SESStat, SESStatAdmin)
class AdminEmailListFilter(admin.SimpleListFilter):
def queryset(self, request, queryset):
logger.info('self.value(): %s' % self.value())
#logger.info('self.value(): %s' % self.value())
return queryset.filter(email__contains=self.value())
class BounceRecordAdmin(admin.ModelAdmin):

View File

@ -8,6 +8,7 @@ from django.dispatch import Signal
from datetime import datetime, timedelta
from time import sleep
import sys
from . import settings
from . import signals
@ -91,6 +92,23 @@ class SESBackend(BaseEmailBackend):
"""Sends one or more EmailMessage objects and returns the number of
email messages sent.
"""
calling_func = ''
try:
fcount = 0
while sys._getframe(fcount).f_code.co_name in ['send_messages', 'send', 'mail_admins', 'send_mail', 'emit', 'handle']:
fcount +=1
calling_func = sys._getframe(fcount).f_code.co_name
except Exception as e:
logger.info("fcount:%s, called from exception = %s" , (fcount, e))
logger.info("called from %s" , (calling_func))
logger.info("send_messages")
if not email_messages:
return
@ -188,6 +206,15 @@ class SESBackend(BaseEmailBackend):
try:
logger.info("Try to send raw email")
#logger.info('message.message().as_string() = %s' % message.message().as_string())
logger.info("source = %s" % source)
logger.info("message.from_email = %s" % self.dkim_key)
logger.info("message.recipients() = %s" % message.recipients())
logger.info("dkim_key = %s" % self.dkim_key)
logger.info("dkim_domain = %s" % self.dkim_domain)
logger.info("dkim_selector = %s" % self.dkim_selector)
logger.info("dkim_headers = %s" % str(self.dkim_headers))
response = self.connection.send_raw_email(
Source=source or message.from_email,
Destinations=message.recipients(),

View File

@ -60,7 +60,7 @@ def update_awsses_user(sender, instance, created, **kwargs):
if created:
AwsSesUserAddon.objects.create(user=instance)
try:
instance.AwsSesUserAddon.save()
instance.aws_ses.save()
except AwsSesUserAddon.DoesNotExist:
AwsSesUserAddon.objects.create(user=instance)
@ -86,7 +86,7 @@ class BounceRecord(models.Model):
reporting_mta = models.CharField(max_length=255, blank=True, null=True,)
status = models.CharField(max_length=255, blank=True, null=True,)
action = models.CharField(max_length=255, blank=True, null=True,)
feedback_id = models.CharField(max_length=255, blank=True, null=True,)
feedback_id = models.TextField(max_length=255, blank=True, null=True,)
diagnostic_code = models.CharField(max_length=255, blank=True, null=True,)
cleared = models.BooleanField(default=False)
@ -97,7 +97,7 @@ class ComplaintRecord(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
email = models.EmailField()
sub_type = models.CharField(max_length=255, blank=True, null=True,)
feedback_id = models.CharField(max_length=255, blank=True, null=True,)
feedback_id = models.TextField(max_length=255, blank=True, null=True,)
feedback_type = models.CharField(max_length=255, blank=True, null=True,)
def __str__(self):
@ -116,8 +116,8 @@ class SendRecord(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
source = models.EmailField()
destination = models.EmailField()
subject = models.CharField(max_length=255, blank=True, null=True,)
message_id = models.CharField(max_length=255, blank=True, null=True,)
subject = models.TextField(max_length=255, blank=True, null=True,)
message_id = models.TextField(max_length=255, blank=True, null=True,)
aws_process_time = models.IntegerField()
smtp_response = models.CharField(max_length=255, blank=True, null=True,)
status = models.CharField(max_length=255, blank=True, null=True,)

View File

@ -5,78 +5,79 @@ import logging
from .models import (
AwsSesSettings
)
try:
aws_ses_Settings, c = AwsSesSettings.objects.get_or_create(site_id=settings.SITE_ID)
except Exception as e:
print("AwsSesSettings does not exist: error: %s" % e)
else:
__all__ = ('ACCESS_KEY', 'SECRET_KEY', 'AWS_SES_REGION_NAME',
'AWS_SES_REGION_ENDPOINT', 'AWS_SES_AUTO_THROTTLE',
'AWS_SES_RETURN_PATH', 'DKIM_DOMAIN', 'DKIM_PRIVATE_KEY',
'DKIM_SELECTOR', 'DKIM_HEADERS', 'TIME_ZONE', 'BASE_DIR',
'BOUNCE_LIMIT','SES_BACKEND_DEBUG','SES_BACKEND_DEBUG_LOGFILE_PATH',
'SES_BACKEND_DEBUG_LOGFILE_FORMATTER')
aws_ses_Settings, c = AwsSesSettings.objects.get_or_create(site_id=settings.SITE_ID)
BASE_DIR = getattr(settings, 'BASE_DIR', None)
__all__ = ('ACCESS_KEY', 'SECRET_KEY', 'AWS_SES_REGION_NAME',
'AWS_SES_REGION_ENDPOINT', 'AWS_SES_AUTO_THROTTLE',
'AWS_SES_RETURN_PATH', 'DKIM_DOMAIN', 'DKIM_PRIVATE_KEY',
'DKIM_SELECTOR', 'DKIM_HEADERS', 'TIME_ZONE', 'BASE_DIR',
'BOUNCE_LIMIT','SES_BACKEND_DEBUG','SES_BACKEND_DEBUG_LOGFILE_PATH',
'SES_BACKEND_DEBUG_LOGFILE_FORMATTER')
if not BASE_DIR:
raise RuntimeError('No BASE_DIR defined in project settings, django_aws_ses requires BASE_DIR to be defined and pointed at your root directory. i.e. BASE_DIR = os.path.dirname(os.path.abspath(__file__))')
BASE_DIR = getattr(settings, 'BASE_DIR', None)
DEFAULT_FROM_EMAIL = getattr(settings, 'DEFAULT_FROM_EMAIL', 'no_reply@%s' % aws_ses_Settings.site.domain)
if not BASE_DIR:
raise RuntimeError('No BASE_DIR defined in project settings, django_aws_ses requires BASE_DIR to be defined and pointed at your root directory. i.e. BASE_DIR = os.path.dirname(os.path.abspath(__file__))')
HOME_URL = getattr(settings, 'HOME_URL', '')
DEFAULT_FROM_EMAIL = getattr(settings, 'DEFAULT_FROM_EMAIL', 'no_reply@%s' % aws_ses_Settings.site.domain)
UNSUBSCRIBE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/unsebscribe.html')
BASE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/base.html')
HOME_URL = getattr(settings, 'HOME_URL', '')
ACCESS_KEY = aws_ses_Settings.access_key or getattr(settings, 'AWS_SES_ACCESS_KEY_ID',getattr(settings, 'AWS_ACCESS_KEY_ID', None))
UNSUBSCRIBE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/unsebscribe.html')
BASE_TEMPLET = getattr(settings, 'UNSUBSCRIBE_TEMPLET', 'django_aws_ses/base.html')
SECRET_KEY = aws_ses_Settings.secret_key or getattr(settings, 'AWS_SES_SECRET_ACCESS_KEY',getattr(settings, 'AWS_SECRET_ACCESS_KEY', None))
ACCESS_KEY = aws_ses_Settings.access_key or getattr(settings, 'AWS_SES_ACCESS_KEY_ID',getattr(settings, 'AWS_ACCESS_KEY_ID', None))
AWS_SES_REGION_NAME = aws_ses_Settings.region_name or getattr(settings, 'AWS_SES_REGION_NAME',getattr(settings, 'AWS_DEFAULT_REGION', 'us-east-1'))
SECRET_KEY = aws_ses_Settings.secret_key or getattr(settings, 'AWS_SES_SECRET_ACCESS_KEY',getattr(settings, 'AWS_SECRET_ACCESS_KEY', None))
AWS_SES_REGION_ENDPOINT = aws_ses_Settings.region_endpoint or getattr(settings, 'AWS_SES_REGION_ENDPOINT','email.us-east-1.amazonaws.com')
AWS_SES_REGION_NAME = aws_ses_Settings.region_name or getattr(settings, 'AWS_SES_REGION_NAME',getattr(settings, 'AWS_DEFAULT_REGION', 'us-east-1'))
AWS_SES_REGION_ENDPOINT_URL = getattr(settings, 'AWS_SES_REGION_ENDPOINT_URL','https://' + AWS_SES_REGION_ENDPOINT)
AWS_SES_REGION_ENDPOINT = aws_ses_Settings.region_endpoint or getattr(settings, 'AWS_SES_REGION_ENDPOINT','email.us-east-1.amazonaws.com')
AWS_SES_AUTO_THROTTLE = getattr(settings, 'AWS_SES_AUTO_THROTTLE', 0.5)
AWS_SES_RETURN_PATH = getattr(settings, 'AWS_SES_RETURN_PATH', None)
AWS_SES_CONFIGURATION_SET = getattr(settings, 'AWS_SES_CONFIGURATION_SET', None)
AWS_SES_REGION_ENDPOINT_URL = getattr(settings, 'AWS_SES_REGION_ENDPOINT_URL','https://' + AWS_SES_REGION_ENDPOINT)
DKIM_DOMAIN = getattr(settings, "DKIM_DOMAIN", None)
DKIM_PRIVATE_KEY = getattr(settings, 'DKIM_PRIVATE_KEY', None)
DKIM_SELECTOR = getattr(settings, 'DKIM_SELECTOR', 'ses')
DKIM_HEADERS = getattr(settings, 'DKIM_HEADERS', ('From', 'To', 'Cc', 'Subject'))
AWS_SES_AUTO_THROTTLE = getattr(settings, 'AWS_SES_AUTO_THROTTLE', 0.5)
AWS_SES_RETURN_PATH = getattr(settings, 'AWS_SES_RETURN_PATH', None)
AWS_SES_CONFIGURATION_SET = getattr(settings, 'AWS_SES_CONFIGURATION_SET', None)
TIME_ZONE = settings.TIME_ZONE
DKIM_DOMAIN = getattr(settings, "DKIM_DOMAIN", None)
DKIM_PRIVATE_KEY = getattr(settings, 'DKIM_PRIVATE_KEY', None)
DKIM_SELECTOR = getattr(settings, 'DKIM_SELECTOR', 'ses')
DKIM_HEADERS = getattr(settings, 'DKIM_HEADERS',
('From', 'To', 'Cc', 'Subject'))
VERIFY_BOUNCE_SIGNATURES = getattr(settings, 'AWS_SES_VERIFY_BOUNCE_SIGNATURES', True)
TIME_ZONE = settings.TIME_ZONE
# Domains that are trusted when retrieving the certificate
# used to sign bounce messages.
BOUNCE_CERT_DOMAINS = getattr(settings, 'AWS_SNS_BOUNCE_CERT_TRUSTED_DOMAINS', (
'amazonaws.com',
'amazon.com',
))
VERIFY_BOUNCE_SIGNATURES = getattr(settings, 'AWS_SES_VERIFY_BOUNCE_SIGNATURES', True)
SES_BOUNCE_LIMIT = getattr(settings,'BOUNCE_LIMT', 1)
# Domains that are trusted when retrieving the certificate
# used to sign bounce messages.
BOUNCE_CERT_DOMAINS = getattr(settings, 'AWS_SNS_BOUNCE_CERT_TRUSTED_DOMAINS', (
'amazonaws.com',
'amazon.com',
))
SES_BACKEND_DEBUG = getattr(settings,'SES_BACKEND_DEBUG', False)
SES_BOUNCE_LIMIT = getattr(settings,'BOUNCE_LIMT', 1)
SES_BACKEND_DEBUG_LOGFILE_PATH = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_PATH', '%s/aws_ses.log' % BASE_DIR)
SES_BACKEND_DEBUG = getattr(settings,'SES_BACKEND_DEBUG', False)
SES_BACKEND_DEBUG_LOGFILE_FORMATTER = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_FORMATTER', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
SES_BACKEND_DEBUG_LOGFILE_PATH = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_PATH', '%s/aws_ses.log' % BASE_DIR)
SES_BACKEND_DEBUG_LOGFILE_FORMATTER = getattr(settings,'SES_BACKEND_DEBUG_LOGFILE_FORMATTER', '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger('django_aws_ses')
# logger.setLevel(logging.WARNING)
if SES_BACKEND_DEBUG:
logger.setLevel(logging.INFO)
# create a file handler
if SES_BACKEND_DEBUG_LOGFILE_PATH:
handler = logging.FileHandler(SES_BACKEND_DEBUG_LOGFILE_PATH)
handler.setLevel(logging.INFO)
# create a logging format
formatter = logging.Formatter(SES_BACKEND_DEBUG_LOGFILE_FORMATTER)
handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(handler)
#logger.info('something we are logging')
logger = logging.getLogger('django_aws_ses')
# logger.setLevel(logging.WARNING)
if SES_BACKEND_DEBUG:
logger.setLevel(logging.INFO)
# create a file handler
if SES_BACKEND_DEBUG_LOGFILE_PATH:
handler = logging.FileHandler(SES_BACKEND_DEBUG_LOGFILE_PATH)
handler.setLevel(logging.INFO)
# create a logging format
formatter = logging.Formatter(SES_BACKEND_DEBUG_LOGFILE_FORMATTER)
handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(handler)
#logger.info('something we are logging')

View File

@ -1,5 +1,4 @@
from django.conf.urls import include, url
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from .views import (

View File

@ -17,7 +17,6 @@ User = get_user_model()
from . import settings
from . import signals
from . import utils
from .models import (
BounceRecord,
ComplaintRecord

View File

@ -365,8 +365,8 @@ def handle_bounce(request):
except Exception as e:
logger.info("error well trying to get_or_create record: %s" % e)
logger.info(
u'Received delivery notification: messageId: %s, feedbackType: %s',
message_id, feedback_type,
u'Received delivery notification: messageId: %s',
message_id,
extra={
'notification': notification,
},
@ -375,7 +375,7 @@ def handle_bounce(request):
signals.delivery_received.send(
sender=handle_bounce,
mail_obj=mail_obj,
delivery_obj=delivery_obj,
delivery_obj=send_obj,
raw_message=raw_json,
)

147
setup.py Normal file
View File

@ -0,0 +1,147 @@
import ast
import os
import re
import sys
from fnmatch import fnmatchcase
from distutils.util import convert_path
from setuptools import setup, find_packages
def read(*path):
return open(os.path.join(os.path.abspath(os.path.dirname(__file__)),
*path)).read()
# Provided as an attribute, so you can append to these instead
# of replicating them:
standard_exclude = ["*.py", "*.pyc", "*~", ".*", "*.bak"]
standard_exclude_directories = [
".*", "CVS", "_darcs", "./build",
"./dist", "EGG-INFO", "*.egg-info"
]
# Copied from paste/util/finddata.py
def find_package_data(where=".", package="", exclude=standard_exclude,
exclude_directories=standard_exclude_directories,
only_in_packages=True, show_ignored=False):
"""
Return a dictionary suitable for use in ``package_data``
in a distutils ``setup.py`` file.
The dictionary looks like::
{"package": [files]}
Where ``files`` is a list of all the files in that package that
don't match anything in ``exclude``.
If ``only_in_packages`` is true, then top-level directories that
are not packages won't be included (but directories under packages
will).
Directories matching any pattern in ``exclude_directories`` will
be ignored; by default directories with leading ``.``, ``CVS``,
and ``_darcs`` will be ignored.
If ``show_ignored`` is true, then all the files that aren't
included in package data are shown on stderr (for debugging
purposes).
Note patterns use wildcards, or can be exact paths (including
leading ``./``), and all searching is case-insensitive.
"""
out = {}
stack = [(convert_path(where), "", package, only_in_packages)]
while stack:
where, prefix, package, only_in_packages = stack.pop(0)
for name in os.listdir(where):
fn = os.path.join(where, name)
if os.path.isdir(fn):
bad_name = False
for pattern in exclude_directories:
if (fnmatchcase(name, pattern) or
fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
sys.stderr.write("Directory %s ignored by pattern %s" % (fn, pattern))
break
if bad_name:
continue
if os.path.isfile(os.path.join(fn, "__init__.py")) \
and not prefix:
if not package:
new_package = name
else:
new_package = package + "." + name
stack.append((fn, "", new_package, False))
else:
stack.append((fn, prefix + name + "/", package,
only_in_packages))
elif package or not only_in_packages:
# is a file
bad_name = False
for pattern in exclude:
if (fnmatchcase(name, pattern) or
fn.lower() == pattern.lower()):
bad_name = True
if show_ignored:
sys.stderr.write("File %s ignored by pattern %s" % (fn, pattern))
break
if bad_name:
continue
out.setdefault(package, []).append(prefix + name)
return out
excluded_directories = standard_exclude_directories + ["example", "tests"]
package_data = find_package_data(exclude_directories=excluded_directories)
DESCRIPTION = "A Django email backend for Amazon's Simple Email Service"
LONG_DESCRIPTION = None
try:
LONG_DESCRIPTION = open('README.rst').read()
except Exception:
pass
# Parse version
_version_re = re.compile(r"VERSION\s+=\s+(.*)")
with open("django_aws_ses/__init__.py", "rb") as f:
version = ".".join(
map(str, ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1)))
)
CLASSIFIERS = [
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
'Framework :: Django',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.0',
'Framework :: Django :: 2.1',
'Framework :: Django :: 2.2',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
]
setup(
name='django_aws_ses',
version=version,
packages=find_packages(exclude=['example', 'tests']),
package_data=package_data,
python_requires='>=2.7',
author='ZeeksGeeks',
author_email='development@zeeksgeeks.com',
url='https://github.com/django-ses/django-ses',
license='MIT',
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
platforms=['any'],
classifiers=CLASSIFIERS,
install_requires=["boto3>=1.0.0", "pytz>=2016.10", "future>=0.16.0", "django>1.10"],
include_package_data=True,
extras_require={
'bounce': ['requests<3', 'M2Crypto'],
},
)