06/12 Redesign Reports page
This commit is contained in:
@@ -93,6 +93,27 @@ def _build_styles():
|
||||
add('FooterStyle', parent='Normal',
|
||||
fontSize=7, textColor=C_SLATE, fontName='Helvetica',
|
||||
alignment=TA_CENTER)
|
||||
# ── Summary PDF styles ────────────────────────────────────────────────
|
||||
add('SummaryTitle', parent='Normal',
|
||||
fontSize=18, textColor=C_DARK, fontName='Helvetica-Bold', spaceAfter=2)
|
||||
add('ReportSubtitle', parent='Normal',
|
||||
fontSize=11, textColor=C_SLATE, fontName='Helvetica', spaceAfter=2)
|
||||
add('Meta', parent='Normal',
|
||||
fontSize=8, textColor=C_SLATE, fontName='Helvetica', spaceAfter=1)
|
||||
add('ScoreValue', parent='Normal',
|
||||
fontSize=22, textColor=C_BLUE, fontName='Helvetica-Bold',
|
||||
alignment=TA_CENTER, spaceAfter=0)
|
||||
add('ScoreLabel', parent='Normal',
|
||||
fontSize=7.5, textColor=C_SLATE, fontName='Helvetica',
|
||||
alignment=TA_CENTER, spaceAfter=0)
|
||||
add('SectionHeader', parent='Normal',
|
||||
fontSize=10, textColor=C_DARK, fontName='Helvetica-Bold',
|
||||
spaceBefore=8, spaceAfter=4)
|
||||
add('TableHeader', parent='Normal',
|
||||
fontSize=8.5, textColor=C_WHITE, fontName='Helvetica-Bold',
|
||||
alignment=TA_CENTER, spaceAfter=0)
|
||||
add('TableCell', parent='Normal',
|
||||
fontSize=8.5, textColor=C_DARK, fontName='Helvetica', spaceAfter=0)
|
||||
|
||||
return base
|
||||
|
||||
@@ -1385,4 +1406,144 @@ def generate_inspections_list_pdf(inspections, filter_summary: str = '') -> byte
|
||||
story.append(tbl)
|
||||
|
||||
doc.build(story, onFirstPage=_page_cb, onLaterPages=_page_cb)
|
||||
|
||||
|
||||
# ── Facility Customer Summary PDF ─────────────────────────────────────────────
|
||||
|
||||
def generate_facility_summary_pdf(facility, days, start, now,
|
||||
total_inspections, avg_score,
|
||||
area_scores, open_issues, resolved_count):
|
||||
"""Customer-facing one-page PDF summary for a facility."""
|
||||
styles = _build_styles()
|
||||
buf = io.BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
buf, pagesize=letter,
|
||||
leftMargin=0.75 * inch, rightMargin=0.75 * inch,
|
||||
topMargin=0.75 * inch, bottomMargin=0.75 * inch,
|
||||
)
|
||||
page_w = letter[0] - 1.5 * inch
|
||||
story = []
|
||||
|
||||
story.append(Paragraph(facility.name, styles['SummaryTitle']))
|
||||
story.append(Paragraph('Facility Performance Summary', styles['ReportSubtitle']))
|
||||
story.append(Paragraph(
|
||||
'Period: {} to {} ({} days)'.format(
|
||||
start.strftime('%b %d, %Y'), now.strftime('%b %d, %Y'), days),
|
||||
styles['Meta'],
|
||||
))
|
||||
if getattr(facility, 'address', None):
|
||||
story.append(Paragraph(facility.address, styles['Meta']))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
story.append(HRFlowable(width='100%', thickness=1, color=C_BORDER))
|
||||
story.append(Spacer(1, 0.15 * inch))
|
||||
|
||||
score_str = '{}%'.format(avg_score) if avg_score is not None else '—'
|
||||
score_color = (C_GREEN if avg_score and avg_score >= 80
|
||||
else C_YELLOW if avg_score and avg_score >= 60 else C_RED)
|
||||
|
||||
def _kpi(label, value, col=C_BLUE):
|
||||
hex_str = '%06x' % (col.hexval() & 0xFFFFFF)
|
||||
return [Paragraph('<font color="#{}">'
|
||||
'<b>{}</b></font>'.format(hex_str, value),
|
||||
styles['ScoreValue']),
|
||||
Paragraph(label, styles['ScoreLabel'])]
|
||||
|
||||
kpi_tbl = Table([[
|
||||
_kpi('Inspections Completed', str(total_inspections)),
|
||||
_kpi('Avg Score', score_str, score_color),
|
||||
_kpi('Open Issues', str(len(open_issues)),
|
||||
C_RED if open_issues else C_GREEN),
|
||||
_kpi('Resolved This Period', str(resolved_count), C_GREEN),
|
||||
]], colWidths=[page_w / 4] * 4)
|
||||
kpi_tbl.setStyle(TableStyle([
|
||||
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
|
||||
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
|
||||
('INNERGRID', (0, 0), (-1, -1), 0.5, C_BORDER),
|
||||
('BOX', (0, 0), (-1, -1), 0.5, C_BORDER),
|
||||
('BACKGROUND', (0, 0), (-1, -1), C_LIGHT),
|
||||
('TOPPADDING', (0, 0), (-1, -1), 8),
|
||||
('BOTTOMPADDING', (0, 0), (-1, -1), 8),
|
||||
]))
|
||||
story.append(kpi_tbl)
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
_P = lambda t: Paragraph(t, styles['TableCell'])
|
||||
_H = lambda t: Paragraph('<b>{}</b>'.format(t), styles['TableHeader'])
|
||||
|
||||
if area_scores:
|
||||
story.append(Paragraph('Score by Area', styles['SectionHeader']))
|
||||
story.append(Spacer(1, 0.05 * inch))
|
||||
tbl_data = [[_H('Area'), _H('Avg Score'), _H('Inspections')]]
|
||||
for a in area_scores:
|
||||
avg = round(float(a.avg), 1)
|
||||
col = C_GREEN if avg >= 80 else C_YELLOW if avg >= 60 else C_RED
|
||||
hex_str = '%06x' % (col.hexval() & 0xFFFFFF)
|
||||
tbl_data.append([
|
||||
_P(a.name),
|
||||
Paragraph('<font color="#{}">'
|
||||
'<b>{}%</b></font>'.format(hex_str, avg),
|
||||
styles['TableCell']),
|
||||
_P(str(a.count)),
|
||||
])
|
||||
area_tbl = Table(tbl_data,
|
||||
colWidths=[page_w * 0.55, page_w * 0.25, page_w * 0.20],
|
||||
repeatRows=1)
|
||||
area_tbl.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), C_DARK),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [C_WHITE, C_LIGHT]),
|
||||
('INNERGRID', (0, 0), (-1, -1), 0.25, C_BORDER),
|
||||
('BOX', (0, 0), (-1, -1), 0.5, C_BORDER),
|
||||
('ALIGN', (1, 0), (-1, -1), 'CENTER'),
|
||||
('TOPPADDING', (0, 0), (-1, -1), 4),
|
||||
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
||||
]))
|
||||
story.append(area_tbl)
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
|
||||
story.append(Paragraph('Open Issues', styles['SectionHeader']))
|
||||
story.append(Spacer(1, 0.05 * inch))
|
||||
if open_issues:
|
||||
tbl_data = [[_H('Severity'), _H('Area'), _H('Description'), _H('Reported')]]
|
||||
for issue in open_issues:
|
||||
sev_col = SEVERITY_COLORS.get(issue.severity, C_SLATE)
|
||||
hex_str = '%06x' % (sev_col.hexval() & 0xFFFFFF)
|
||||
desc = issue.description or ''
|
||||
tbl_data.append([
|
||||
Paragraph('<font color="#{}">'
|
||||
'<b>{}</b></font>'.format(hex_str, (issue.severity or '').title()),
|
||||
styles['TableCell']),
|
||||
_P(issue.area.name if issue.area else '—'),
|
||||
_P(desc[:100] + ('...' if len(desc) > 100 else '')),
|
||||
_P(issue.reported_at.strftime('%Y-%m-%d') if issue.reported_at else '—'),
|
||||
])
|
||||
iss_tbl = Table(tbl_data,
|
||||
colWidths=[page_w * 0.14, page_w * 0.20,
|
||||
page_w * 0.50, page_w * 0.16],
|
||||
repeatRows=1)
|
||||
iss_tbl.setStyle(TableStyle([
|
||||
('BACKGROUND', (0, 0), (-1, 0), C_DARK),
|
||||
('ROWBACKGROUNDS', (0, 1), (-1, -1), [C_WHITE, C_LIGHT]),
|
||||
('INNERGRID', (0, 0), (-1, -1), 0.25, C_BORDER),
|
||||
('BOX', (0, 0), (-1, -1), 0.5, C_BORDER),
|
||||
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
||||
('TOPPADDING', (0, 0), (-1, -1), 4),
|
||||
('BOTTOMPADDING', (0, 0), (-1, -1), 4),
|
||||
('LEFTPADDING', (0, 0), (-1, -1), 6),
|
||||
('RIGHTPADDING', (0, 0), (-1, -1), 6),
|
||||
]))
|
||||
story.append(iss_tbl)
|
||||
else:
|
||||
story.append(Paragraph('No open issues — all clear.', styles['Meta']))
|
||||
|
||||
story.append(Spacer(1, 0.2 * inch))
|
||||
story.append(HRFlowable(width='100%', thickness=0.5, color=C_BORDER))
|
||||
story.append(Spacer(1, 0.05 * inch))
|
||||
story.append(Paragraph(
|
||||
'Generated {} — Confidential'.format(now.strftime('%B %d, %Y %I:%M %p')),
|
||||
styles['Meta'],
|
||||
))
|
||||
doc.build(story)
|
||||
return buf.getvalue()
|
||||
return buf.getvalue()
|
||||
Reference in New Issue
Block a user