segno qr code
This commit is contained in:
parent
40eb9c3ad2
commit
f0ec02c295
|
|
@ -0,0 +1,8 @@
|
|||
<div>
|
||||
{{ img_svg | safe }}
|
||||
</div>
|
||||
|
||||
|
||||
<object type="image/svg+xml" data="data:image/svg+xml;base64,{{ img_svg64 }}">
|
||||
fallback
|
||||
</object>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
# Create your tasks here
|
||||
|
||||
|
|
@ -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}")
|
||||
|
||||
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}")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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/qrtest.html', context=context)
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
# -------------------------------------/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue