Ticket Generator opérational

This commit is contained in:
Jonas 12t 2021-11-08 18:21:59 +04:00
parent f0ec02c295
commit 777b4e4835
15 changed files with 702 additions and 248 deletions

View File

@ -0,0 +1,231 @@
<html lang="fr">
<head>
{% 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({% static 'ticket/librebarcode128-regular.ttf' %});
}
@font-face {
font-family: Barlow Condensed;
src: url({% static 'ticket/barlowcondensed-regular.otf' %});
}
@font-face {
font-family: Barlow Condensed;
font-weight: 300;
src: url({% static 'ticket/barlowcondensed-light.otf' %});
}
@font-face {
font-family: Barlow Condensed;
font-weight: 700;
src: url({% static 'ticket/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>
<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>Gate</dt>
<dd>29</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>5:10pm</li>
<li>Dec 15, 2018</li>
<li>Coach</li>
<li>PROUTPROUT</li>
</ul>
</section>
<section id="ticket">
<p>1257797706706</p>
<h2>{{ ticket.first_name }} {{ ticket.last_name }}</h2>
<dl>
<dt>Flight</dt>
<dd>DL31</dd>
<dt>Gate</dt>
<dd>29</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>{{ ticket.reservation.event.name }}</li>
<li>5:10pm</li>
</ul>
</section>
</body>
</html>

View File

@ -2,7 +2,14 @@
{{ img_svg | safe }} {{ img_svg | safe }}
</div> </div>
{#<object type="image/svg+xml" data="data:image/svg+xml;base64,{{ img_svg64 }}">#}
{# fallback#}
{#</object>#}
<object type="image/svg+xml" data="data:image/svg+xml;base64,{{ img_svg64 }}"> {#<div>#}
{# {{ bar_svg | safe }}#}
{#</div>#}
<object type="image/svg+xml" data="data:image/svg+xml;base64,{{ bar_svg64 }}">
fallback fallback
</object> </object>

View File

@ -1,235 +1,24 @@
<html lang="fr"> <html lang="fr">
<head> <head>
{% load static %}
<meta charset="utf-8"> <meta charset="utf-8">
<title>Boarding ticket</title> <title>Boarding ticket</title>
<meta name="description" content="Boarding ticket"> <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> </head>
<body> <body>
<div>
{{ img_svg | safe }}
</div>
<section id="informations"> <section id="informations">
<h1 id="name">{{ ticket.first_name }} {{ ticket.last_name }}</h1>
<h1 id="destination">{{ ticket.reservation.event.name }}</h1>
<dl>
<dt>IMG</dt>
<dd>Prout</dd>
<dt>Gate</dt>
<dd>img_svg</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>5:10pm</li>
<li>Dec 15, 2018</li>
<li>Coach</li>
<li>PROUTPROUT</li>
</ul>
</section>
<section id="ticket"> <h1>{{ ticket.first_name }} {{ ticket.last_name }}</h1>
<p>proutproutprout</p> <h1>{{ config.organisation }}</h1>
<h2>{{ ticket.first_name }} {{ ticket.last_name }}</h2> <h1>{{ ticket.reservation.event.name }}</h1>
<dl> <h1>{{ ticket.reservation.event.datetime }}</h1>
<dt>Flight</dt> <h1>Siège : {{ ticket.seat }}</h1>
<dd>DL31</dd> <h1>Numéro de billet : {{ ticket.numero_uuid }}</h1>
<dt>Gate</dt>
<dd>29</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>{{ ticket.reservation.event.name }}</li>
<li>5:10pm</li>
</ul>
</section>
<div>{{ img_svg | safe }}</div>
<div>{{ bar_svg | safe }}</div>
</section>
</body> </body>
</html> </html>

View File

@ -449,6 +449,9 @@ class Ticket(models.Model):
datetime.short_description = 'Date de reservation' datetime.short_description = 'Date de reservation'
datetime.admin_order_field = 'reservation__datetime' datetime.admin_order_field = 'reservation__datetime'
def numero_uuid(self):
return f"{self.uuid}".split('-')[0]
class meta: class meta:
ordering = ('-datetime',) ordering = ('-datetime',)

View File

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

View File

@ -0,0 +1,151 @@
@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;
}

View File

@ -0,0 +1,231 @@
<html lang="fr">
<head>
{% 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({% static 'ticket/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>
<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>Gate</dt>
<dd>29</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>5:10pm</li>
<li>Dec 15, 2018</li>
<li>Coach</li>
<li>PROUTPROUT</li>
</ul>
</section>
<section id="ticket">
<p>1257797706706</p>
<h2>{{ ticket.first_name }} {{ ticket.last_name }}</h2>
<dl>
<dt>Flight</dt>
<dd>DL31</dd>
<dt>Gate</dt>
<dd>29</dd>
<dt>Seat</dt>
<dd>26E</dd>
<dt>Zone</dt>
<dd>4</dd>
</dl>
<ul>
<li>{{ ticket.reservation.event.name }}</li>
<li>5:10pm</li>
</ul>
</section>
</body>
</html>

Binary file not shown.

View File

@ -1,7 +1,9 @@
import base64
import os import os
from io import BytesIO from io import BytesIO
import base64
import segno import segno
import barcode
from djoser import utils
from weasyprint import HTML, CSS from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration from weasyprint.text.fonts import FontConfiguration
@ -35,6 +37,7 @@ class CeleryMailerClass():
self.config = Configuration.get_solo() self.config = Configuration.get_solo()
self.context = context self.context = context
self.attached_files = attached_files self.attached_files = attached_files
self.sended = None
if template and context: if template and context:
self.html = render_to_string(template, context=context) self.html = render_to_string(template, context=context)
@ -68,9 +71,11 @@ class CeleryMailerClass():
mail_return = mail.send(fail_silently=False) mail_return = mail.send(fail_silently=False)
if mail_return == 1: if mail_return == 1:
self.sended = True
logger.info(f' mail envoyé : {mail_return} - {self.email}') logger.info(f' mail envoyé : {mail_return} - {self.email}')
else: else:
logger.error(f' mail non envoyé : {mail_return} - {self.email}') logger.error(f' mail non envoyé : {mail_return} - {self.email}')
return mail_return return mail_return
else: else:
logger.error(f'Pas de contenu HTML ou de configuration email valide') logger.error(f'Pas de contenu HTML ou de configuration email valide')
@ -79,32 +84,38 @@ class CeleryMailerClass():
def create_ticket_pdf(ticket: Ticket): def create_ticket_pdf(ticket: Ticket):
qr = segno.make(f'{ticket.uuid}') qr = segno.make(f"{ticket.uuid}", micro=False)
buffer_png = BytesIO()
qr.save(buffer_png, kind='PNG', scale=15)
img_str = base64.b64encode(buffer_png.getvalue()).decode('utf-8')
buffer_svg = BytesIO() buffer_svg = BytesIO()
qr.save(buffer_svg, kind='svg', scale=10) qr.save(buffer_svg, kind='svg', scale=8)
CODE128 = barcode.get_barcode_class('code128')
bar_svg = BytesIO()
bar_secret = utils.encode_uid(f"{ticket.uuid}".split('-')[4])
bar = CODE128(f"{bar_secret}")
options = {
'module_height': 30,
'module_width': 0.6,
'font_size': 10,
}
bar.write(bar_svg, options = options)
context = { context = {
'ticket': ticket, 'ticket': ticket,
'config': Configuration.get_solo(), 'config': Configuration.get_solo(),
'img_str': base64.b64encode(buffer_png.getvalue()).decode('utf-8'),
'img_svg': buffer_svg.getvalue().decode('utf-8'), 'img_svg': buffer_svg.getvalue().decode('utf-8'),
'img_svg64': base64.b64encode(buffer_svg.getvalue()).decode('utf-8'), 'bar_svg': bar_svg.getvalue().decode('utf-8'),
# 'bar_svg64': base64.b64encode(bar_svg.getvalue()).decode('utf-8'),
} }
template_name = 'ticket/ticket.html' template_name = 'ticket/ticket.html'
# template_name = 'ticket/qrtest.html' # template_name = 'ticket/example_flight_ticket.html'
font_config = FontConfiguration() font_config = FontConfiguration()
template = get_template(template_name) template = get_template(template_name)
html = template.render(context) html = template.render(context)
css = CSS(string= css = CSS(string=
''' '''
@font-face { @font-face {
@ -130,7 +141,7 @@ def create_ticket_pdf(ticket: Ticket):
pdf_binary = HTML(string=html).write_pdf( pdf_binary = HTML(string=html).write_pdf(
stylesheets=[css], stylesheets=[css],
font_config=font_config font_config=font_config,
) )
return pdf_binary return pdf_binary
@ -138,11 +149,6 @@ def create_ticket_pdf(ticket: Ticket):
@app.task @app.task
def ticket_celery_mailer(reservation_uuid: str): 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() config = Configuration.get_solo()
reservation = Reservation.objects.get(pk=reservation_uuid) reservation = Reservation.objects.get(pk=reservation_uuid)
@ -163,7 +169,13 @@ def ticket_celery_mailer(reservation_uuid: str):
attached_files=attached_files, attached_files=attached_files,
) )
mail.send() mail.send()
return True
logger.info(f"mail.sended : {mail.sended}")
if mail.sended :
reservation.mail_send = True
reservation.status = Reservation.VALID
reservation.save()
except Exception as e: except Exception as e:
logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {reservation} : {e}") logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {reservation} : {e}")
raise Exception raise Exception

View File

@ -10,6 +10,9 @@ from BaseBillet.models import Configuration, Event, Ticket
import base64 import base64
import segno import segno
import barcode
from djoser import utils
from io import StringIO, BytesIO from io import StringIO, BytesIO
from django.template import engines from django.template import engines
@ -47,20 +50,31 @@ class Ticket_html_view(APIView):
ticket = get_object_or_404(Ticket, uuid=pk_uuid) ticket = get_object_or_404(Ticket, uuid=pk_uuid)
qr = segno.make(f'{ticket.uuid}') qr = segno.make(f"{ticket.uuid}", micro=False)
buffer_png = BytesIO()
qr.save(buffer_png, kind='PNG', scale=3)
buffer_svg = BytesIO() buffer_svg = BytesIO()
qr.save(buffer_svg, kind='svg', scale=10) qr.save(buffer_svg, kind='svg', scale=8)
CODE128 = barcode.get_barcode_class('code128')
buffer_barcode_SVG = BytesIO()
bar_secret = utils.encode_uid(f"{ticket.uuid}".split('-')[4])
bar = CODE128(f"{bar_secret}")
options = {
'module_height': 30,
'module_width': 0.6,
'font_size': 10,
}
bar.write(buffer_barcode_SVG, options=options)
context = { context = {
'ticket': ticket, 'ticket': ticket,
'config': Configuration.get_solo(), 'config': Configuration.get_solo(),
'img_str': base64.b64encode(buffer_png.getvalue()).decode('utf-8'),
'img_svg': buffer_svg.getvalue().decode('utf-8'), 'img_svg': buffer_svg.getvalue().decode('utf-8'),
'img_svg64': base64.b64encode(buffer_svg.getvalue()).decode('utf-8'), # 'img_svg64': base64.b64encode(buffer_svg.getvalue()).decode('utf-8'),
'bar_svg': buffer_barcode_SVG.getvalue().decode('utf-8'),
# 'bar_svg64': base64.b64encode(buffer_barcode_SVG.getvalue()).decode('utf-8'),
} }
return render(request, 'ticket/ticket.html', context=context) return render(request, 'ticket/ticket.html', context=context)

View File

@ -75,6 +75,7 @@ RUN pip install redis
RUN pip install tenant-schemas-celery RUN pip install tenant-schemas-celery
RUN pip install segno RUN pip install segno
RUN pip install python-barcode
RUN apt-get -y clean RUN apt-get -y clean