refactor invoiceworker to be run by uwsgi, support graceful shutdown, and support reloading code without exiting
This commit is contained in:
parent
bd93e0c2ea
commit
4f61a95d83
65
src/shop/invoiceworker.py
Normal file
65
src/shop/invoiceworker.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
from django.core.files import File
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils import timezone
|
||||||
|
from shop.pdf import generate_pdf_letter
|
||||||
|
from shop.email import send_invoice_email, send_creditnote_email
|
||||||
|
from shop.models import Order, CustomOrder, Invoice, CreditNote
|
||||||
|
from decimal import Decimal
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def run_invoice_worker():
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# check if we need to generate any invoices for shop orders
|
||||||
|
for order in Order.objects.filter(paid=True, invoice__isnull=True):
|
||||||
|
# generate invoice for this Order
|
||||||
|
Invoice.objects.create(order=order)
|
||||||
|
logger.info('Generated Invoice object for %s' % order)
|
||||||
|
|
||||||
|
# check if we need to generate any invoices for custom orders
|
||||||
|
for customorder in CustomOrder.objects.filter(invoice__isnull=True):
|
||||||
|
# generate invoice for this CustomOrder
|
||||||
|
Invoice.objects.create(customorder=customorder)
|
||||||
|
logger.info('Generated Invoice object for %s' % customorder)
|
||||||
|
|
||||||
|
# check if we need to generate any pdf invoices
|
||||||
|
for invoice in Invoice.objects.filter(pdf=''):
|
||||||
|
# put the dict with data for the pdf together
|
||||||
|
formatdict = {
|
||||||
|
'invoice': invoice,
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate the pdf
|
||||||
|
try:
|
||||||
|
if invoice.customorder:
|
||||||
|
template='pdf/custominvoice.html'
|
||||||
|
else:
|
||||||
|
template='pdf/invoice.html'
|
||||||
|
pdffile = generate_pdf_letter(
|
||||||
|
filename=invoice.filename,
|
||||||
|
template=template,
|
||||||
|
formatdict=formatdict,
|
||||||
|
)
|
||||||
|
logger.info('Generated pdf for invoice %s' % invoice)
|
||||||
|
except Exception as E:
|
||||||
|
logger.error('ERROR: Unable to generate PDF file for invoice #%s. Error: %s' % (invoice.pk, E))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# so, do we have a pdf?
|
||||||
|
if not pdffile:
|
||||||
|
logger.error('ERROR: Unable to generate PDF file for invoice #%s' % invoice.pk)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# update invoice object with the file
|
||||||
|
invoice.pdf.save(invoice.filename, File(pdffile))
|
||||||
|
invoice.save()
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# 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=''):
|
||||||
|
# send the email
|
||||||
|
if send_invoice_email(invoice=invoice):
|
||||||
|
logger.info('OK: Invoice email sent to %s' % invoice.order.user.email)
|
||||||
|
|
|
@ -1,119 +1,46 @@
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.core.files import File
|
from shop import invoiceworker
|
||||||
from django.conf import settings
|
|
||||||
from django.utils import timezone
|
|
||||||
from shop.pdf import generate_pdf_letter
|
|
||||||
from shop.email import send_invoice_email, send_creditnote_email
|
|
||||||
from shop.models import Order, CustomOrder, Invoice, CreditNote
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from decimal import Decimal
|
import signal, sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
args = 'none'
|
args = 'none'
|
||||||
help = 'Generate invoices and credit notes, and email invoices that have not been sent yet'
|
help = 'Generate invoices and credit notes, and email invoices that have not been sent yet'
|
||||||
|
exit = False
|
||||||
|
|
||||||
def output(self, message):
|
def __init__(self):
|
||||||
self.stdout.write('%s: %s' % (timezone.now().strftime("%Y-%m-%d %H:%M:%S"), message))
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
self.logger = logging.getLogger('bornhack.%s' % __name__)
|
||||||
|
|
||||||
|
def reload_worker_code(self, signum, frame):
|
||||||
|
self.logger.info("Reloading shop.invoiceworker module...")
|
||||||
|
reload(invoiceworker)
|
||||||
|
self.logger.info("Done reloading shop.invoiceworker module")
|
||||||
|
|
||||||
|
def clean_exit(self, signum, frame):
|
||||||
|
self.logger.info("SIGTERM received, exiting gracefully soon...")
|
||||||
|
self.exit = True
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.output('Invoice worker running...')
|
self.logger.info("Connecting signals...")
|
||||||
|
signal.signal(signal.SIGHUP, self.reload_worker_code)
|
||||||
|
signal.signal(signal.SIGTERM, self.clean_exit)
|
||||||
|
signal.signal(signal.SIGINT, self.clean_exit)
|
||||||
|
|
||||||
|
self.logger.info("Entering main loop...")
|
||||||
while True:
|
while True:
|
||||||
# check if we need to generate any invoices for shop orders
|
# run invoiceworker
|
||||||
for order in Order.objects.filter(paid=True, invoice__isnull=True):
|
invoiceworker.run_invoice_worker()
|
||||||
# generate invoice for this Order
|
|
||||||
Invoice.objects.create(order=order)
|
|
||||||
self.output('Generated Invoice object for %s' % order)
|
|
||||||
|
|
||||||
# check if we need to generate any invoices for custom orders
|
# sleep for 60 seconds, but check sys.exit every second
|
||||||
for customorder in CustomOrder.objects.filter(invoice__isnull=True):
|
i = 0
|
||||||
# generate invoice for this CustomOrder
|
while i < 60:
|
||||||
Invoice.objects.create(customorder=customorder)
|
if self.exit:
|
||||||
self.output('Generated Invoice object for %s' % customorder)
|
self.logger.info("Graceful exit requested, calling sys.exit(0) now")
|
||||||
|
sys.exit(0)
|
||||||
# check if we need to generate any pdf invoices
|
|
||||||
for invoice in Invoice.objects.filter(pdf=''):
|
|
||||||
# put the dict with data for the pdf together
|
|
||||||
formatdict = {
|
|
||||||
'invoice': invoice,
|
|
||||||
}
|
|
||||||
|
|
||||||
# generate the pdf
|
|
||||||
try:
|
|
||||||
if invoice.customorder:
|
|
||||||
template='pdf/custominvoice.html'
|
|
||||||
else:
|
|
||||||
template='pdf/invoice.html'
|
|
||||||
pdffile = generate_pdf_letter(
|
|
||||||
filename=invoice.filename,
|
|
||||||
template=template,
|
|
||||||
formatdict=formatdict,
|
|
||||||
)
|
|
||||||
self.output('Generated pdf for invoice %s' % invoice)
|
|
||||||
except Exception as E:
|
|
||||||
self.output('ERROR: Unable to generate PDF file for invoice #%s. Error: %s' % (invoice.pk, E))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# so, do we have a pdf?
|
|
||||||
if not pdffile:
|
|
||||||
self.output('ERROR: Unable to generate PDF file for invoice #%s' % invoice.pk)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# update invoice object with the file
|
|
||||||
invoice.pdf.save(invoice.filename, File(pdffile))
|
|
||||||
invoice.save()
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# 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=''):
|
|
||||||
# send the email
|
|
||||||
if send_invoice_email(invoice=invoice):
|
|
||||||
self.output('OK: Invoice email sent to %s' % invoice.order.user.email)
|
|
||||||
invoice.sent_to_customer=True
|
|
||||||
invoice.save()
|
|
||||||
else:
|
else:
|
||||||
self.output('ERROR: Unable to send invoice email for order %s to %s' % (invoice.order.pk, invoice.order.user.email))
|
i += 1
|
||||||
|
sleep(1)
|
||||||
###############################################################
|
|
||||||
# check if we need to generate any pdf creditnotes?
|
|
||||||
for creditnote in CreditNote.objects.filter(pdf=''):
|
|
||||||
# put the dict with data for the pdf together
|
|
||||||
formatdict = {
|
|
||||||
'creditnote': creditnote,
|
|
||||||
}
|
|
||||||
|
|
||||||
# generate the pdf
|
|
||||||
try:
|
|
||||||
pdffile = generate_pdf_letter(
|
|
||||||
filename=creditnote.filename,
|
|
||||||
template='pdf/creditnote.html',
|
|
||||||
formatdict=formatdict,
|
|
||||||
)
|
|
||||||
self.output('Generated pdf for creditnote %s' % creditnote)
|
|
||||||
except Exception as E:
|
|
||||||
self.output('ERROR: Unable to generate PDF file for creditnote #%s. Error: %s' % (creditnote.pk, E))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# so, do we have a pdf?
|
|
||||||
if not pdffile:
|
|
||||||
self.output('ERROR: Unable to generate PDF file for creditnote #%s' % creditnote.pk)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# update creditnote object with the file
|
|
||||||
creditnote.pdf.save(creditnote.filename, File(pdffile))
|
|
||||||
creditnote.save()
|
|
||||||
|
|
||||||
###############################################################
|
|
||||||
# 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=''):
|
|
||||||
# send the email
|
|
||||||
if send_creditnote_email(creditnote=creditnote):
|
|
||||||
self.output('OK: Creditnote email sent to %s' % creditnote.user.email)
|
|
||||||
creditnote.sent_to_customer=True
|
|
||||||
creditnote.save()
|
|
||||||
else:
|
|
||||||
self.output('ERROR: Unable to send creditnote email for creditnote %s to %s' % (creditnote.pk, creditnote.user.email))
|
|
||||||
|
|
||||||
# pause for a bit
|
|
||||||
sleep(60)
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue