My doubt is in the time that my application is delayed at the moment I want to generate a PDF report, with python and reportlab. while they are up to 20 pages the application does not take long, in just seconds it generates the report in PDF, but when the report that is going to be generated has approximately 70 pages or more, the time exceeds 3 minutes, the problem is that the application generates reports of up to 1000 pages or more, how can I solve this, or what other alternative do I have to generate this type of PDF reports?
I attach the report code ...
__author__ = "FelipeMedel"
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter, portrait
from reportlab.lib.units import cm, mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import Paragraph, Table, BaseDocTemplate, Frame, PageTemplate, Image, TableStyle
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY, TA_RIGHT, TA_CENTER, TA_LEFT
import os
from .... import session
import uuid
from ....models import DefaultValue
from ....utils.math_ext import _round
from ....utils.image_converter import ImagesConverter
from ...libs.functions import paragraph_over_flow, paragraph_over_flow_height
import time
pdfmetrics.registerFont(TTFont('Arial', 'Arial.ttf'))
pdfmetrics.registerFont(TTFont('Arial-Bold', 'Arial_Bold.ttf'))
pdfmetrics.registerFontFamily(
'Arial',
normal='Arial',
bold='Arial-Bold'
)
class NumberedCanvas(canvas.Canvas):
def __init__(self, *args, **kwargs):
canvas.Canvas.__init__(self, *args, **kwargs)
self._saved_page_states = []
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page_footer_n_header(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_footer_n_header(self, page_count):
preview_data = AccountingYearClosePreview.preview_data
# TABLAS HEADER -----------------------------------------------------------------------------------------
style_table_company = TableStyle(
[
# ('BOX', (0, 0), (-1, -1), 1, (0, 0, 0)),
# ('GRID', (0, 0), (-1, -1), 1, (0, 0, 0)),
('LEFTPADDING', (0, 0), (-1, -1), 2.5 * cm),
('RIGHTPADDING', (0, 0), (-1, -1), 2.5 * cm),
('TOPPADDING', (0, 0), (-1, -1), 0.05 * cm),
('VALIGN', (0, 0), (-1, -1), 'TOP')
]
)
# TABLAS HEADER - FIN ------------------------------------------------------------------------------------
# ESTILOS HEADER ------------------------------------------------------------------------------------------
texto_company = ParagraphStyle('Normal')
texto_company.fontName = 'Arial'
texto_company.fontSize = 12
texto_company.leading = 12
texto_company.alignment = TA_CENTER
texto_company_nit = ParagraphStyle('Normal')
texto_company_nit.fontName = 'Arial'
texto_company_nit.fontSize = 10
texto_company_nit.leading = 10
texto_company_nit.alignment = TA_CENTER
texto_derecha = ParagraphStyle('Normal')
texto_derecha.fontName = 'Arial'
texto_derecha.fontSize = 8
texto_derecha.leading = 8
texto_derecha.alignment = TA_RIGHT
texto_izquierda = ParagraphStyle('Normal')
texto_izquierda.fontName = 'Arial'
texto_izquierda.fontSize = 8
texto_izquierda.leading = 8
texto_izquierda.alignment = TA_LEFT
texto_centro = ParagraphStyle('Normal')
texto_centro.fontName = 'Arial'
texto_centro.fontSize = 8
texto_centro.leading = 8
texto_centro.alignment = TA_CENTER
texto_justificado = ParagraphStyle('Normal')
texto_justificado.fontName = 'Arial'
texto_justificado.fontSize = 8
texto_justificado.leading = 8
texto_justificado.alignment = TA_JUSTIFY
# ESTILOS HEADER - FIN ------------------------------------------------------------------------------------
def header(cnv):
cnv.saveState()
# TABLA DATOS COMPAÑIA --------------------------------------------------------------------------------
company_name = '{0} - {1}'.format(preview_data['company_name'], preview_data['branch_name'])
nit = preview_data['nit'].split('-')
x = 0 if preview_data['libro_1'] == '0' else 1
y = 0 if preview_data['libro_2'] == '0' else 1
datos_company = [
[
Paragraph('<b>{0}</b>'.format(
paragraph_over_flow_height(text='' if x == 0 and y == 0 else company_name.upper(),
width=12.8,
no_par=3,
font_size=12,
leading=12,
left_indent=2.8,
right_indent=2.8)), texto_company)
],
[
Paragraph('' if x == 0 and y == 0 else '<b>{0}-{1}</b>'.format('.'.join([str(nit[0])[i:i + 3]
for i in range(0, len(str(nit[0])), 3)]), nit[1]), texto_company_nit)
]
]
tabla_company = Table(
datos_company,
[19.6 * cm],
style=style_table_company
)
tabla_company.wrap(19.6 * cm, 4 * cm)
tabla_company.drawOn(cnv, 1 * cm, 25.5 * cm)
# TABLA DATOS COMPAÑIA - FIN --------------------------------------------------------------------------
# LOGO ------------------------------------------------------------------------------------------------
image = None if preview_data['image'] is None \
else "{0},{1}".format("data:image/*;base64",
ImagesConverter.img_convert_to_base64(preview_data['image'].image))
if image is not None:
img = Image(image, width=2.3 * cm, height=2.3 * cm)
img.wrap(2.3 * cm, 2.3 * cm)
img.drawOn(cnv, 1 * cm, 24.5 * cm)
# LOGO - FIN ------------------------------------------------------------------------------------------
# FECHA Y PAGINADO ------------------------------------------------------------------------------------
page_number = Paragraph('Página {0} de {1}'.format(self._pageNumber, page_count), texto_derecha)
page_number.wrap(3 * cm, 0.5 * cm)
page_number.drawOn(cnv, 17.6 * cm, 25.1 * cm)
# FECHA Y PAGINADO - FIN ------------------------------------------------------------------------------
cnv.restoreState()
def footer(cnv):
cnv.saveState()
# FACTURA Y FECHA -------------------------------------------------------------------------------------
fecha_hora = Paragraph(time.strftime("%d/%m/%Y %I:%M:%S %p"), texto_derecha)
fecha_hora.wrap(4 * cm, 1 * cm)
fecha_hora.drawOn(cnv, 16.6 * cm, 1 * cm)
# FACTURA Y FECHA - FIN -------------------------------------------------------------------------------
# VALIDACION PARA DOCUMENTO ANULADO -------------------------------------------------------------------
if preview_data['annuled']:
msm = 'ANULADO'
anulado(cnv, msm)
# VALIDACION PARA DOCUMENTO ANULADO - FIN -------------------------------------------------------------
cnv.restoreState()
def anulado(cvn, cadena):
cvn.saveState()
cvn.translate(18.2 * cm, 21 * cm)
cvn.setFontSize(100, 30)
cvn.setFillColorRGB(1, 0, 0, alpha=0.3)
cvn.rotate(35)
cvn.drawRightString(0, 0, cadena)
cvn.restoreState()
header(self)
footer(self)
class AccountingYearClosePreview:
preview_data = None
@staticmethod
def make_preview_pdf(preview_data):
AccountingYearClosePreview.preview_data = preview_data
outfilename = "{0}.pdf".format(uuid.uuid1())
outfiledir = os.getcwd().replace('aplicacion/registros', 'Assets/documents')
outfilepath = os.path.join(outfiledir, outfilename)
# ESTILOS TABLA DETALLES ----------------------------------------------------------------------------------
style_table_descripcion = TableStyle(
[
# ('BOX', (0, 0), (-1, -1), 1, (0, 0, 0)),
# ('GRID', (0, 0), (-1, -1), 1, (0, 0, 0)),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (5, -1), 0),
('TOPPADDING', (0, 0), (-1, -1), 0),
('LINEABOVE', (0, 1), (5, 1), 1, (0, 0, 0)),
('LINEABOVE', (0, 0), (5, 0), 1, (0, 0, 0)),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('SPAN', (0, 1), (5, 1)),
('SPAN', (0, 2), (5, 2)),
]
)
estilo = TableStyle(
[
# ('BOX', (0, 0), (-1, -1), 1, (0, 0, 0)),
# ('GRID', (0, 0), (-1, -1), 1, (0, 0, 0)),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('LEFTPADDING', (1, 0), (-1, -1), 0.1 * cm),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (0, -1), 0.1 * cm),
('TOPPADDING', (0, 0), (-1, -1), 0),
('VALIGN', (0, 0), (-1, -1), 'TOP')
]
)
estilo_total = TableStyle(
[
# ('BOX', (0, 0), (-1, -1), 1, (0, 0, 0)),
# ('GRID', (0, 0), (-1, -1), 1, (0, 0, 0)),
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
('TOPPADDING', (0, 0), (-1, -1), 0),
('LINEABOVE', (0, 0), (-1, 0), 1, (0, 0, 0)),
('LINEABOVE', (0, 2), (-1, 2), 1, (0, 0, 0))
]
)
estilo_cabecera = TableStyle(
[
('LEFTPADDING', (0, 0), (-1, -1), 0),
('RIGHTPADDING', (0, 0), (-1, -1), 0),
('TOPPADDING', (0, 0), (-1, -1), 0),
]
)
text_right = ParagraphStyle('Normal')
text_right.fontName = 'Arial'
text_right.fontSize = 8
text_right.leading = 8
text_right.alignment = TA_RIGHT
text_left = ParagraphStyle('Normal')
text_left.fontName = 'Arial'
text_left.fontSize = 8
text_left.leading = 8
text_left.alignment = TA_LEFT
text_center = ParagraphStyle('Normal')
text_center.fontName = 'Arial'
text_center.fontSize = 8
text_center.leading = 8
text_center.alignment = TA_CENTER
text_center_contabilidad = ParagraphStyle('Normal')
text_center_contabilidad.fontName = 'Arial'
text_center_contabilidad.fontSize = 12
text_center_contabilidad.leading = 12
text_center_contabilidad.alignment = TA_CENTER
# ESTILOS TABLA DETALLES - FIN ------------------------------------------------------------------------
# VARIABLES LIBROS -----------------------------------------------------------------------------------
x = 0 if preview_data['libro_1'] == '0' else 1
y = 0 if preview_data['libro_2'] == '0' else 1
# VARIABLES LIBROS - FIN ------------------------------------------------------------------------------
# TABLA DESCRIPCION -----------------------------------------------------------------------------------
if (x == 1 and y == 0) or (y == 1 and x == 0):
if x == 0 and y == 1:
contabilizacion = 'LIBRO 2'
else:
contabilizacion = 'LIBRO 1'
datos_cabeza = [
[
Paragraph('', text_center),
],
[
Paragraph('<b>{0}</b>'.format(contabilizacion), text_center_contabilidad),
],
[
Paragraph('', text_center)
]
]
datos_descripcion = [
[
Paragraph('<b>NÚMERO</b>', text_left),
Paragraph('<b>DOCUMENTO</b>', text_right),
Paragraph('<b>FECHA</b>', text_right),
Paragraph('<b>CANTIDAD</b>', text_right),
Paragraph('<b>VALOR</b>', text_right),
Paragraph('<b>TOTAL</b>', text_right)
],
[
Paragraph('<b>{0}</b>'.format('texto de prueba'.upper()), text_left),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right)
],
[
Paragraph('<b>OTRO TEXTO</b>'.upper(), text_left),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right)
]
]
# TABLA DESCRIPCION - FIN -----------------------------------------------------------------------------
# DETALLES ------------------------------------------------------------------------------------------------
datos = []
if (x == 1 and y == 0) or (x == 0 and y == 1):
for i in range(20):
datos.append(
[
Paragraph('0000 00 000', text_left),
Paragraph('ASD 0123456789', text_right),
Paragraph(time.strftime('%d/%m/%Y'), text_right),
Paragraph('{:20,.{}f}'.format(_round(1, default_decimals.quantityDecimals),
default_decimals.quantityDecimals), text_right),
Paragraph('{:20,.{}f}'.format(_round(1234567890, default_decimals.valueDecimals),
default_decimals.valueDecimals), text_right),
Paragraph('{:20,.{}f}'.format(_round(1234567890, default_decimals.valueDecimals),
default_decimals.valueDecimals), text_right)
]
)
else:
vacio = []
vacio.append(
[
Paragraph('', text_left),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right)
]
)
# DETALLES - FIN ------------------------------------------------------------------------------------------
# TOTAL ---------------------------------------------------------------------------------------------------
# datos_total = []
if (x == 1 and y == 0) or (x == 0 and y == 1):
datos_total = [
[
Paragraph('', text_left),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('<b>TOTAL</b>', text_right),
Paragraph('<b>{:20,.{}f}</b>'.format(_round(0, default_decimals.valueDecimals),
default_decimals.valueDecimals), text_right),
Paragraph('<b>{:20,.{}f}</b>'.format(_round(0, default_decimals.valueDecimals),
default_decimals.valueDecimals), text_right),
],
[
Paragraph('', text_left),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('', text_right),
Paragraph('<b>{:20,.{}f}</b>'.format(_round(0, default_decimals.valueDecimals),
default_decimals.valueDecimals), text_right),
]
]
# TOTAL - FIN ---------------------------------------------------------------------------------------------
doc = BaseDocTemplate(outfilepath, pagesize=portrait(letter))
doc.addPageTemplates(
[
PageTemplate(
frames=[
Frame(
x1=1 * cm,
y1=1.6 * cm,
width=19.6 * cm,
height=23.3 * cm,
leftPadding=0,
rightPadding=0,
bottomPadding=0,
topPadding=0,
id=None,
showBoundary=False
)
]
)
]
)
if (x == 1 and y == 0) or (x == 0 and y == 1):
doc.build(
[Table(
datos_cabeza,
[19.6 * cm],
[0.1 * cm, 0.8 * cm, 0.4 * cm],
style=estilo_cabecera
), Table(
datos_descripcion,
[6.6 * cm, 2.5 * cm, 1.6 * cm, 1.7 * cm, 3.6 * cm, 3.6 * cm],
0.5 * cm,
style=style_table_descripcion
), Table(
datos,
[6.6 * cm, 2.5 * cm, 1.6 * cm, 1.7 * cm, 3.6 * cm, 3.6 * cm],
style=estilo
), Table(
datos_total,
[6.6 * cm, 2.5 * cm, 1.6 * cm, 1.7 * cm, 3.6 * cm, 3.6 * cm],
style=estilo_total
)], canvasmaker=NumberedCanvas
)
else:
doc.build(
[Table(
vacio,
[6.6 * cm, 2.5 * cm, 1.6 * cm, 1.7 * cm, 3.6 * cm, 3.6 * cm],
style=style_table_descripcion
)], canvasmaker=NumberedCanvas
)
return "{0}".format(outfi
lename)