segno qr code

This commit is contained in:
Jonas 12t 2021-11-08 15:42:25 +04:00
parent 40eb9c3ad2
commit f0ec02c295
15 changed files with 405 additions and 36 deletions

View File

@ -0,0 +1,8 @@
<div>
{{ img_svg | safe }}
</div>
<object type="image/svg+xml" data="data:image/svg+xml;base64,{{ img_svg64 }}">
fallback
</object>

View File

@ -3,21 +3,202 @@
{% load static %}
<meta charset="utf-8">
<link rel="stylesheet" href="{% static 'ticket/ticket.css' %}"/>
<title>Boarding ticket</title>
<meta name="description" content="Boarding ticket">
<style>
@font-face {
font-family: Libre Barcode;
src: url(librebarcode128-regular.ttf);
}
@font-face {
font-family: Barlow Condensed;
src: url(barlowcondensed-regular.otf);
}
@font-face {
font-family: Barlow Condensed;
font-weight: 300;
src: url(barlowcondensed-light.otf);
}
@font-face {
font-family: Barlow Condensed;
font-weight: 700;
src: url(barlowcondensed-bold.otf);
}
@page {
margin: 0;
size: landscape;
}
html {
align-content: center;
align-items: center;
background: #fff;
display: flex;
font-family: Barlow Condensed, sans-serif;
height: 100%;
justify-content: center;
}
body {
background: #eef1f5;
box-sizing: border-box;
color: #2A3239;
display: flex;
flex-wrap: wrap;
height: 8cm;
justify-content: space-between;
margin: 0;
width: 25cm;
}
section {
box-sizing: border-box;
}
dl {
columns: 4;
text-align: center;
}
dt {
font-size: 9pt;
font-weight: 700;
text-transform: uppercase;
}
dd {
margin-left: 0;
}
ul {
align-items: center;
display: flex;
list-style: none;
margin: 0;
padding-left: 0;
}
li {
font-weight: 700;
text-transform: uppercase;
}
#informations {
flex: 1;
padding: 0;
position: relative;
}
#informations h1 {
display: inline-block;
font-size: 25pt;
font-weight: 300;
text-transform: uppercase;
}
#informations #name {
margin-left: 1cm;
}
#informations #destination {
position: absolute;
right: 1cm;
}
#informations dl {
background: #2A3239;
color: #fff;
margin: 0;
padding: 1cm 0;
}
#informations dd {
border-left: 1pt solid #fff;
font-size: 35pt;
}
#informations dd:first-of-type {
border-left: 0;
}
#informations ul {
margin-left: 1cm;
}
#informations li {
font-weight: 300;
padding: 0.15cm;
}
#informations li:first-of-type {
background: #2A3239;
border-radius: 4pt;
color: #fff;
}
#informations li:last-of-type {
font-family: Libre Barcode, cursive;
color: black;
font-size: 25pt;
margin-left: auto;
padding-right: 1cm;
padding-top: 0.5cm;
}
#ticket {
border-left: 1pt dashed #2A3239;
display: flex;
flex-direction: column;
height: 8cm;
justify-content: space-around;
padding: 0 1cm;
}
#ticket h2 {
font-weight: 300;
margin: 0;
text-transform: uppercase;
}
#ticket p {
font-family: Libre Barcode, cursive;
font-size: 25pt;
margin: 0;
text-align: center;
}
#ticket dl {
margin: 0;
}
#ticket li {
margin: 0 0.25cm;
}
</style>
</head>
<body>
<div>
{{ img_svg | safe }}
</div>
<section id="informations">
<h1 id="name">{{ ticket.first_name }} {{ ticket.last_name }}</h1>
<h1 id="destination">{{ ticket.reservation.event.name }}</h1>
<dl>
<dt>Flight</dt>
<dd>DL31</dd>
<dt>IMG</dt>
<dd>Prout</dd>
<dt>Gate</dt>
<dd>29</dd>
<dd>img_svg</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
@ -32,7 +213,7 @@
</section>
<section id="ticket">
<p>1257797706706</p>
<p>proutproutprout</p>
<h2>{{ ticket.first_name }} {{ ticket.last_name }}</h2>
<dl>
<dt>Flight</dt>

View File

@ -150,6 +150,13 @@ class TicketPdf(WeasyTemplateView):
kwargs['ticket'] = ticket
kwargs['config'] = self.config
'''
context = {
'ticket': ticket,
'config': config,
}
'''
self.pdf_filename = ticket.pdf_filename()
return kwargs

View File

@ -1,2 +0,0 @@
# Create your tasks here

View File

@ -7,6 +7,7 @@ from django.utils import timezone
from ApiBillet.thread_mailer import ThreadMaileur
from BaseBillet.models import Reservation, LigneArticle, Ticket, Product, Configuration, Paiement_stripe
from BaseBillet.tasks import ticket_celery_mailer
from TiBillet import settings
@ -127,7 +128,6 @@ def check_paid(old_instance, new_instance):
# def send_billet_to_mail(sender, instance: Reservation, **kwargs):
def send_billet_to_mail(old_instance, new_instance):
# On active les tickets
urls_for_attached_files = {}
if new_instance.tickets:
# On prend aussi ceux qui sont déja activé ( avec les Q() )
# pour pouvoir les envoyer par mail en cas de nouvelle demande
@ -136,33 +136,16 @@ def send_billet_to_mail(old_instance, new_instance):
ticket.status = Ticket.NOT_SCANNED
ticket.save()
# on rajoute les urls du pdf pour le thread async
urls_for_attached_files[ticket.pdf_filename()] = ticket.pdf_url()
# import ipdb; ipdb.set_trace()
# On vérifie qu'on a pas déja envoyé le mail
if not new_instance.mail_send :
logger.info(f" TRIGGER RESERVATION send_billet_to_mail {new_instance.status}")
new_instance : Reservation
config = Configuration.get_solo()
if new_instance.user_commande.email:
try:
mail = ThreadMaileur(
new_instance.user_commande.email,
f"Votre reservation pour {config.organisation}",
template='mails/buy_confirmation.html',
context={
'config': config,
'reservation': new_instance,
},
urls_for_attached_files = urls_for_attached_files,
)
# import ipdb; ipdb.set_trace()
mail.send_with_tread()
except Exception as e :
logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {new_instance} : {e}")
# import ipdb; ipdb.set_trace()
task = ticket_celery_mailer.delay(new_instance.pk)
# https://github.com/psf/requests/issues/5832
else :
logger.info(f" TRIGGER RESERVATION mail déja envoyé {new_instance} : {new_instance.mail_send} - status : {new_instance.status}")

View File

@ -0,0 +1,169 @@
import os
from io import BytesIO
import base64
import segno
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string, get_template
from django.utils import timezone
from BaseBillet.models import Configuration, Reservation, Ticket
from TiBillet.celery import app
import logging
logger = logging.getLogger(__name__)
class CeleryMailerClass():
def __init__(self,
email: str,
title: str,
text=None,
html=None,
template=None,
context=None,
attached_files=None,
):
self.title = title
self.email = email
self.text = text
self.html = html
self.config = Configuration.get_solo()
self.context = context
self.attached_files = attached_files
if template and context:
self.html = render_to_string(template, context=context)
def config_valid(self):
EMAIL_HOST = os.environ.get('EMAIL_HOST')
EMAIL_PORT = os.environ.get('EMAIL_PORT')
EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
if EMAIL_HOST and EMAIL_PORT and EMAIL_HOST_USER and EMAIL_HOST_PASSWORD and self.config.email:
return True
else:
return False
def send(self):
if self.html and self.config_valid():
logger.info(f' send_mail')
mail = EmailMultiAlternatives(
self.title,
self.text,
self.config.email,
[self.email, ],
)
mail.attach_alternative(self.html, "text/html")
if self.attached_files:
for filename, file in self.attached_files.items():
mail.attach(filename, file, 'application/pdf')
mail_return = mail.send(fail_silently=False)
if mail_return == 1:
logger.info(f' mail envoyé : {mail_return} - {self.email}')
else:
logger.error(f' mail non envoyé : {mail_return} - {self.email}')
return mail_return
else:
logger.error(f'Pas de contenu HTML ou de configuration email valide')
raise ValueError('Pas de contenu HTML ou de configuration email valide')
def create_ticket_pdf(ticket: Ticket):
qr = segno.make(f'{ticket.uuid}')
buffer_png = BytesIO()
qr.save(buffer_png, kind='PNG', scale=15)
img_str = base64.b64encode(buffer_png.getvalue()).decode('utf-8')
buffer_svg = BytesIO()
qr.save(buffer_svg, kind='svg', scale=10)
context = {
'ticket': ticket,
'config': Configuration.get_solo(),
'img_str': base64.b64encode(buffer_png.getvalue()).decode('utf-8'),
'img_svg': buffer_svg.getvalue().decode('utf-8'),
'img_svg64': base64.b64encode(buffer_svg.getvalue()).decode('utf-8'),
}
template_name = 'ticket/ticket.html'
# template_name = 'ticket/qrtest.html'
font_config = FontConfiguration()
template = get_template(template_name)
html = template.render(context)
css = CSS(string=
'''
@font-face {
font-family: Libre Barcode;
src: url(file:///DjangoFiles/ApiBillet/templates/ticket/librebarcode128-regular.ttf);
}
@font-face {
font-family: Barlow Condensed;
src: url(file:///DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-regular.otf)
}
@font-face {
font-family: Barlow Condensed;
font-weight: 300;
src: url(file:///DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-light.otf);
}
@font-face {
font-family: Barlow Condensed;
font-weight: 700;
src: url(file:///DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-bold.otf);
}
''',
font_config=font_config)
pdf_binary = HTML(string=html).write_pdf(
stylesheets=[css],
font_config=font_config
)
return pdf_binary
@app.task
def ticket_celery_mailer(reservation_uuid: str):
'''
for ticket in reservation.tickets.filter(status=Ticket.NOT_SCANNED):
response = requests.get(ticket.pdf_url())
print(response.status_code)
'''
config = Configuration.get_solo()
reservation = Reservation.objects.get(pk=reservation_uuid)
attached_files = {}
for ticket in reservation.tickets.filter(status=Ticket.NOT_SCANNED):
attached_files[ticket.pdf_filename()] = create_ticket_pdf(ticket)
try:
mail = CeleryMailerClass(
reservation.user_commande.email,
f"Votre reservation pour {config.organisation}",
template='mails/buy_confirmation.html',
context={
'config': config,
'reservation': reservation,
},
attached_files=attached_files,
)
mail.send()
return True
except Exception as e:
logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {reservation} : {e}")
raise Exception

View File

@ -8,6 +8,14 @@ from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from BaseBillet.models import Configuration, Event, Ticket
import base64
import segno
from io import StringIO, BytesIO
from django.template import engines
from django.http import HttpResponse
from PIL import Image
class index(APIView):
permission_classes = [AllowAny]
@ -36,10 +44,24 @@ class Ticket_html_view(APIView):
permission_classes = [AllowAny]
def get(self, request, pk_uuid):
ticket = get_object_or_404(Ticket, uuid=pk_uuid)
qr = segno.make(f'{ticket.uuid}')
buffer_png = BytesIO()
qr.save(buffer_png, kind='PNG', scale=3)
buffer_svg = BytesIO()
qr.save(buffer_svg, kind='svg', scale=10)
context = {
'ticket': ticket,
'config': Configuration.get_solo(),
'img_str': base64.b64encode(buffer_png.getvalue()).decode('utf-8'),
'img_svg': buffer_svg.getvalue().decode('utf-8'),
'img_svg64': base64.b64encode(buffer_svg.getvalue()).decode('utf-8'),
}
return render(request, 'ticket/ticket.html', context=context)
return render(request, 'ticket/ticket.html', context=context)
# return render(request, 'ticket/qrtest.html', context=context)

View File

@ -34,10 +34,10 @@ app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
@app.task
def add(x, y):
return x + y
@app.task
def add2(x, y):
return x + y
def schema_name():
return connection.schema_name

View File

@ -216,11 +216,10 @@ EMAIL_USE_SSL = os.environ.get('EMAIL_USE_SSL', True)
CELERY_TIMEZONE=os.environ.get('TIME_ZONE', 'UTC')
CELERY_TASK_TRACK_STARTED=True
CELERY_TASK_TIME_LIMIT=30 * 60
BROKER_URL=os.environ.get('CELERY_BROKER')
RESULT_BACKEND=os.environ.get('CELERY_BROKER')
CELERY_RESULT_BACKEND=os.environ.get('CELERY_BACKEND')
BROKER_URL=os.environ.get('CELERY_BROKER', 'redis://redis:6379/0')
CELERY_RESULT_BACKEND=os.environ.get('CELERY_BACKEND', 'redis://redis:6379/0')
# DJANGO_CELERY_BEAT_TZ_AWARE=False
# celery -A TiBillet worker -l INFO
# Jet Menu
# -------------------------------------/

View File

@ -74,6 +74,8 @@ RUN pip install celery
RUN pip install redis
RUN pip install tenant-schemas-celery
RUN pip install segno
RUN apt-get -y clean
RUN python --version