File size: 5,017 Bytes
79b0bef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""
dashboard/app.py
────────────────────────────────────────────────────────────────
Streamlit application entry point.

Run locally:
    streamlit run dashboard/app.py

Deploy to Streamlit Cloud:
    1. Push this repo to GitHub
    2. Go to share.streamlit.io β†’ New app
    3. Set main file path: dashboard/app.py
    4. Add secrets (see VSCODE_GUIDE.md for full instructions):
         API_BASE_URL = "https://your-api-host.com"
         DATABASE_URL = "postgresql://..."

Navigation
──────────
  πŸ”¬ Demo       β€” paste clinical text, get real-time analysis
  πŸ“Š Explorer   β€” entity frequency charts and co-occurrence graph
  🧠 Metrics    β€” classifier performance and training history
────────────────────────────────────────────────────────────────
"""

from __future__ import annotations

import sys
from pathlib import Path

# Streamlit runs this script directly (`streamlit run dashboard/app.py`),
# which puts this file's own directory (dashboard/) on sys.path -- not the
# project root. That makes `dashboard` itself unimportable as a package
# from within its own folder. Insert the project root (this file's parent)
# so absolute imports like `from dashboard.api_client import ...` resolve,
# same pattern the notebooks use (`sys.path.insert(0, '..')`).
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

import streamlit as st

from dashboard.api_client import check_health
from dashboard.views import demo, explorer, model_metrics

# ── Page config ───────────────────────────────────────────────────
# Must be the first Streamlit call in the script.
st.set_page_config(
    page_title     = "Clinical NLP Pipeline",
    page_icon      = "πŸ”¬",
    layout         = "wide",
    initial_sidebar_state = "expanded",
)

# ── Custom CSS ────────────────────────────────────────────────────
# Minimal styling β€” keeps the app looking clean without overriding
# Streamlit's built-in theme.
st.markdown("""
<style>
  /* Tighten the sidebar width slightly */
  [data-testid="stSidebar"] { min-width: 220px; max-width: 280px; }

  /* Subtle card background for metric containers */
  [data-testid="metric-container"] {
    background: #f8f9fa;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 12px 16px;
  }

  /* Reduce top padding on the main content area */
  .block-container { padding-top: 1.5rem; }
</style>
""", unsafe_allow_html=True)


# ── Sidebar navigation ────────────────────────────────────────────

with st.sidebar:
    st.markdown("## πŸ”¬ Clinical NLP")
    st.markdown("---")

    page = st.radio(
        label     = "Navigation",
        options   = ["Demo", "Explorer", "Metrics"],
        format_func = lambda p: {
            "Demo":     "πŸ”¬  Live Demo",
            "Explorer": "πŸ“Š  Explorer",
            "Metrics":  "🧠  Model Metrics",
        }[p],
        label_visibility = "collapsed",
    )

    st.markdown("---")

    # API health indicator
    health = check_health()
    status = health.get("status", "unknown")
    db_status = health.get("database", "unknown")

    colour = "#2ecc71" if status == "ok" else "#e74c3c"
    st.markdown(
        f"<div style='font-size:0.8rem; color:#666'>"
        f"API &nbsp;"
        f"<span style='color:{colour}; font-weight:600'>"
        f"{'● online' if status == 'ok' else '● offline'}</span>"
        f"</div>",
        unsafe_allow_html=True,
    )
    if db_status not in ("unknown", "unreachable"):
        db_colour = "#2ecc71" if db_status == "connected" else "#e74c3c"
        st.markdown(
            f"<div style='font-size:0.8rem; color:#666'>"
            f"DB &nbsp;"
            f"<span style='color:{db_colour}; font-weight:600'>"
            f"{'● connected' if db_status == 'connected' else '● unreachable'}</span>"
            f"</div>",
            unsafe_allow_html=True,
        )

    st.markdown("---")
    st.markdown(
        "<div style='font-size:0.75rem; color:#aaa'>"
        "Clinical NLP Pipeline Β· v1.0<br>"
        "Built with spaCy Β· Bio_ClinicalBERT<br>"
        "FastAPI Β· Streamlit"
        "</div>",
        unsafe_allow_html=True,
    )


# ── Page routing ──────────────────────────────────────────────────

if page == "Demo":
    demo.render()
elif page == "Explorer":
    explorer.render()
elif page == "Metrics":
    model_metrics.render()