From f0ec02c295e8d66f837e6fd45f6ce57c87162170 Mon Sep 17 00:00:00 2001 From: Jonas 12t Date: Mon, 8 Nov 2021 15:42:25 +0400 Subject: [PATCH] segno qr code --- .../ticket/barlowcondensed-bold.otf | Bin .../ticket/barlowcondensed-light.otf | Bin .../ticket/barlowcondensed-regular.otf | Bin .../ticket/librebarcode128-regular.ttf | Bin .../ApiBillet/templates/ticket/qrtest.html | 8 + .../templates}/ticket/ticket.css | 0 .../ApiBillet/templates/ticket/ticket.html | 191 +++++++++++++++++- DjangoFiles/ApiBillet/views.py | 7 + DjangoFiles/AuthBillet/tasks.py | 2 - DjangoFiles/BaseBillet/signals.py | 25 +-- DjangoFiles/BaseBillet/tasks.py | 169 ++++++++++++++++ DjangoFiles/BaseBillet/views.py | 24 ++- DjangoFiles/TiBillet/celery.py | 6 +- DjangoFiles/TiBillet/settings.py | 7 +- Docker/Dockerfile/dockerfile | 2 + 15 files changed, 405 insertions(+), 36 deletions(-) rename DjangoFiles/{BaseBillet/static => ApiBillet/templates}/ticket/barlowcondensed-bold.otf (100%) rename DjangoFiles/{BaseBillet/static => ApiBillet/templates}/ticket/barlowcondensed-light.otf (100%) rename DjangoFiles/{BaseBillet/static => ApiBillet/templates}/ticket/barlowcondensed-regular.otf (100%) rename DjangoFiles/{BaseBillet/static => ApiBillet/templates}/ticket/librebarcode128-regular.ttf (100%) create mode 100644 DjangoFiles/ApiBillet/templates/ticket/qrtest.html rename DjangoFiles/{BaseBillet/static => ApiBillet/templates}/ticket/ticket.css (100%) delete mode 100644 DjangoFiles/AuthBillet/tasks.py create mode 100644 DjangoFiles/BaseBillet/tasks.py diff --git a/DjangoFiles/BaseBillet/static/ticket/barlowcondensed-bold.otf b/DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-bold.otf similarity index 100% rename from DjangoFiles/BaseBillet/static/ticket/barlowcondensed-bold.otf rename to DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-bold.otf diff --git a/DjangoFiles/BaseBillet/static/ticket/barlowcondensed-light.otf b/DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-light.otf similarity index 100% rename from DjangoFiles/BaseBillet/static/ticket/barlowcondensed-light.otf rename to DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-light.otf diff --git a/DjangoFiles/BaseBillet/static/ticket/barlowcondensed-regular.otf b/DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-regular.otf similarity index 100% rename from DjangoFiles/BaseBillet/static/ticket/barlowcondensed-regular.otf rename to DjangoFiles/ApiBillet/templates/ticket/barlowcondensed-regular.otf diff --git a/DjangoFiles/BaseBillet/static/ticket/librebarcode128-regular.ttf b/DjangoFiles/ApiBillet/templates/ticket/librebarcode128-regular.ttf similarity index 100% rename from DjangoFiles/BaseBillet/static/ticket/librebarcode128-regular.ttf rename to DjangoFiles/ApiBillet/templates/ticket/librebarcode128-regular.ttf diff --git a/DjangoFiles/ApiBillet/templates/ticket/qrtest.html b/DjangoFiles/ApiBillet/templates/ticket/qrtest.html new file mode 100644 index 0000000..ed94776 --- /dev/null +++ b/DjangoFiles/ApiBillet/templates/ticket/qrtest.html @@ -0,0 +1,8 @@ +
+ {{ img_svg | safe }} +
+ + + + fallback + diff --git a/DjangoFiles/BaseBillet/static/ticket/ticket.css b/DjangoFiles/ApiBillet/templates/ticket/ticket.css similarity index 100% rename from DjangoFiles/BaseBillet/static/ticket/ticket.css rename to DjangoFiles/ApiBillet/templates/ticket/ticket.css diff --git a/DjangoFiles/ApiBillet/templates/ticket/ticket.html b/DjangoFiles/ApiBillet/templates/ticket/ticket.html index e2a3db5..786b6a4 100644 --- a/DjangoFiles/ApiBillet/templates/ticket/ticket.html +++ b/DjangoFiles/ApiBillet/templates/ticket/ticket.html @@ -3,21 +3,202 @@ {% load static %} - Boarding ticket + + +
+ {{ img_svg | safe }} +
+ +

{{ ticket.first_name }} {{ ticket.last_name }}

{{ ticket.reservation.event.name }}

-
Flight
-
DL31
+
IMG
+
Prout
Gate
-
29
+
img_svg
Seat
26E
Zone
@@ -32,7 +213,7 @@
-

1257797706706

+

proutproutprout

{{ ticket.first_name }} {{ ticket.last_name }}

Flight
diff --git a/DjangoFiles/ApiBillet/views.py b/DjangoFiles/ApiBillet/views.py index a2679b8..963a6ac 100644 --- a/DjangoFiles/ApiBillet/views.py +++ b/DjangoFiles/ApiBillet/views.py @@ -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 diff --git a/DjangoFiles/AuthBillet/tasks.py b/DjangoFiles/AuthBillet/tasks.py deleted file mode 100644 index 79bd54d..0000000 --- a/DjangoFiles/AuthBillet/tasks.py +++ /dev/null @@ -1,2 +0,0 @@ -# Create your tasks here - diff --git a/DjangoFiles/BaseBillet/signals.py b/DjangoFiles/BaseBillet/signals.py index 92ecf6b..8176089 100644 --- a/DjangoFiles/BaseBillet/signals.py +++ b/DjangoFiles/BaseBillet/signals.py @@ -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}") diff --git a/DjangoFiles/BaseBillet/tasks.py b/DjangoFiles/BaseBillet/tasks.py new file mode 100644 index 0000000..b70bb58 --- /dev/null +++ b/DjangoFiles/BaseBillet/tasks.py @@ -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 diff --git a/DjangoFiles/BaseBillet/views.py b/DjangoFiles/BaseBillet/views.py index 4e4a3cc..3c3c3e6 100644 --- a/DjangoFiles/BaseBillet/views.py +++ b/DjangoFiles/BaseBillet/views.py @@ -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) \ No newline at end of file + return render(request, 'ticket/ticket.html', context=context) + # return render(request, 'ticket/qrtest.html', context=context) \ No newline at end of file diff --git a/DjangoFiles/TiBillet/celery.py b/DjangoFiles/TiBillet/celery.py index 4f58e53..8a842e7 100644 --- a/DjangoFiles/TiBillet/celery.py +++ b/DjangoFiles/TiBillet/celery.py @@ -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 diff --git a/DjangoFiles/TiBillet/settings.py b/DjangoFiles/TiBillet/settings.py index ac1eb01..57bb4e7 100644 --- a/DjangoFiles/TiBillet/settings.py +++ b/DjangoFiles/TiBillet/settings.py @@ -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 # -------------------------------------/ diff --git a/Docker/Dockerfile/dockerfile b/Docker/Dockerfile/dockerfile index d11d328..a81a54d 100644 --- a/Docker/Dockerfile/dockerfile +++ b/Docker/Dockerfile/dockerfile @@ -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