| """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 |
|
|
| |
| _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() |
|
|
| |
| 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", |
| ]) |
|
|
| |
| 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) |
|
|
| |
| 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() |
|
|
| |
| 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() |
|
|
| |
| 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"""<div style="border-left: 4px solid {severity_color}; |
| padding: 12px 16px; background: white; border-radius: 0 8px 8px 0;"> |
| <strong>{alert['alert_type']}</strong> — Severity: {alert['severity']} |
| — Confidence: {alert['confidence']:.0%} |
| </div>""", |
| 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"]) |
|
|
| |
| with tab_vision: |
| st.subheader("Wound / Skin Vision Analysis") |
| st.caption("Upload a wound photograph for AI-powered staging and assessment.") |
|
|
| |
| 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", |
| ) |
|
|
| |
| 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"]) |
|
|
| |
| 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"]) |
|
|
| |
| 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"]) |
|
|
| |
| 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") |
|
|