Jul 16 - Update facilities QR codes - add filters & export PDF

This commit is contained in:
2026-07-16 09:39:05 -04:00
parent 593c630e1b
commit faf4796b4a
4 changed files with 366 additions and 48 deletions
+90
View File
@@ -1589,4 +1589,94 @@ def generate_facility_summary_pdf(facility, days, start, now,
))
doc.build(story)
return buf.getvalue()
return buf.getvalue()
def generate_qr_codes_pdf(items, filter_summary: str = '') -> bytes:
"""Return a PDF byte-string laying out selected QR codes in a grid.
Parameters
----------
items : list of dicts, each:
{
'title': str, # main label (facility or area name)
'subtitle': str | None, # e.g. contract name, or parent facility
'caption': str | None, # small line under the QR
'png': bytes, # QR code PNG image bytes
}
filter_summary : human-readable string describing the selection (optional)
"""
buf = io.BytesIO()
generated_at = datetime.now().strftime('%B %d, %Y %I:%M %p ET')
report_title = 'QR Codes'
doc = SimpleDocTemplate(
buf,
pagesize=letter,
leftMargin=0.65 * inch,
rightMargin=0.65 * inch,
topMargin=1.35 * inch,
bottomMargin=0.75 * inch,
title=report_title,
author='Janitorial QC System',
)
def _page_cb(canvas, doc):
_on_page(canvas, doc, report_title, generated_at)
story = []
if filter_summary:
story.append(Paragraph(f'Filters: {filter_summary}', STYLES['ReportSub']))
story.append(Paragraph(
f'Total codes: {len(items)}', STYLES['ReportSub']))
story.append(Spacer(1, 10))
if not items:
story.append(Paragraph('No QR codes selected.', STYLES['FieldValue']))
doc.build(story, onFirstPage=_page_cb, onLaterPages=_page_cb)
return buf.getvalue()
COLS = 3
pw = letter[0] - 1.3 * inch # usable width
cell_w = pw / COLS
qr_size = 1.7 * inch
title_style = ParagraphStyle('QRT', fontName='Helvetica-Bold', fontSize=9,
alignment=TA_CENTER, leading=11, textColor=C_DARK)
sub_style = ParagraphStyle('QRS', fontName='Helvetica', fontSize=7.5,
alignment=TA_CENTER, leading=9, textColor=C_SLATE)
cap_style = ParagraphStyle('QRC', fontName='Helvetica', fontSize=6.5,
alignment=TA_CENTER, leading=8, textColor=C_SLATE)
def _cell(item):
flow = [Paragraph(item.get('title') or '', title_style)]
if item.get('subtitle'):
flow.append(Paragraph(item['subtitle'], sub_style))
flow.append(Spacer(1, 4))
flow.append(RLImage(io.BytesIO(item['png']), width=qr_size, height=qr_size))
if item.get('caption'):
flow.append(Spacer(1, 3))
flow.append(Paragraph(item['caption'], cap_style))
return flow
rows = []
for i in range(0, len(items), COLS):
chunk = items[i:i + COLS]
row = [_cell(it) for it in chunk]
while len(row) < COLS:
row.append('') # filler cell to keep the grid rectangular
rows.append(row)
tbl = Table(rows, colWidths=[cell_w] * COLS)
tbl.setStyle(TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('TOPPADDING', (0, 0), (-1, -1), 10),
('BOTTOMPADDING', (0, 0), (-1, -1), 16),
('LEFTPADDING', (0, 0), (-1, -1), 6),
('RIGHTPADDING', (0, 0), (-1, -1), 6),
]))
story.append(tbl)
doc.build(story, onFirstPage=_page_cb, onLaterPages=_page_cb)
return buf.getvalue()