Push the rest
This commit is contained in:
82
backend/app/core/export.py
Normal file
82
backend/app/core/export.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import A4, landscape
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
from reportlab.lib.units import cm
|
||||
from reportlab.platypus import (
|
||||
Paragraph,
|
||||
SimpleDocTemplate,
|
||||
Spacer,
|
||||
Table,
|
||||
TableStyle,
|
||||
)
|
||||
|
||||
_PAGE_W, _ = landscape(A4)
|
||||
_MARGIN = 1.5 * cm
|
||||
_HEADER_BG = colors.HexColor("#1a56db")
|
||||
_ROW_BG = colors.HexColor("#eef2ff")
|
||||
|
||||
|
||||
def _pdf_table(rows: list[dict]) -> Table:
|
||||
if not rows:
|
||||
table_data: list[list] = [["No data available"]]
|
||||
n_cols = 1
|
||||
else:
|
||||
headers = list(rows[0].keys())
|
||||
n_cols = len(headers)
|
||||
table_data = [headers] + [
|
||||
[str(row.get(h, "")) for h in headers] for row in rows
|
||||
]
|
||||
|
||||
col_w = (_PAGE_W - 2 * _MARGIN) / n_cols
|
||||
t = Table(table_data, colWidths=[col_w] * n_cols, repeatRows=1)
|
||||
|
||||
style: list = [
|
||||
("BACKGROUND", (0, 0), (-1, 0), _HEADER_BG),
|
||||
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
|
||||
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
|
||||
("FONTSIZE", (0, 0), (-1, 0), 8),
|
||||
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
|
||||
("FONTSIZE", (0, 1), (-1, -1), 7),
|
||||
("ALIGN", (0, 0), (-1, -1), "LEFT"),
|
||||
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
|
||||
("GRID", (0, 0), (-1, -1), 0.25, colors.HexColor("#d1d5db")),
|
||||
("TOPPADDING", (0, 0), (-1, -1), 3),
|
||||
("BOTTOMPADDING", (0, 0), (-1, -1), 3),
|
||||
("LEFTPADDING", (0, 0), (-1, -1), 5),
|
||||
("RIGHTPADDING", (0, 0), (-1, -1), 5),
|
||||
]
|
||||
for i in range(1, len(table_data)):
|
||||
bg = _ROW_BG if i % 2 == 1 else colors.white
|
||||
style.append(("BACKGROUND", (0, i), (-1, i), bg))
|
||||
|
||||
t.setStyle(TableStyle(style))
|
||||
return t
|
||||
|
||||
|
||||
def to_pdf_bytes(rows: list[dict], title: str, subtitle: str = "") -> bytes:
|
||||
"""Serialise *rows* to a single-sheet PDF and return the raw bytes."""
|
||||
buf = io.BytesIO()
|
||||
styles = getSampleStyleSheet()
|
||||
story = []
|
||||
|
||||
story.append(Paragraph(title, styles["Title"]))
|
||||
if subtitle:
|
||||
story.append(Spacer(1, 0.2 * cm))
|
||||
story.append(Paragraph(subtitle, styles["Normal"]))
|
||||
story.append(Spacer(1, 0.5 * cm))
|
||||
story.append(_pdf_table(rows))
|
||||
|
||||
doc = SimpleDocTemplate(
|
||||
buf,
|
||||
pagesize=landscape(A4),
|
||||
leftMargin=_MARGIN,
|
||||
rightMargin=_MARGIN,
|
||||
topMargin=_MARGIN,
|
||||
bottomMargin=_MARGIN,
|
||||
)
|
||||
doc.build(story)
|
||||
return buf.getvalue()
|
||||
Reference in New Issue
Block a user