8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-25 22:22:26 +00:00
re2o/cotisations/pdf.py

168 lines
5.5 KiB
Python

# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017 Gabriel Détraz
# Copyright © 2017 Lara Kermarec
# Copyright © 2017 Augustin Lemesle
# Copyright © 2021 Jean-Romain Garnier
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""pdf.py
Module in charge of rendering some HTML templates to generated PDF invoices.
"""
import os
import tempfile
from datetime import datetime
from django.conf import settings
from django.http import HttpResponse
from django.template.loader import get_template
from django.utils.text import slugify
from preferences.models import CotisationsOption
from xhtml2pdf import pisa
from django.contrib.staticfiles import finders
def render_invoice(_request, ctx={}):
"""
Render an invoice using some available information such as the current
date, the user, the articles, the prices, ...
"""
options, _ = CotisationsOption.objects.get_or_create()
is_estimate = ctx.get("is_estimate", False)
filename = "_".join(
[
"cost_estimate" if is_estimate else "invoice",
slugify(ctx.get("asso_name", "")),
slugify(ctx.get("recipient_name", "")),
str(ctx.get("DATE", datetime.now()).year),
str(ctx.get("DATE", datetime.now()).month),
str(ctx.get("DATE", datetime.now()).day),
]
)
templatename = options.invoice_template.template.name.split("/")[-1]
r = render_pdf(_request, templatename, ctx, filename)
return r
def render_voucher(_request, ctx={}):
"""
Render a subscribtion voucher.
"""
options, _ = CotisationsOption.objects.get_or_create()
filename = "_".join(
[
"voucher",
slugify(ctx.get("asso_name", "")),
slugify(ctx.get("firstname", "")),
slugify(ctx.get("lastname", "")),
str(ctx.get("date_begin", datetime.now()).year),
str(ctx.get("date_begin", datetime.now()).month),
str(ctx.get("date_begin", datetime.now()).day),
]
)
templatename = options.voucher_template.template.name.split("/")[-1]
r = render_pdf(_request, templatename, ctx, filename)
return r
def link_callback(uri, rel):
"""
Convert HTML URIs to absolute system paths so xhtml2pdf can access those
resources
See https://xhtml2pdf.readthedocs.io/en/latest/usage.html#using-xhtml2pdf-in-django
"""
result = finders.find(uri)
if result:
if not isinstance(result, (list, tuple)):
result = [result]
result = list(os.path.realpath(path) for path in result)
path = result[0]
else:
sUrl = settings.STATIC_URL # Typically /static/
sRoot = settings.STATIC_ROOT # Typically /project_static/
mUrl = settings.MEDIA_URL # Typically /media/
mRoot = settings.MEDIA_ROOT # Typically /project_static/media/
if uri.startswith(mUrl):
path = os.path.join(mRoot, uri.replace(mUrl, ""))
elif uri.startswith(sUrl):
path = os.path.join(sRoot, uri.replace(sUrl, ""))
else:
return uri
# Make sure that file exists
if not os.path.isfile(path):
raise Exception("media URI must start with %s or %s" % (sUrl, mUrl))
return path
def create_pdf(template, ctx={}):
"""Creates and returns a PDF from an HTML template using xhtml2pdf.
It create a temporary file for the PDF then read it to return its content.
Args:
template: Path to the HTML template.
ctx: Dict with the context for rendering the template.
Returns:
The content of the temporary PDF file generated.
"""
context = ctx
template = get_template(template)
rendered_tpl = template.render(context).encode("utf-8")
with tempfile.TemporaryFile("wb+") as tempf:
pisa_status = pisa.CreatePDF(rendered_tpl, dest=tempf)
if pisa_status.err:
raise pisa_status.err
tempf.seek(0)
pdf = tempf.read()
return pdf
def render_pdf(_request, template, ctx={}, filename="invoice"):
"""
Creates a PDF from an HTML template using xhtml2pdf.
Calls `create_pdf` and send back an HTTP response for accessing this file.
Args:
_request: Unused, but allow using this function as a Django view.
template: Path to the HTML template.
ctx: Dict with the context for rendering the template.
filename: The name of the file to include in the request headers.
Returns:
The content of the temporary PDF file generated.
"""
pdf = create_pdf(template, ctx)
r = HttpResponse(content_type="application/pdf")
r["Content-Disposition"] = 'attachment; filename="{name}.pdf"'.format(name=filename)
r.write(pdf)
return r