Harshit Ghosh
Refactor email content generation and update email sending logic
e3566c9
{% extends "base.html" %}
{% block title %}{{ row.image_id }} — Report{% endblock %}
{% block content %}
<!-- Breadcrumb -->
<section class="breadcrumb">
<a href="{{ url_for('home') }}">Home</a>
<span class="sep">/</span>
<a href="{{ url_for('reports') }}">Reports</a>
<span class="sep">/</span>
<span class="mono">{{ row.image_id }}</span>
</section>
<!-- Header -->
<section class="detail-header">
<div>
<h1 class="mono">{{ row.image_id }}</h1>
<p>
{% if row.is_positive %}
<span class="dot dot-red"></span> {{ row.outcome }}
{% else %}
<span class="dot dot-green"></span> {{ row.outcome }}
{% endif %}
</p>
<p class="muted small" style="margin-top: 4px">
{% if row.date_display != '—' %}
Generated: {{ row.date_display }} UTC
{% endif %}
{% if payload and payload.report_id %}
&middot; Report ID: {{ payload.report_id }}
{% endif %}
</p>
</div>
<div class="detail-actions">
{% if row.report_file %}
<a class="btn"
href="{{ url_for('serve_report_json', filename=row.report_file) }}"
target="_blank">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<polyline points="14 2 14 8 20 8" />
</svg>
Raw JSON
</a>
{% endif %}
<a class="btn" href="{{ url_for('reports') }}">← Back</a>
</div>
</section>
<!-- Two-column grid -->
<section class="detail-grid">
<!-- Left: Prediction Details -->
<article class="panel">
<h3>Prediction Summary</h3>
<div class="kv-group">
<div class="kv">
<span>Screening Outcome</span>
<strong>{{ row.outcome }}</strong>
</div>
<div class="kv">
<span>Calibrated Probability</span>
<strong>{{ '%.4f'|format(row.cal_prob) if row.cal_prob is not none else 'N/A' }}</strong>
</div>
<div class="kv">
<span>Raw Probability</span>
<strong>{{ '%.4f'|format(row.raw_prob) if row.raw_prob is not none else 'N/A' }}</strong>
</div>
<div class="kv">
<span>Confidence Band</span>
<strong><span class="badge badge-{{ row.band|lower }}">{{ row.band }}</span></strong>
</div>
<div class="kv">
<span>Triage Action</span>
<strong>{{ row.triage }}</strong>
</div>
<div class="kv">
<span>Urgency</span>
<strong><span class="badge badge-{{ row.urgency|lower }}">{{ row.urgency }}</span></strong>
</div>
<div class="kv">
<span>Ground Truth</span>
<strong>{{ row.true_label if row.true_label and row.true_label != 'N/A' else 'Not available' }}</strong>
</div>
<div class="kv">
<span>Report Date</span>
<strong>{{ row.date_display }}</strong>
</div>
</div>
<form method="post" action="{{ url_for('update_ground_truth', image_id=row.image_id) }}" style="margin-top: 12px">
<label class="muted small" for="trueLabel">Update Ground Truth</label>
<div class="dir-input-row" style="margin-top: 6px">
<select name="true_label" id="trueLabel" class="input" style="max-width: 240px">
<option value="" {% if not row.true_label %}selected{% endif %}>Not set</option>
<option value="POSITIVE" {% if row.true_label == 'POSITIVE' %}selected{% endif %}>Positive</option>
<option value="NEGATIVE" {% if row.true_label == 'NEGATIVE' %}selected{% endif %}>Negative</option>
<option value="UNKNOWN" {% if row.true_label == 'UNKNOWN' %}selected{% endif %}>Unknown</option>
</select>
<button type="submit" class="btn btn-primary" style="margin-left: 8px">Save</button>
</div>
</form>
<!-- Confidence bar -->
{% if row.cal_prob is not none %}
<div class="prob-bar-wrap">
<div class="prob-bar-label">
<span>0</span>
<span>Calibrated probability</span>
<span>1</span>
</div>
<div class="prob-bar">
<div class="prob-fill {% if row.cal_prob >= 0.75 %}fill-high{% elif row.cal_prob >= 0.35 %}fill-medium{% else %}fill-low{% endif %}"
style="width: {{ (row.cal_prob * 100)|round(1) }}%"></div>
<div class="prob-marker"
style="left: {{ (row.cal_prob * 100)|round(1) }}%">
{{ '%.2f'|format(row.cal_prob) }}
</div>
</div>
</div>
{% endif %}
</article>
<!-- Right: Grad-CAM -->
<article class="panel">
<h3>Grad-CAM Visualization</h3>
{% if row.gradcam_url %}
<img class="heatmap-img"
src="{{ row.gradcam_url }}"
alt="Grad-CAM for {{ row.image_id }}" />
<p class="muted small" style="margin-top: 10px">
Highlighted regions indicate areas with greatest influence on the
model's decision.
</p>
{% else %}
<div class="empty-state">
<p class="muted">No Grad-CAM heatmap available for this case.</p>
</div>
{% endif %}
</article>
</section>
<!-- AI LLM Summary -->
{% if report_record and report_record.llm_summary %}
<section class="panel" style="margin-bottom: 24px; border-left: 4px solid var(--primary, #007aff);">
<h3 style="color: var(--primary, #007aff); display: flex; align-items: center; gap: 8px;">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
</svg>
AI Medical Intelligence Summary
</h3>
<p style="line-height: 1.6; font-size: 1.1rem; color: var(--text-dark, #2c3e50);">
{{ report_record.llm_summary }}
</p>
</section>
{% endif %}
{% if payload and (payload.llm_provider or payload.llm_model) %}
<section class="panel" style="margin-top: 16px">
<h3>LLM Information</h3>
<div class="kv-group" style="max-width: 500px">
<div class="kv">
<span>Provider</span>
<strong>{{ payload.llm_provider or 'N/A' }}</strong>
</div>
<div class="kv">
<span>Model</span>
<strong>{{ payload.llm_model or 'N/A' }}</strong>
</div>
</div>
</section>
{% endif %}
<!-- Model info (from payload) -->
{% if payload and payload.screening_module %}
<section class="panel" style="margin-top: 16px">
<h3>Model Information</h3>
<div class="kv-group" style="max-width: 500px">
<div class="kv">
<span>Architecture</span>
<strong>{{ payload.screening_module.architecture }}</strong>
</div>
<div class="kv">
<span>Version</span>
<strong>{{ payload.screening_module.version }}</strong>
</div>
<div class="kv">
<span>Calibration</span>
<strong>{{ payload.screening_module.calibration_method }}</strong>
</div>
<div class="kv">
<span>Decision Threshold</span>
<strong>{{ '%.4f'|format(payload.prediction.decision_threshold) }}</strong>
</div>
</div>
</section>
{% endif %}
<!-- Disclaimer -->
<section class="disclaimer-box">
<strong>Disclaimer:</strong>
This report is produced by an AI-assisted screening tool and does NOT
constitute a medical diagnosis. All screening findings must be reviewed and
confirmed by a qualified, licensed medical professional before any clinical
decision is made.
</section>
{% endblock %}