Changeset - f77e1498f128
[Not reviewed]
Sachi King - 7 years ago 2017-12-27 11:42:15
nakato@nakato.io
Mgmt command for boarding pass mailout

Slowly but surely sends boarding passes out.

Has template for html and txt sepearte so we can try to direct users to
HTML to get the base64 image.

We may consider adding the image as an attachment as well as the in-line
to allow it to be opened in plain-text emails, but I don't know if
that's desired.

Just a base, templates will need to be spell checked and spruced up
before sendout. But this will provide the tool to send the boarding
pass out.

Now we need a UI for front-desk to use.
7 files changed with 101 insertions and 0 deletions:
0 comments (0 inline, 0 general)
vendor/registrasion/registrasion/management/commands/send_boarding_pass_mailout.py
Show inline comments
 
new file 100644
 
import base64
 
import io
 

	
 
from django.conf import settings
 
from django.core.management.base import BaseCommand
 
from django.core.mail import EmailMultiAlternatives
 
from django.template.loader import render_to_string
 
import pyqrcode
 

	
 
from registrasion.models import commerce
 

	
 
class Command(BaseCommand):
 

	
 
    def handle(self, *args, **options):
 
        try:
 
            bcc_email = settings.ENVELOPE_BCC_LIST
 
        except AttributeError:
 
            bcc_email = None
 

	
 
        invoices = commerce.Invoice.objects.filter(
 
            user__attendee__boarding_pass_sent=False,
 
            status=commerce.Cart.STATUS_PAID,
 
        ).order_by("-issue_time")
 

	
 
        for invoice in invoices:
 
            access_code = invoice.user.attendee.access_code
 

	
 
            acqr = pyqrcode.create(access_code)
 
            acqr_png = io.BytesIO()
 
            acqr.png(acqr_png, scale=6)
 
            acqr_png.seek(0)
 

	
 
            img = acqr_png.read()
 
            b64image = base64.b64encode(img).decode('UTF-8')
 

	
 
            context = {
 
                "qr_code_string": b64image,
 
                "access_code": access_code,
 
            }
 
            subject_path = 'registrasion/emails/boarding_pass/subject.txt'
 
            html_path = 'registrasion/emails/boarding_pass/message.html'
 
            txt_path = 'registrasion/emails/boarding_pass/message.txt'
 
            subject = "[linux.conf.au] %s" % (
 
                render_to_string(subject_path, {}).strip()
 
            )
 
            message_html = render_to_string(html_path, context)
 
            message_txt = render_to_string(txt_path, context)
 

	
 
            msg = EmailMultiAlternatives(
 
                subject,
 
                message_txt,
 
                settings.DEFAULT_FROM_EMAIL,
 
                [invoice.user.email],
 
                bcc=bcc_email,
 
            )
 
            msg.attach_alternative(message_html, "text/html")
 

	
 
            msg.send()
 

	
 
            invoice.user.attendee.boarding_pass_sent = True
 
            invoice.user.attendee.save()
vendor/registrasion/registrasion/migrations/0009_attendee_boarding_pass_sent.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8 -*-
 
# Generated by Django 1.11.8 on 2017-12-27 05:05
 
from __future__ import unicode_literals
 

	
 
from django.db import migrations, models
 

	
 

	
 
class Migration(migrations.Migration):
 

	
 
    dependencies = [
 
        ('registrasion', '0008_auto_20170930_1843'),
 
    ]
 

	
 
    operations = [
 
        migrations.AddField(
 
            model_name='attendee',
 
            name='boarding_pass_sent',
 
            field=models.BooleanField(default=False),
 
        ),
 
    ]
vendor/registrasion/registrasion/models/people.py
Show inline comments
 
from registrasion import util
 

	
 
from django.contrib.auth.models import User
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.db import models
 
from django.utils.encoding import python_2_unicode_compatible
 
from model_utils.managers import InheritanceManager
 

	
 

	
 
# User models
 

	
 
@python_2_unicode_compatible
 
class Attendee(models.Model):
 
    ''' Miscellaneous user-related data. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def __str__(self):
 
        return "%s" % self.user
 

	
 
    @staticmethod
 
    def get_instance(user):
 
        ''' Returns the instance of attendee for the given user, or creates
 
        a new one. '''
 
        try:
 
            return Attendee.objects.get(user=user)
 
        except ObjectDoesNotExist:
 
            return Attendee.objects.create(user=user)
 

	
 
    def save(self, *a, **k):
 
        while not self.access_code:
 
            access_code = util.generate_access_code()
 
            if Attendee.objects.filter(access_code=access_code).count() == 0:
 
                self.access_code = access_code
 
        return super(Attendee, self).save(*a, **k)
 

	
 
    user = models.OneToOneField(User, on_delete=models.CASCADE)
 
    # Badge/profile is linked
 
    access_code = models.CharField(
 
        max_length=6,
 
        unique=True,
 
        db_index=True,
 
    )
 
    boarding_pass_sent = models.BooleanField(default=False)
 
    completed_registration = models.BooleanField(default=False)
 
    guided_categories_complete = models.ManyToManyField("category", blank=True)
 

	
 

	
 
class AttendeeProfileBase(models.Model):
 
    ''' Information for an attendee's badge and related preferences.
 
    Subclass this in your Django site to ask for attendee information in your
 
    registration progess.
 
     '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    objects = InheritanceManager()
 

	
 
    @classmethod
 
    def name_field(cls):
 
        '''
 
        Returns:
 
            The name of a field that stores the attendee's name. This is used
 
            to pre-fill the attendee's name from their Speaker profile, if they
 
            have one.
 
        '''
 
        return None
 

	
 
    def attendee_name(self):
 
        if type(self) == AttendeeProfileBase:
 
            real = AttendeeProfileBase.objects.get_subclass(id=self.id)
 
        else:
 
            real = self
 
        return getattr(real, real.name_field())
 

	
 
    def invoice_recipient(self):
 
        '''
 

	
 
        Returns:
 
            A representation of this attendee profile for the purpose
 
            of rendering to an invoice. This should include any information
 
            that you'd usually include on an invoice. Override in subclasses.
 
        '''
 

	
 
        # Manual dispatch to subclass. Fleh.
 
        slf = AttendeeProfileBase.objects.get_subclass(id=self.id)
 
        # Actually compare the functions.
 
        if type(slf).invoice_recipient != type(self).invoice_recipient:
 
            return type(slf).invoice_recipient(slf)
 

	
 
        # Return a default
vendor/registrasion/registrasion/templates/registrasion/emails/boarding_pass/message.html
Show inline comments
 
new file 100644
 
<html>
 
    <body>
 
        <p>This is your boarding pass</p>
 
        <p>A copy of the QR Code is required for check in, please bring this email on either your phone or on a print out.</p>
 
        <p><img src="data:image/png;base64,{{ qr_code_string }}" /></p>
 
        <p>Backup Code: {{ access_code }}</p>
 
    </body>
 
</html>
vendor/registrasion/registrasion/templates/registrasion/emails/boarding_pass/message.txt
Show inline comments
 
new file 100644
 
This is the plain text version of your boarding pass for linux.conf.au 2018.
 

	
 
The HTML version of this email has a QR Code embedded in it, and it would be greatly appricated if you could switch to that version to print or bring on a phone for check in so we may use the QR code to check you in.
 

	
 

	
 
In the event this cannot be facilitated, please provide the following backup code.
 

	
 
Backup Code: {{ access_code }}
vendor/registrasion/registrasion/templates/registrasion/emails/boarding_pass/subject.txt
Show inline comments
 
new file 100644
 
Your LCA2018 Boarding Pass
vendor/registrasion/requirements/base.txt
Show inline comments
 
django-nested-admin==2.2.6
 
#symposion==1.0b2.dev3
 
lxml==4.0.0
 
pyqrcode
 
pypng
0 comments (0 inline, 0 general)