HH/templates/analytics/kpi_report_weasyprint.html

618 lines
19 KiB
HTML

{% load i18n %}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ report.kpi_id }} - {{ report.indicator_title }} - PX360</title>
<style>
@page {
size: A4 landscape;
margin: 1.5cm;
@top-center {
content: "{{ report.hospital.name }} — KPI Report {{ report.kpi_id }}";
font-size: 8pt;
color: #94a3b8;
}
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
font-size: 8pt;
color: #94a3b8;
}
@bottom-left {
content: "Generated {{ generated_at }}";
font-size: 8pt;
color: #94a3b8;
}
}
@page :first {
@top-center { content: ""; }
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 9pt;
line-height: 1.5;
color: #1f2937;
}
/* Header */
.report-header {
border-bottom: 3px solid #005696;
padding-bottom: 12px;
margin-bottom: 20px;
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.report-header .header-info {
flex: 1;
}
.report-header .header-logo {
height: 50px;
width: auto;
flex-shrink: 0;
margin-left: 16px;
}
.report-header h1 {
font-size: 18pt;
color: #005696;
margin: 0;
}
.report-header .kpi-badge {
display: inline-block;
background: #005696;
color: white;
padding: 3px 10px;
border-radius: 4px;
font-size: 10pt;
font-weight: bold;
margin-bottom: 6px;
}
.report-header .subtitle {
font-size: 10pt;
color: #64748b;
margin-top: 4px;
}
/* Summary cards */
.summary-grid {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.summary-card {
flex: 1;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 12px;
text-align: center;
}
.summary-card .label {
font-size: 8pt;
color: #64748b;
text-transform: uppercase;
font-weight: bold;
margin-bottom: 4px;
}
.summary-card .value {
font-size: 20pt;
font-weight: bold;
color: #005696;
}
.summary-card .value.danger { color: #dc2626; }
.summary-card .value.success { color: #16a34a; }
.summary-card .detail {
font-size: 8pt;
color: #94a3b8;
margin-top: 2px;
}
/* Progress bar */
.progress-container {
margin-bottom: 20px;
padding: 12px;
background: #f8fafc;
border-radius: 8px;
}
.progress-label {
display: flex;
justify-content: space-between;
font-size: 8pt;
font-weight: bold;
color: #64748b;
text-transform: uppercase;
margin-bottom: 6px;
}
.progress-bar {
height: 12px;
background: #e2e8f0;
border-radius: 6px;
overflow: hidden;
position: relative;
}
.progress-fill {
height: 100%;
border-radius: 6px;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
table th {
background: #005696;
color: white;
font-size: 8pt;
font-weight: bold;
text-transform: uppercase;
padding: 6px 4px;
text-align: center;
border: 1px solid #005696;
}
table td {
padding: 5px 4px;
border: 1px solid #e2e8f0;
font-size: 8.5pt;
text-align: center;
}
table tr:nth-child(even) td { background: #f8fafc; }
table .row-label {
text-align: left;
font-weight: bold;
color: #1f2937;
background: #f1f5f9 !important;
}
table .total-row td {
background: #005696 !important;
color: white !important;
font-weight: bold;
}
table .below-target {
color: #dc2626;
font-weight: bold;
}
/* Metadata grid */
.metadata-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 20px;
}
.metadata-item {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 6px 12px;
min-width: 120px;
}
.metadata-item .label {
font-size: 7pt;
color: #94a3b8;
text-transform: uppercase;
font-weight: bold;
}
.metadata-item .value {
font-size: 9pt;
color: #1f2937;
font-weight: 600;
}
/* Charts */
.charts-row {
display: flex;
gap: 20px;
margin-bottom: 20px;
page-break-inside: avoid;
}
.chart-box {
flex: 1;
text-align: center;
}
.chart-box img {
max-width: 100%;
height: auto;
}
/* Breakdown cards */
.breakdown-grid {
display: flex;
gap: 12px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.breakdown-card {
flex: 1;
min-width: 180px;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 10px;
page-break-inside: avoid;
}
.breakdown-card h4 {
font-size: 9pt;
color: #005696;
margin-bottom: 6px;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 4px;
}
.breakdown-card .stat {
font-size: 8.5pt;
color: #64748b;
margin-bottom: 2px;
}
.breakdown-card .stat strong { color: #1f2937; }
/* Section heading */
.section-title {
font-size: 11pt;
color: #005696;
font-weight: bold;
margin-bottom: 10px;
margin-top: 20px;
border-left: 4px solid #005696;
padding-left: 8px;
}
/* AI Analysis */
.ai-section {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 14px;
margin-bottom: 20px;
page-break-inside: avoid;
}
.ai-section h4 {
font-size: 9pt;
color: #005696;
margin-bottom: 6px;
text-transform: uppercase;
font-weight: bold;
}
.ai-section p { font-size: 9pt; margin-bottom: 8px; }
.ai-section ul { padding-left: 18px; margin-bottom: 8px; }
.ai-section li { font-size: 9pt; margin-bottom: 3px; }
.findings-box {
background: white;
border-left: 3px solid #007bbd;
padding: 8px 12px;
margin-bottom: 8px;
border-radius: 0 4px 4px 0;
}
.recommendation-box {
background: #ecfdf5;
border-left: 3px solid #16a34a;
padding: 8px 12px;
margin-bottom: 8px;
border-radius: 0 4px 4px 0;
}
/* Sign-off */
.signoff-grid {
display: flex;
gap: 16px;
margin-top: 30px;
page-break-inside: avoid;
}
.signoff-box {
flex: 1;
border: 1px solid #cbd5e1;
border-radius: 8px;
padding: 20px 12px 12px 12px;
min-height: 80px;
}
.signoff-box .role {
font-size: 8pt;
color: #64748b;
text-transform: uppercase;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.signoff-box .line {
border-top: 1px solid #94a3b8;
margin-bottom: 4px;
}
.signoff-box .line-label {
font-size: 7pt;
color: #94a3b8;
}
/* Footer */
.report-footer {
margin-top: 30px;
padding-top: 10px;
border-top: 1px solid #e2e8f0;
font-size: 8pt;
color: #94a3b8;
text-align: center;
}
.page-break { page-break-before: always; }
.no-break { page-break-inside: avoid; }
</style>
</head>
<body>
<!-- Report Header -->
<div class="report-header">
<div class="header-info">
<div class="kpi-badge">{{ report.kpi_id }}</div>
<h1>{{ report.indicator_title }}</h1>
<div class="subtitle">
{{ report.hospital.name }} &nbsp;|&nbsp;
{{ report.report_period_display }} &nbsp;|&nbsp;
{% trans "Generated" %}: {{ report.generated_at|default:report.created_at|date:"M d, Y H:i" }}
</div>
</div>
{% if logo_path %}
<img src="{{ logo_path }}" alt="{{ report.hospital.name }}" class="header-logo">
{% endif %}
</div>
<!-- Executive Summary Cards -->
<div class="summary-grid">
<div class="summary-card">
<div class="label">{% trans "Overall Result" %}</div>
<div class="value {% if report.overall_result < report.threshold_percentage %}danger{% elif report.overall_result >= report.target_percentage %}success{% endif %}">
{{ report.overall_result|floatformat:1 }}%
</div>
</div>
<div class="summary-card">
<div class="label">{{ report.numerator_label }}</div>
<div class="value">{{ report.total_numerator }}</div>
</div>
<div class="summary-card">
<div class="label">{{ report.denominator_label }}</div>
<div class="value">{{ report.total_denominator }}</div>
</div>
<div class="summary-card">
<div class="label">{% trans "Target" %}</div>
<div class="value" style="color:#16a34a;">{{ report.target_percentage|floatformat:0 }}%</div>
</div>
<div class="summary-card">
<div class="label">{% trans "Threshold" %}</div>
<div class="value" style="color:#dc2626;">{{ report.threshold_percentage|floatformat:0 }}%</div>
</div>
</div>
<!-- Progress Bar -->
<div class="progress-container">
<div class="progress-label">
<span>{% trans "Performance vs Target" %}</span>
<span>{{ report.overall_result|floatformat:1 }}% / {{ report.target_percentage|floatformat:0 }}%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: {{ report.overall_result|floatformat:0|stringformat:'s' }}%; background: {% if report.overall_result < report.threshold_percentage %}#dc2626{% elif report.overall_result >= report.target_percentage %}#16a34a{% else %}#f59e0b{% endif %};"></div>
</div>
</div>
<!-- Monthly Data Table -->
<div class="section-title">{% trans "Monthly Performance Data" %}</div>
<table>
<thead>
<tr>
<th style="width:140px;">{% trans "Month" %}</th>
<th>Jan</th><th>Feb</th><th>Mar</th><th>Apr</th>
<th>May</th><th>Jun</th><th>Jul</th><th>Aug</th>
<th>Sep</th><th>Oct</th><th>Nov</th><th>Dec</th>
<th style="background:#003d5c;">{% trans "TOTAL" %}</th>
</tr>
</thead>
<tbody>
<!-- Numerator row -->
<tr>
<td class="row-label">{{ report.numerator_label }}</td>
{% for m in monthly_data %}<td>{% if m %}{{ m.numerator }}{% else %}-{% endif %}</td>{% endfor %}
<td>{{ report.total_numerator }}</td>
</tr>
<!-- Denominator row -->
<tr>
<td class="row-label">{{ report.denominator_label }}</td>
{% for m in monthly_data %}<td>{% if m %}{{ m.denominator }}{% else %}-{% endif %}</td>{% endfor %}
<td>{{ report.total_denominator }}</td>
</tr>
<!-- Result % row -->
<tr>
<td class="row-label">{% trans "Result %" %}</td>
{% for m in monthly_data %}
<td {% if m and m.percentage < report.threshold_percentage %}class="below-target"{% endif %}>
{% if m %}{{ m.percentage|floatformat:1 }}{% else %}-{% endif %}
</td>
{% endfor %}
<td><strong>{{ report.overall_result|floatformat:1 }}</strong></td>
</tr>
<!-- Target row -->
<tr>
<td class="row-label">{% trans "Target" %}</td>
<td colspan="12">{{ report.target_percentage|floatformat:0 }}%</td>
<td>{{ report.target_percentage|floatformat:0 }}%</td>
</tr>
<!-- Threshold row -->
<tr>
<td class="row-label">{% trans "Threshold" %}</td>
<td colspan="12">{{ report.threshold_percentage|floatformat:0 }}%</td>
<td>{{ report.threshold_percentage|floatformat:0 }}%</td>
</tr>
</tbody>
</table>
<!-- Metadata -->
<div class="metadata-grid">
<div class="metadata-item">
<div class="label">{% trans "Category" %}</div>
<div class="value">{{ report.category }}</div>
</div>
<div class="metadata-item">
<div class="label">{% trans "KPI Type" %}</div>
<div class="value">{{ report.kpi_type }}</div>
</div>
<div class="metadata-item">
<div class="label">{% trans "Risk Level" %}</div>
<div class="value">{{ report.risk_level }}</div>
</div>
<div class="metadata-item">
<div class="label">{% trans "Dimension" %}</div>
<div class="value">{{ report.dimension }}</div>
</div>
<div class="metadata-item">
<div class="label">{% trans "Data Collection" %}</div>
<div class="value">{{ report.data_collection_method }}</div>
</div>
<div class="metadata-item">
<div class="label">{% trans "Frequency" %}</div>
<div class="value">{{ report.reporting_frequency }}</div>
</div>
{% if report.collector_name %}
<div class="metadata-item">
<div class="label">{% trans "Collector" %}</div>
<div class="value">{{ report.collector_name }}</div>
</div>
{% endif %}
{% if report.analyzer_name %}
<div class="metadata-item">
<div class="label">{% trans "Analyzer" %}</div>
<div class="value">{{ report.analyzer_name }}</div>
</div>
{% endif %}
</div>
<!-- Charts -->
{% if trend_chart or source_chart %}
<div class="charts-row">
{% if trend_chart %}
<div class="chart-box">
<img src="data:image/png;base64,{{ trend_chart }}" alt="Monthly Performance Trend" />
</div>
{% endif %}
{% if source_chart %}
<div class="chart-box" style="max-width: 520px;">
<img src="data:image/png;base64,{{ source_chart }}" alt="Complaints by Source" />
</div>
{% endif %}
</div>
{% endif %}
<!-- Department Breakdown -->
{% if department_breakdowns %}
<div class="section-title">{% trans "Department Breakdown" %}</div>
<div class="breakdown-grid">
{% for dept in department_breakdowns %}
<div class="breakdown-card">
<h4>{{ dept.get_department_category_display }}</h4>
<div class="stat"><strong>{% trans "Complaints" %}:</strong> {{ dept.complaint_count }}</div>
<div class="stat"><strong>{% trans "Resolved" %}:</strong> {{ dept.resolved_count }}</div>
{% if dept.avg_resolution_days %}
<div class="stat"><strong>{% trans "Avg Resolution" %}:</strong> {{ dept.avg_resolution_days|floatformat:1 }} {% trans "days" %}</div>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
<!-- Location Breakdown -->
{% if location_breakdowns %}
<div class="section-title">{% trans "Location Breakdown" %}</div>
<div class="breakdown-grid">
{% for loc in location_breakdowns %}
<div class="breakdown-card">
<h4>{{ loc.location_type }}</h4>
<div class="stat"><strong>{% trans "Complaints" %}:</strong> {{ loc.complaint_count }}</div>
<div class="stat"><strong>{% trans "Share" %}:</strong> {{ loc.percentage|floatformat:1 }}%</div>
</div>
{% endfor %}
</div>
{% endif %}
<!-- AI Analysis -->
{% if ai_analysis %}
<div class="page-break"></div>
<div class="section-title">{% trans "AI-Generated Analysis" %}</div>
{% if ai_analysis.executive_summary %}
<div class="ai-section">
<h4>{% trans "Executive Summary" %}</h4>
<p>{{ ai_analysis.executive_summary }}</p>
</div>
{% endif %}
{% if ai_analysis.performance_analysis %}
<div class="ai-section">
<h4>{% trans "Performance Analysis" %}</h4>
<p>{{ ai_analysis.performance_analysis }}</p>
</div>
{% endif %}
{% if ai_analysis.key_findings %}
<div class="ai-section">
<h4>{% trans "Key Findings" %}</h4>
{% for finding in ai_analysis.key_findings %}
<div class="findings-box">{{ finding }}</div>
{% endfor %}
</div>
{% endif %}
{% if ai_analysis.reasons_for_delays %}
<div class="ai-section">
<h4>{% trans "Reasons for Delays" %}</h4>
<ul>
{% for reason in ai_analysis.reasons_for_delays %}
<li>{{ reason }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if ai_analysis.recommendations %}
<div class="ai-section">
<h4>{% trans "Recommendations" %}</h4>
{% for rec in ai_analysis.recommendations %}
<div class="recommendation-box">{{ rec }}</div>
{% endfor %}
</div>
{% endif %}
{% if ai_analysis.comparison_to_target %}
<div class="ai-section">
<h4>{% trans "Comparison to Target" %}</h4>
<p>{{ ai_analysis.comparison_to_target }}</p>
</div>
{% endif %}
{% endif %}
<!-- Sign-off Section -->
<div class="signoff-grid">
<div class="signoff-box">
<div class="role">{% trans "Prepared By" %}</div>
<div class="line"></div>
<div class="line-label">{% trans "Name & Signature" %}</div>
</div>
<div class="signoff-box">
<div class="role">{% trans "Reviewed By" %}</div>
<div class="line"></div>
<div class="line-label">{% trans "Name & Signature" %}</div>
</div>
<div class="signoff-box">
<div class="role">{% trans "Approved By" %}</div>
<div class="line"></div>
<div class="line-label">{% trans "Name & Signature" %}</div>
</div>
</div>
<!-- Footer -->
<div class="report-footer">
PX360 — Patient Experience Management System &nbsp;|&nbsp;
{{ report.hospital.name }} &nbsp;|&nbsp;
{% trans "Report ID" %}: {{ report.id }} &nbsp;|&nbsp;
{% trans "Generated" %}: {{ generated_at }}
</div>
</body>
</html>