Merge pull request #124 from bornhack/emailworker
add emailworker for async emails
This commit is contained in:
commit
469b708782
|
@ -1,9 +1,9 @@
|
||||||
from utils.email import _send_email
|
from utils.email import add_outgoing_email
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
def send_creditnote_email(creditnote):
|
def add_creditnote_email(creditnote):
|
||||||
# put formatdict together
|
# put formatdict together
|
||||||
formatdict = {
|
formatdict = {
|
||||||
'creditnote': creditnote,
|
'creditnote': creditnote,
|
||||||
|
@ -11,11 +11,11 @@ def send_creditnote_email(creditnote):
|
||||||
|
|
||||||
subject = 'BornHack creditnote %s' % creditnote.pk
|
subject = 'BornHack creditnote %s' % creditnote.pk
|
||||||
|
|
||||||
# send mail
|
# add email to outgoing email queue
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template='emails/creditnote_email.txt',
|
text_template='emails/creditnote_email.txt',
|
||||||
html_template='emails/creditnote_email.html',
|
html_template='emails/creditnote_email.html',
|
||||||
recipient=creditnote.user.email,
|
to_recipients=creditnote.user.email,
|
||||||
formatdict=formatdict,
|
formatdict=formatdict,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
attachment=creditnote.pdf.read(),
|
attachment=creditnote.pdf.read(),
|
||||||
|
@ -23,7 +23,7 @@ def send_creditnote_email(creditnote):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def send_invoice_email(invoice):
|
def add_invoice_email(invoice):
|
||||||
# put formatdict together
|
# put formatdict together
|
||||||
formatdict = {
|
formatdict = {
|
||||||
'ordernumber': invoice.order.pk,
|
'ordernumber': invoice.order.pk,
|
||||||
|
@ -33,11 +33,11 @@ def send_invoice_email(invoice):
|
||||||
|
|
||||||
subject = 'BornHack invoice %s' % invoice.pk
|
subject = 'BornHack invoice %s' % invoice.pk
|
||||||
|
|
||||||
# send mail
|
# add email to outgoing email queue
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template='emails/invoice_email.txt',
|
text_template='emails/invoice_email.txt',
|
||||||
html_template='emails/invoice_email.html',
|
html_template='emails/invoice_email.html',
|
||||||
recipient=invoice.order.user.email,
|
to_recipients=invoice.order.user.email,
|
||||||
formatdict=formatdict,
|
formatdict=formatdict,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
attachment=invoice.pdf.read(),
|
attachment=invoice.pdf.read(),
|
||||||
|
@ -45,9 +45,9 @@ def send_invoice_email(invoice):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def send_test_email(recipient):
|
def add_test_email(recipient):
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template='emails/testmail.txt',
|
text_template='emails/testmail.txt',
|
||||||
recipient=recipient,
|
to_recipients=recipient,
|
||||||
subject='testmail from bornhack website'
|
subject='testmail from bornhack website'
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.utils import timezone
|
|
||||||
from shop.pdf import generate_pdf_letter
|
from shop.pdf import generate_pdf_letter
|
||||||
from shop.email import send_invoice_email, send_creditnote_email
|
from shop.email import add_invoice_email, add_creditnote_email
|
||||||
from shop.models import Order, CustomOrder, Invoice, CreditNote
|
from shop.models import Order, CustomOrder, Invoice, CreditNote
|
||||||
from decimal import Decimal
|
import logging
|
||||||
import logging, importlib
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger('bornhack.%s' % __name__)
|
logger = logging.getLogger('bornhack.%s' % __name__)
|
||||||
|
|
||||||
|
@ -16,31 +14,26 @@ def do_work():
|
||||||
that have no PDF. It also emails invoices for shop orders.
|
that have no PDF. It also emails invoices for shop orders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to generate any invoices for shop orders
|
# check if we need to generate any invoices for shop orders
|
||||||
for order in Order.objects.filter(paid=True, invoice__isnull=True):
|
for order in Order.objects.filter(paid=True, invoice__isnull=True):
|
||||||
# generate invoice for this Order
|
# generate invoice for this Order
|
||||||
Invoice.objects.create(order=order)
|
Invoice.objects.create(order=order)
|
||||||
logger.info('Generated Invoice object for %s' % order)
|
logger.info('Generated Invoice object for %s' % order)
|
||||||
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to generate any invoices for custom orders
|
# check if we need to generate any invoices for custom orders
|
||||||
for customorder in CustomOrder.objects.filter(invoice__isnull=True):
|
for customorder in CustomOrder.objects.filter(invoice__isnull=True):
|
||||||
# generate invoice for this CustomOrder
|
# generate invoice for this CustomOrder
|
||||||
Invoice.objects.create(customorder=customorder)
|
Invoice.objects.create(customorder=customorder)
|
||||||
logger.info('Generated Invoice object for %s' % customorder)
|
logger.info('Generated Invoice object for %s' % customorder)
|
||||||
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to generate any pdf invoices
|
# check if we need to generate any pdf invoices
|
||||||
for invoice in Invoice.objects.filter(pdf=''):
|
for invoice in Invoice.objects.filter(pdf=''):
|
||||||
# generate the pdf
|
# generate the pdf
|
||||||
try:
|
try:
|
||||||
if invoice.customorder:
|
if invoice.customorder:
|
||||||
template='pdf/custominvoice.html'
|
template = 'pdf/custominvoice.html'
|
||||||
else:
|
else:
|
||||||
template='pdf/invoice.html'
|
template = 'pdf/invoice.html'
|
||||||
pdffile = generate_pdf_letter(
|
pdffile = generate_pdf_letter(
|
||||||
filename=invoice.filename,
|
filename=invoice.filename,
|
||||||
template=template,
|
template=template,
|
||||||
|
@ -57,21 +50,23 @@ def do_work():
|
||||||
invoice.pdf.save(invoice.filename, File(pdffile))
|
invoice.pdf.save(invoice.filename, File(pdffile))
|
||||||
invoice.save()
|
invoice.save()
|
||||||
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to send out any invoices (only for shop orders, and only where pdf has been generated)
|
# check if we need to send out any invoices (only for shop orders, and only where pdf has been generated)
|
||||||
for invoice in Invoice.objects.filter(order__isnull=False, sent_to_customer=False).exclude(pdf=''):
|
for invoice in Invoice.objects.filter(order__isnull=False, sent_to_customer=False).exclude(pdf=''):
|
||||||
logger.info("found unmailed Invoice object: %s" % invoice)
|
logger.info("found unmailed Invoice object: %s" % invoice)
|
||||||
# send the email
|
# add email to the outgoing email queue
|
||||||
if send_invoice_email(invoice=invoice):
|
if add_invoice_email(invoice=invoice):
|
||||||
invoice.sent_to_customer=True
|
invoice.sent_to_customer = True
|
||||||
invoice.save()
|
invoice.save()
|
||||||
logger.info('OK: Invoice email sent to %s' % invoice.order.user.email)
|
logger.info('OK: Invoice email to {} added to queue.'.format(
|
||||||
|
invoice.order.user.email)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.error('Unable to send invoice email for order %s to %s' % (invoice.order.pk, invoice.order.user.email))
|
logger.error('Unable to add email for invoice {} to {}'.format(
|
||||||
|
invoice.pk,
|
||||||
|
invoice.order.user.email
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to generate any pdf creditnotes?
|
# check if we need to generate any pdf creditnotes?
|
||||||
for creditnote in CreditNote.objects.filter(pdf=''):
|
for creditnote in CreditNote.objects.filter(pdf=''):
|
||||||
# generate the pdf
|
# generate the pdf
|
||||||
|
@ -92,15 +87,13 @@ def do_work():
|
||||||
creditnote.pdf.save(creditnote.filename, File(pdffile))
|
creditnote.pdf.save(creditnote.filename, File(pdffile))
|
||||||
creditnote.save()
|
creditnote.save()
|
||||||
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# check if we need to send out any creditnotes (only where pdf has been generated)
|
# check if we need to send out any creditnotes (only where pdf has been generated)
|
||||||
for creditnote in CreditNote.objects.filter(sent_to_customer=False).exclude(pdf=''):
|
for creditnote in CreditNote.objects.filter(sent_to_customer=False).exclude(pdf=''):
|
||||||
# send the email
|
# send the email
|
||||||
if send_creditnote_email(creditnote=creditnote):
|
if add_creditnote_email(creditnote=creditnote):
|
||||||
logger.info('OK: Creditnote email sent to %s' % creditnote.user.email)
|
logger.info('OK: Creditnote email to %s added' % creditnote.user.email)
|
||||||
creditnote.sent_to_customer=True
|
creditnote.sent_to_customer = True
|
||||||
creditnote.save()
|
creditnote.save()
|
||||||
else:
|
else:
|
||||||
logger.error('Unable to send creditnote email for creditnote %s to %s' % (creditnote.pk, creditnote.user.email))
|
logger.error('Unable to add creditnote email for creditnote %s to %s' % (creditnote.pk, creditnote.user.email))
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,9 @@ from wkhtmltopdf.views import PDFTemplateResponse
|
||||||
from PyPDF2 import PdfFileWriter, PdfFileReader
|
from PyPDF2 import PdfFileWriter, PdfFileReader
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
import io, logging
|
import logging
|
||||||
|
import io
|
||||||
|
import os
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,10 +15,10 @@ def generate_pdf_letter(filename, template, formatdict):
|
||||||
request.user = AnonymousUser()
|
request.user = AnonymousUser()
|
||||||
request.session = {}
|
request.session = {}
|
||||||
|
|
||||||
### produce text-only PDF from template
|
# produce text-only PDF from template
|
||||||
pdfgenerator = PDFTemplateResponse(
|
pdfgenerator = PDFTemplateResponse(
|
||||||
request=request,
|
request=request,
|
||||||
template=template,
|
template=template,
|
||||||
context=formatdict,
|
context=formatdict,
|
||||||
cmd_options={
|
cmd_options={
|
||||||
'margin-top': 50,
|
'margin-top': 50,
|
||||||
|
@ -26,33 +28,35 @@ def generate_pdf_letter(filename, template, formatdict):
|
||||||
textonlypdf = io.BytesIO()
|
textonlypdf = io.BytesIO()
|
||||||
textonlypdf.write(pdfgenerator.rendered_content)
|
textonlypdf.write(pdfgenerator.rendered_content)
|
||||||
|
|
||||||
### create a blank pdf to work with
|
# create a blank pdf to work with
|
||||||
finalpdf = PdfFileWriter()
|
finalpdf = PdfFileWriter()
|
||||||
|
|
||||||
### open the text-only pdf
|
# open the text-only pdf
|
||||||
pdfreader = PdfFileReader(textonlypdf)
|
pdfreader = PdfFileReader(textonlypdf)
|
||||||
|
|
||||||
### get watermark from watermark file
|
# get watermark from watermark file
|
||||||
watermark = PdfFileReader(open("%s/pdf/%s" % (settings.STATICFILES_DIRS[0], settings.PDF_LETTERHEAD_FILENAME), 'rb'))
|
watermark = PdfFileReader(
|
||||||
|
open(os.path.join(settings.STATICFILES_DIRS[0], 'pdf', settings.PDF_LETTERHEAD_FILENAME), 'rb')
|
||||||
|
)
|
||||||
|
|
||||||
### add the watermark to all pages
|
# add the watermark to all pages
|
||||||
for pagenum in range(pdfreader.getNumPages()):
|
for pagenum in range(pdfreader.getNumPages()):
|
||||||
page = watermark.getPage(0)
|
page = watermark.getPage(0)
|
||||||
try:
|
try:
|
||||||
page.mergePage(pdfreader.getPage(pagenum))
|
page.mergePage(pdfreader.getPage(pagenum))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
### watermark pdf might be broken?
|
# watermark pdf might be broken?
|
||||||
return False
|
return False
|
||||||
### add page to output
|
# add page to output
|
||||||
finalpdf.addPage(page)
|
finalpdf.addPage(page)
|
||||||
|
|
||||||
### save the generated pdf to the archive
|
# save the generated pdf to the archive
|
||||||
fullpath = settings.PDF_ARCHIVE_PATH+filename
|
fullpath = os.path.join(settings.PDF_ARCHIVE_PATH, filename)
|
||||||
with open(fullpath, 'wb') as fh:
|
with open(fullpath, 'wb') as fh:
|
||||||
finalpdf.write(fh)
|
finalpdf.write(fh)
|
||||||
logger.info('Saved pdf to archive: %s' % fullpath)
|
logger.info('Saved pdf to archive: %s' % fullpath)
|
||||||
|
|
||||||
### return a file object with the data
|
# return a file object with the data
|
||||||
returnfile = io.BytesIO()
|
returnfile = io.BytesIO()
|
||||||
finalpdf.write(returnfile)
|
finalpdf.write(returnfile)
|
||||||
return returnfile
|
return returnfile
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import Team, TeamArea, TeamMember
|
from .models import Team, TeamArea, TeamMember
|
||||||
from .email import send_add_membership_email, send_remove_membership_email
|
from .email import add_added_membership_email, add_removed_membership_email
|
||||||
|
|
||||||
admin.site.register(TeamArea)
|
admin.site.register(TeamArea)
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class TeamMemberAdmin(admin.ModelAdmin):
|
||||||
membership.approved = True
|
membership.approved = True
|
||||||
membership.save()
|
membership.save()
|
||||||
updated += 1
|
updated += 1
|
||||||
send_add_membership_email(membership)
|
add_added_membership_email(membership)
|
||||||
|
|
||||||
self.message_user(
|
self.message_user(
|
||||||
request,
|
request,
|
||||||
|
@ -57,7 +57,7 @@ class TeamMemberAdmin(admin.ModelAdmin):
|
||||||
updated = 0
|
updated = 0
|
||||||
|
|
||||||
for membership in queryset:
|
for membership in queryset:
|
||||||
send_remove_membership_email(membership)
|
add_removed_membership_email(membership)
|
||||||
membership.delete()
|
membership.delete()
|
||||||
updated += 1
|
updated += 1
|
||||||
|
|
||||||
|
@ -69,4 +69,3 @@ class TeamMemberAdmin(admin.ModelAdmin):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
remove_member.description = 'Remove a user from the team.'
|
remove_member.description = 'Remove a user from the team.'
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
from utils.email import _send_email
|
from utils.email import add_outgoing_email
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
def send_add_membership_email(membership):
|
def add_added_membership_email(membership):
|
||||||
formatdict = {
|
formatdict = {
|
||||||
'team': membership.team.name,
|
'team': membership.team.name,
|
||||||
'camp': membership.team.camp.title
|
'camp': membership.team.camp.title
|
||||||
}
|
}
|
||||||
|
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template='emails/add_membership_email.txt',
|
text_template='emails/add_membership_email.txt',
|
||||||
html_template='emails/add_membership_email.html',
|
html_template='emails/add_membership_email.html',
|
||||||
recipient=membership.user.email,
|
to_recipients=membership.user.email,
|
||||||
formatdict=formatdict,
|
formatdict=formatdict,
|
||||||
subject='Team update from {}'.format(membership.team.camp.title)
|
subject='Team update from {}'.format(membership.team.camp.title)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def send_remove_membership_email(membership):
|
def add_removed_membership_email(membership):
|
||||||
formatdict = {
|
formatdict = {
|
||||||
'team': membership.team.name,
|
'team': membership.team.name,
|
||||||
'camp': membership.team.camp.title
|
'camp': membership.team.camp.title
|
||||||
|
@ -31,25 +31,25 @@ def send_remove_membership_email(membership):
|
||||||
text_template = 'emails/unapproved_membership_email.txt',
|
text_template = 'emails/unapproved_membership_email.txt',
|
||||||
html_template = 'emails/unapproved_membership_email.html'
|
html_template = 'emails/unapproved_membership_email.html'
|
||||||
|
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template=text_template,
|
text_template=text_template,
|
||||||
html_template=html_template,
|
html_template=html_template,
|
||||||
recipient=membership.user.email,
|
to_recipients=membership.user.email,
|
||||||
formatdict=formatdict,
|
formatdict=formatdict,
|
||||||
subject='Team update from {}'.format(membership.team.camp.title)
|
subject='Team update from {}'.format(membership.team.camp.title)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def send_new_membership_email(membership):
|
def add_new_membership_email(membership):
|
||||||
formatdict = {
|
formatdict = {
|
||||||
'team': membership.team.name,
|
'team': membership.team.name,
|
||||||
'camp': membership.team.camp.title
|
'camp': membership.team.camp.title
|
||||||
}
|
}
|
||||||
|
|
||||||
return _send_email(
|
return add_outgoing_email(
|
||||||
text_template='emails/new_membership_email.txt',
|
text_template='emails/new_membership_email.txt',
|
||||||
html_template='emails/new_membership_email.html',
|
html_template='emails/new_membership_email.html',
|
||||||
recipient=[resp.email for resp in membership.team.responsible],
|
to_recipients=[resp.email for resp in membership.team.responsible],
|
||||||
formatdict=formatdict,
|
formatdict=formatdict,
|
||||||
subject='New membership request for {} at {}'.format(
|
subject='New membership request for {} at {}'.format(
|
||||||
membership.team.name,
|
membership.team.name,
|
||||||
|
|
|
@ -3,9 +3,11 @@ from django.db.models.signals import post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from utils.models import CampRelatedModel
|
from utils.models import CampRelatedModel
|
||||||
from .email import send_new_membership_email
|
from .email import add_new_membership_email
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
class TeamArea(CampRelatedModel):
|
class TeamArea(CampRelatedModel):
|
||||||
|
@ -96,6 +98,7 @@ class TeamMember(models.Model):
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=TeamMember)
|
@receiver(post_save, sender=TeamMember)
|
||||||
def send_responsible_email(sender, instance, created, **kwargs):
|
def add_responsible_email(sender, instance, created, **kwargs):
|
||||||
if created:
|
if created:
|
||||||
send_new_membership_email(instance)
|
if not add_new_membership_email(instance):
|
||||||
|
logger.error('Error adding email to outgoing queue')
|
||||||
|
|
8
src/utils/admin.py
Normal file
8
src/utils/admin.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import OutgoingEmail
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(OutgoingEmail)
|
||||||
|
class OutgoingEmailAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
|
@ -1,49 +1,105 @@
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
|
from django.core.validators import validate_email
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
|
from .models import OutgoingEmail
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("bornhack.%s" % __name__)
|
logger = logging.getLogger("bornhack.%s" % __name__)
|
||||||
|
|
||||||
|
|
||||||
def _send_email(
|
def _send_email(
|
||||||
text_template,
|
text_template,
|
||||||
recipient,
|
|
||||||
formatdict,
|
|
||||||
subject,
|
subject,
|
||||||
html_template=None,
|
to_recipients=[],
|
||||||
|
cc_recipients=[],
|
||||||
|
bcc_recipients=[],
|
||||||
|
html_template='',
|
||||||
sender='BornHack <info@bornhack.dk>',
|
sender='BornHack <info@bornhack.dk>',
|
||||||
attachment=None,
|
attachment=None,
|
||||||
attachment_filename=None
|
attachment_filename=''
|
||||||
):
|
):
|
||||||
if not isinstance(recipient, list):
|
if not isinstance(to_recipients, list):
|
||||||
recipient = [recipient]
|
to_recipients = [to_recipients]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# put the basic email together
|
# put the basic email together
|
||||||
msg = EmailMultiAlternatives(
|
msg = EmailMultiAlternatives(
|
||||||
subject,
|
subject,
|
||||||
render_to_string(text_template, formatdict),
|
text_template,
|
||||||
sender,
|
sender,
|
||||||
recipient,
|
to_recipients,
|
||||||
[settings.ARCHIVE_EMAIL]
|
bcc_recipients + [settings.ARCHIVE_EMAIL] if bcc_recipients else [settings.ARCHIVE_EMAIL],
|
||||||
|
cc_recipients
|
||||||
)
|
)
|
||||||
|
|
||||||
# is there a html version of this email?
|
# is there a html version of this email?
|
||||||
if html_template:
|
if html_template:
|
||||||
msg.attach_alternative(
|
msg.attach_alternative(
|
||||||
render_to_string(html_template, formatdict),
|
html_template,
|
||||||
'text/html'
|
'text/html'
|
||||||
)
|
)
|
||||||
|
|
||||||
# is there a pdf attachment to this mail?
|
# is there a pdf attachment to this mail?
|
||||||
if attachment:
|
if attachment:
|
||||||
msg.attach(attachment_filename, attachment, 'application/pdf')
|
msg.attach(attachment_filename, attachment, 'application/pdf')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception('exception while rendering email: {}'.format(e))
|
logger.exception('exception while rendering email: {}'.format(e))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# send the email
|
# send the email
|
||||||
msg.send()
|
try:
|
||||||
|
msg.send(fail_silently=False)
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception('exception while sending email: {}'.format(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def add_outgoing_email(
|
||||||
|
text_template,
|
||||||
|
formatdict,
|
||||||
|
subject,
|
||||||
|
to_recipients=[],
|
||||||
|
cc_recipients=[],
|
||||||
|
bcc_recipients=[],
|
||||||
|
html_template='',
|
||||||
|
sender='BornHack <info@bornhack.dk>',
|
||||||
|
attachment=None,
|
||||||
|
attachment_filename=''
|
||||||
|
):
|
||||||
|
""" adds an email to the outgoing queue
|
||||||
|
recipients is a list of to recipients
|
||||||
|
"""
|
||||||
|
text_template = render_to_string(text_template, formatdict)
|
||||||
|
|
||||||
|
if html_template:
|
||||||
|
html_template = render_to_string(html_template, formatdict)
|
||||||
|
|
||||||
|
if not isinstance(to_recipients, list):
|
||||||
|
to_recipients = [to_recipients]
|
||||||
|
|
||||||
|
for recipient in to_recipients:
|
||||||
|
try:
|
||||||
|
validate_email(recipient)
|
||||||
|
except ValidationError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
email = OutgoingEmail.objects.create(
|
||||||
|
text_template=text_template,
|
||||||
|
html_template=html_template,
|
||||||
|
subject=subject,
|
||||||
|
sender=sender,
|
||||||
|
to_recipients=to_recipients,
|
||||||
|
cc_recipients=cc_recipients,
|
||||||
|
bcc_recipients=bcc_recipients
|
||||||
|
)
|
||||||
|
|
||||||
|
if attachment:
|
||||||
|
django_file = ContentFile(attachment)
|
||||||
|
django_file.name = attachment_filename
|
||||||
|
email.attachment.save(attachment_filename, django_file, save=True)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
34
src/utils/migrations/0001_initial.py
Normal file
34
src/utils/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2017-04-30 12:18
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OutgoingEmail',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('subject', models.CharField(max_length=500)),
|
||||||
|
('text_template', models.TextField()),
|
||||||
|
('html_template', models.TextField(blank=True)),
|
||||||
|
('recipient', models.CharField(max_length=500)),
|
||||||
|
('sender', models.CharField(max_length=500)),
|
||||||
|
('attachment', models.FileField(blank=True, upload_to='')),
|
||||||
|
('processed', models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
19
src/utils/migrations/0002_remove_outgoingemail_recipient.py
Normal file
19
src/utils/migrations/0002_remove_outgoingemail_recipient.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2017-05-21 16:08
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('utils', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='outgoingemail',
|
||||||
|
name='recipient',
|
||||||
|
),
|
||||||
|
]
|
31
src/utils/migrations/0003_auto_20170521_1932.py
Normal file
31
src/utils/migrations/0003_auto_20170521_1932.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.5 on 2017-05-21 17:32
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('utils', '0002_remove_outgoingemail_recipient'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='outgoingemail',
|
||||||
|
name='bcc_recipients',
|
||||||
|
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=500), blank=True, null=True, size=None),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='outgoingemail',
|
||||||
|
name='cc_recipients',
|
||||||
|
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=500), blank=True, null=True, size=None),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='outgoingemail',
|
||||||
|
name='to_recipients',
|
||||||
|
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=500), blank=True, null=True, size=None),
|
||||||
|
),
|
||||||
|
]
|
0
src/utils/migrations/__init__.py
Normal file
0
src/utils/migrations/__init__.py
Normal file
|
@ -1,4 +1,5 @@
|
||||||
import uuid
|
import uuid
|
||||||
|
from django.contrib.postgres.fields import ArrayField
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -68,3 +69,38 @@ class CampRelatedModel(CreatedUpdatedModel):
|
||||||
raise ValidationError('This camp is in read only mode.')
|
raise ValidationError('This camp is in read only mode.')
|
||||||
|
|
||||||
super().delete(**kwargs)
|
super().delete(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class OutgoingEmail(CreatedUpdatedModel):
|
||||||
|
subject = models.CharField(max_length=500)
|
||||||
|
text_template = models.TextField()
|
||||||
|
html_template = models.TextField(blank=True)
|
||||||
|
sender = models.CharField(max_length=500)
|
||||||
|
to_recipients = ArrayField(
|
||||||
|
models.CharField(max_length=500, blank=True),
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
cc_recipients = ArrayField(
|
||||||
|
models.CharField(max_length=500, blank=True),
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
bcc_recipients = ArrayField(
|
||||||
|
models.CharField(max_length=500, blank=True),
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
attachment = models.FileField(blank=True)
|
||||||
|
processed = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'OutgoingEmail Object id: {} '.format(self.id)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
if not self.to_recipients \
|
||||||
|
and not self.bcc_recipients \
|
||||||
|
and not self.cc_recipients:
|
||||||
|
raise ValidationError(
|
||||||
|
{'recipient': 'either to_recipient, bcc_recipient or cc_recipient required.'}
|
||||||
|
)
|
||||||
|
|
43
src/utils/outgoingemailworker.py
Normal file
43
src/utils/outgoingemailworker.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
from .models import OutgoingEmail
|
||||||
|
from .email import _send_email
|
||||||
|
import logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger('bornhack.%s' % __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def do_work():
|
||||||
|
"""
|
||||||
|
The outgoing email worker sends emails added to the OutgoingEmail
|
||||||
|
queue.
|
||||||
|
"""
|
||||||
|
not_processed_email = OutgoingEmail.objects.filter(processed=False)
|
||||||
|
|
||||||
|
if len(not_processed_email) > 0:
|
||||||
|
logger.debug('about to process {} emails'.format(
|
||||||
|
len(not_processed_email))
|
||||||
|
)
|
||||||
|
|
||||||
|
for email in not_processed_email:
|
||||||
|
|
||||||
|
attachment = None
|
||||||
|
attachment_filename = ''
|
||||||
|
if email.attachment:
|
||||||
|
attachment = email.attachment.read()
|
||||||
|
attachment_filename = email.attachment.name
|
||||||
|
|
||||||
|
mail_send_success = _send_email(
|
||||||
|
text_template=email.text_template,
|
||||||
|
to_recipients=email.to_recipients,
|
||||||
|
subject=email.subject,
|
||||||
|
cc_recipients=email.cc_recipients,
|
||||||
|
bcc_recipients=email.bcc_recipients,
|
||||||
|
html_template=email.html_template,
|
||||||
|
attachment=attachment,
|
||||||
|
attachment_filename=attachment_filename
|
||||||
|
)
|
||||||
|
if mail_send_success:
|
||||||
|
email.processed = True
|
||||||
|
email.save()
|
||||||
|
logger.debug('successfully sent {}'.format(email))
|
||||||
|
else:
|
||||||
|
logger.error('unable to sent {}'.format(email))
|
Loading…
Reference in a new issue