"""Lab Administrator persona.
Features:
- Lab turnaround time analytics
- Specimen volume tracking
- Positivity rate monitoring
- AI Diagnostics Lab (Vision, Audio)
- Administrative OCR form digitizer
- Lab Intelligence chatbot
"""
import os
import streamlit as st
import plotly.express as px
from data.synthetic import get_lab_results, get_polypharmacy_alerts
from services.diagnostics import analyze_wound_image, analyze_respiratory_audio, process_ocr_form
from components.chatbot import render_chatbot
# Path to sample assets
_ASSETS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "assets", "samples")
def render():
"""Render the Lab Admin dashboard."""
st.title("Laboratory Intelligence Center")
lab_data = get_lab_results()
# Top KPI row
avg_tat = lab_data["TAT_Hours"].mean()
total_specimens = lab_data["Specimens_Processed"].sum()
avg_positivity = lab_data["Positivity_Rate"].mean()
total_critical = lab_data["Critical_Values_Reported"].sum()
total_rejected = lab_data["Rejected_Specimens"].sum()
c1, c2, c3, c4, c5 = st.columns(5)
with c1:
st.metric("Avg TAT", f"{avg_tat:.1f} hrs", "-12%")
with c2:
st.metric("Specimens (30d)", f"{total_specimens:,}")
with c3:
st.metric("Avg Positivity", f"{avg_positivity:.1%}")
with c4:
st.metric("Critical Values", total_critical)
with c5:
st.metric("Rejected", total_rejected, delta_color="inverse")
st.divider()
tab_analytics, tab_medintel, tab_vision, tab_audio, tab_ocr, tab_chat = st.tabs([
"Lab Analytics", "💊 Medication Intelligence",
"Wound Vision AI", "Respiratory Audio AI", "Form Digitizer (OCR)", "🔬 Lab Intelligence",
])
# --- TAB: LAB ANALYTICS ---
with tab_analytics:
col1, col2 = st.columns(2)
with col1:
fig_tat = px.line(
lab_data, x="Date", y="TAT_Hours",
title="Lab Result Turnaround Time Trend",
template="plotly_white",
)
fig_tat.add_hline(y=4.0, line_dash="dash", line_color="orange", annotation_text="SLA Target: 4h")
fig_tat.update_traces(line_color="#3b82f6")
st.plotly_chart(fig_tat, use_container_width=True)
with col2:
fig_pos = px.bar(
lab_data, x="Date", y="Positivity_Rate",
title="Infection Positivity Rate",
template="plotly_white",
color="Positivity_Rate",
color_continuous_scale="OrRd",
)
st.plotly_chart(fig_pos, use_container_width=True)
col3, col4 = st.columns(2)
with col3:
fig_spec = px.area(
lab_data, x="Date", y="Specimens_Processed",
title="Daily Specimen Volume",
template="plotly_white",
)
fig_spec.update_traces(line_color="#10b981", fillcolor="rgba(16,185,129,0.1)")
st.plotly_chart(fig_spec, use_container_width=True)
with col4:
fig_crit = px.bar(
lab_data, x="Date", y="Critical_Values_Reported",
title="Critical Values Reported",
template="plotly_white",
color="Critical_Values_Reported",
color_continuous_scale="Reds",
)
st.plotly_chart(fig_crit, use_container_width=True)
# --- TAB: MEDICATION INTELLIGENCE ---
with tab_medintel:
st.subheader("Medication Interaction Intelligence")
st.caption(
"AI-powered pharmaceutical surveillance across all residents. "
"Detects polypharmacy risks, duplicate classes, and drug-lab correlations."
)
med_alerts = get_polypharmacy_alerts()
# Summary metrics
mi1, mi2, mi3 = st.columns(3)
with mi1:
st.metric("Active Alerts", len(med_alerts))
with mi2:
high_count = sum(1 for a in med_alerts if a["severity"] == "High")
st.metric("High Severity", high_count, delta_color="inverse")
with mi3:
total_meds = sum(len(a["medications_involved"]) for a in med_alerts)
st.metric("Medications Flagged", total_meds)
st.divider()
# Alert detail cards
for alert in med_alerts:
severity_color = {"High": "#ef4444", "Moderate": "#f59e0b", "Low": "#10b981"}.get(
alert["severity"], "#64748b"
)
with st.expander(
f"{'🔴' if alert['severity'] == 'High' else '🟡'} "
f"{alert['resident']} — {alert['alert_type']}",
expanded=(alert["severity"] == "High"),
):
st.markdown(
f"""
{alert['alert_type']} — Severity: {alert['severity']}
— Confidence: {alert['confidence']:.0%}
""",
unsafe_allow_html=True,
)
st.markdown(alert["description"])
st.markdown("**Medications Involved:**")
for med in alert["medications_involved"]:
st.markdown(f"- 💊 `{med}`")
lab_col, rec_col = st.columns(2)
with lab_col:
st.markdown("**Lab Correlation:**")
if "diuretic" in alert["description"].lower():
st.markdown(
"- **K+:** Monitor q24h (risk: hypo/hyperkalemia)\n"
"- **Na+:** Monitor q24h (risk: hyponatremia)\n"
"- **Cr/BUN:** Check within 24h (renal function)\n"
"- **Orthostatic BP:** TID checks"
)
elif "metformin" in alert["description"].lower():
st.markdown(
"- **eGFR:** Trending 62 → 48 over 3 months\n"
"- **Lactate:** Check if eGFR < 45\n"
"- **HbA1c:** Due for recheck\n"
"- **BMP:** Next scheduled draw"
)
else:
st.markdown(
"- **CBC:** Monitor for bleeding (bruising)\n"
"- **PT/INR:** If on anticoagulant\n"
"- **Fall risk assessment:** Ongoing"
)
with rec_col:
st.markdown("**Recommendation:**")
st.info(alert["recommendation"])
# --- TAB: WOUND VISION AI ---
with tab_vision:
st.subheader("Wound / Skin Vision Analysis")
st.caption("Upload a wound photograph for AI-powered staging and assessment.")
# Sample demo option
use_sample_wound = st.checkbox(
"Use built-in sample image (Sacral Pressure Ulcer - Demo)",
key="use_sample_wound",
)
if use_sample_wound:
sample_path = os.path.join(_ASSETS_DIR, "sample_wound.jpg")
if os.path.exists(sample_path):
st.image(sample_path, width=400, caption="Sample: Sacral Pressure Ulcer (Synthetic Demo)")
if st.button("Run Vision Diagnostics", type="primary", key="btn_vision_sample"):
with st.spinner("Vision agent analyzing image..."):
result = analyze_wound_image(None)
st.success("Analysis complete!")
st.markdown(result["summary"])
with st.expander("Detailed Results (JSON)"):
st.json(result["result"])
with st.expander("FHIR Observation (auto-generated)"):
st.code(
'{\n'
' "resourceType": "Observation",\n'
' "status": "final",\n'
' "category": [{"coding": [{"code": "exam", "display": "Exam"}]}],\n'
f' "code": {{"text": "Wound Assessment - {result["result"]["stage"]}"}},\n'
f' "bodySite": {{"text": "{result["result"]["location"]}"}},\n'
f' "valueString": "{result["result"]["size_cm"]} cm, {result["result"]["stage"]}",\n'
' "interpretation": [{"text": "AI Vision Analysis"}],\n'
f' "note": [{{"text": "{result["result"]["recommendation"]}"}}]\n'
'}',
language="json",
)
else:
st.warning("Sample file not found. Please upload an image instead.")
else:
img_file = st.file_uploader("Upload Wound Photo", type=["jpg", "jpeg", "png"], key="wound_upload")
if img_file:
st.image(img_file, width=350, caption="Uploaded Image")
if st.button("Run Vision Diagnostics", type="primary", key="btn_vision"):
with st.spinner("Vision agent analyzing image..."):
result = analyze_wound_image(img_file)
st.success("Analysis complete!")
st.markdown(result["summary"])
with st.expander("Detailed Results (JSON)"):
st.json(result["result"])
with st.expander("FHIR Observation (auto-generated)"):
st.code(
'{\n'
' "resourceType": "Observation",\n'
' "status": "final",\n'
' "category": [{"coding": [{"code": "exam", "display": "Exam"}]}],\n'
f' "code": {{"text": "Wound Assessment - {result["result"]["stage"]}"}},\n'
f' "bodySite": {{"text": "{result["result"]["location"]}"}},\n'
f' "valueString": "{result["result"]["size_cm"]} cm, {result["result"]["stage"]}",\n'
' "interpretation": [{"text": "AI Vision Analysis"}],\n'
f' "note": [{{"text": "{result["result"]["recommendation"]}"}}]\n'
'}',
language="json",
)
# --- TAB: RESPIRATORY AUDIO AI ---
with tab_audio:
st.subheader("Respiratory Bioacoustic Analysis")
st.caption("Upload a lung sound or cough recording for AI-powered respiratory assessment.")
use_sample_audio = st.checkbox(
"Use built-in sample audio (Simulated Respiratory Recording - Demo)",
key="use_sample_audio",
)
if use_sample_audio:
sample_path = os.path.join(_ASSETS_DIR, "sample_respiratory.wav")
if os.path.exists(sample_path):
st.audio(sample_path, format="audio/wav")
st.caption("Sample: Simulated lung sounds with cough events at ~1.2s and ~2.8s")
if st.button("Run Audio Bioacoustics", type="primary", key="btn_audio_sample"):
with st.spinner("Audio agent analyzing recording..."):
result = analyze_respiratory_audio(None)
st.success("Analysis complete!")
st.markdown(result["summary"])
# Visual analysis display
col_a, col_b = st.columns(2)
with col_a:
st.markdown("**Detection Summary:**")
st.markdown(
f"- **Cough Rate:** {result['result']['cough_rate_per_hr']} coughs/hour\n"
f"- **Cough Type:** {result['result']['cough_type']}\n"
f"- **Trend:** {result['result']['trend']}\n"
f"- **Pneumonia Risk:** {result['result']['pneumonia_risk_pct']}%"
)
with col_b:
st.markdown("**AI Recommendation:**")
st.info(result["result"]["recommendation"])
with st.expander("Detailed Results (JSON)"):
st.json(result["result"])
else:
st.warning("Sample file not found. Please upload an audio file instead.")
else:
audio_file = st.file_uploader("Upload Audio Recording", type=["mp3", "wav"], key="audio_upload")
if audio_file:
st.audio(audio_file)
if st.button("Run Audio Bioacoustics", type="primary", key="btn_audio"):
with st.spinner("Audio agent analyzing recording..."):
result = analyze_respiratory_audio(audio_file)
st.success("Analysis complete!")
st.markdown(result["summary"])
col_a, col_b = st.columns(2)
with col_a:
st.markdown("**Detection Summary:**")
st.markdown(
f"- **Cough Rate:** {result['result']['cough_rate_per_hr']} coughs/hour\n"
f"- **Cough Type:** {result['result']['cough_type']}\n"
f"- **Trend:** {result['result']['trend']}\n"
f"- **Pneumonia Risk:** {result['result']['pneumonia_risk_pct']}%"
)
with col_b:
st.markdown("**AI Recommendation:**")
st.info(result["result"]["recommendation"])
with st.expander("Detailed Results (JSON)"):
st.json(result["result"])
# --- TAB: ADMINISTRATIVE OCR ---
with tab_ocr:
st.subheader("Administrative Form Digitizer")
st.caption("Convert paper orders, lab reports, and invoices into structured HL7/FHIR-ready data.")
use_sample_form = st.checkbox(
"Use built-in sample form (Lab Order - Margaret Chen - Demo)",
key="use_sample_form",
)
if use_sample_form:
sample_path = os.path.join(_ASSETS_DIR, "sample_lab_order.jpg")
if os.path.exists(sample_path):
st.image(sample_path, width=450, caption="Sample: Laboratory Order Form (Synthetic Demo)")
if st.button("Digitize Form", type="primary", key="btn_ocr_sample"):
with st.spinner("OCR agent processing document..."):
result = process_ocr_form(None)
st.success(
f"Form digitized successfully. Type: **{result['result']['form_type']}**. "
f"Confidence: **{result['result'].get('confidence', 'N/A')}**. "
f"Output format: HL7 {result['hl7_version']} / FHIR R4."
)
col_o1, col_o2 = st.columns(2)
with col_o1:
st.markdown("**Extracted Fields:**")
st.json(result["result"])
with col_o2:
st.markdown("**FHIR ServiceRequest (auto-generated):**")
st.code(
'{\n'
' "resourceType": "ServiceRequest",\n'
' "status": "active",\n'
' "intent": "order",\n'
f' "subject": {{"reference": "Patient/{result["result"].get("mrn", "MC-402")}"}},\n'
f' "requester": {{"display": "{result["result"].get("ordering_physician", "Dr. Sarah Kim")}"}},\n'
' "code": {"text": "Laboratory Tests"},\n'
' "priority": "routine",\n'
' "authoredOn": "2026-03-26"\n'
'}',
language="json",
)
st.markdown("**HL7 v2.5.1 Message (auto-generated):**")
st.code(
"MSH|^~\\&|SENIORCARE|LAB|EHR|FACILITY|20260326||ORM^O01|MSG001|P|2.5.1\n"
f"PID|1||{result['result'].get('mrn', 'MC-402')}||{result['result'].get('patient', 'Chen^Margaret')}||\n"
"ORC|NW|ORD001||||||20260326|||Dr. Sarah Kim\n"
"OBR|1|ORD001||CBC^Complete Blood Count|||20260326\n"
"OBR|2|ORD001||CMP^Comprehensive Metabolic Panel|||20260326\n"
"OBR|3|ORD001||UA^Urinalysis with Culture|||20260326",
language="text",
)
else:
st.warning("Sample file not found. Please upload a form instead.")
else:
admin_doc = st.file_uploader("Upload Form (Image/PDF)", type=["jpg", "jpeg", "png", "pdf"], key="ocr_upload")
if admin_doc:
if admin_doc.type and "image" in admin_doc.type:
st.image(admin_doc, width=300, caption="Uploaded Form")
else:
st.info(f"Uploaded: {admin_doc.name}")
if st.button("Digitize Form", type="primary", key="btn_ocr"):
with st.spinner("OCR agent processing document..."):
result = process_ocr_form(admin_doc)
st.success(
f"Form digitized successfully. Type: **{result['result']['form_type']}**. "
f"Confidence: **{result['result'].get('confidence', 'N/A')}**. "
f"Output format: HL7 {result['hl7_version']} / FHIR R4."
)
with st.expander("Extracted Data"):
st.json(result["result"])
# --- TAB: LAB INTELLIGENCE CHATBOT ---
with tab_chat:
st.subheader("Lab Intelligence Assistant")
st.caption(
"AI-powered lab Q&A. Ask about turnaround times, specimen tracking, "
"critical values, infection surveillance, and diagnostic AI modules."
)
render_chatbot(persona="lab_admin")