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 }}
</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
</object>

View File

@ -1,235 +1,24 @@
<html lang="fr">
<head>
{% load static %}
<meta charset="utf-8">
<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>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">
<p>proutproutprout</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>
<h1>{{ ticket.first_name }} {{ ticket.last_name }}</h1>
<h1>{{ config.organisation }}</h1>
<h1>{{ ticket.reservation.event.name }}</h1>
<h1>{{ ticket.reservation.event.datetime }}</h1>
<h1>Siège : {{ ticket.seat }}</h1>
<h1>Numéro de billet : {{ ticket.numero_uuid }}</h1>
<div>{{ img_svg | safe }}</div>
<div>{{ bar_svg | safe }}</div>
</section>
</body>
</html>

View File

@ -449,6 +449,9 @@ class Ticket(models.Model):
datetime.short_description = 'Date de reservation'
datetime.admin_order_field = 'reservation__datetime'
def numero_uuid(self):
return f"{self.uuid}".split('-')[0]
class meta:
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
from io import BytesIO
import base64
import segno
import barcode
from djoser import utils
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
@ -35,6 +37,7 @@ class CeleryMailerClass():
self.config = Configuration.get_solo()
self.context = context
self.attached_files = attached_files
self.sended = None
if template and context:
self.html = render_to_string(template, context=context)
@ -68,9 +71,11 @@ class CeleryMailerClass():
mail_return = mail.send(fail_silently=False)
if mail_return == 1:
self.sended = True
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')
@ -79,32 +84,38 @@ class CeleryMailerClass():
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')
qr = segno.make(f"{ticket.uuid}", micro=False)
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 = {
'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'),
'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/qrtest.html'
# template_name = 'ticket/example_flight_ticket.html'
font_config = FontConfiguration()
template = get_template(template_name)
html = template.render(context)
css = CSS(string=
'''
@font-face {
@ -130,7 +141,7 @@ def create_ticket_pdf(ticket: Ticket):
pdf_binary = HTML(string=html).write_pdf(
stylesheets=[css],
font_config=font_config
font_config=font_config,
)
return pdf_binary
@ -138,11 +149,6 @@ def create_ticket_pdf(ticket: Ticket):
@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)
@ -163,7 +169,13 @@ def ticket_celery_mailer(reservation_uuid: str):
attached_files=attached_files,
)
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:
logger.error(f"{timezone.now()} Erreur envoie de mail pour reservation {reservation} : {e}")
raise Exception

View File

@ -10,6 +10,9 @@ from BaseBillet.models import Configuration, Event, Ticket
import base64
import segno
import barcode
from djoser import utils
from io import StringIO, BytesIO
from django.template import engines
@ -47,20 +50,31 @@ class Ticket_html_view(APIView):
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)
qr = segno.make(f"{ticket.uuid}", micro=False)
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 = {
'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'),
# '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)

View File

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