zeon01 commited on
Commit
0258ba4
·
verified ·
1 Parent(s): ec3e5ab

Upload folder using huggingface_hub

Browse files
app.py CHANGED
@@ -44,8 +44,7 @@ def make_backend():
44
 
45
  def maybe_seed(conn) -> None:
46
  """On the demo Space (WC_SEED_DEMO=1) only, populate an empty DB with a realistic
47
- 60-day decline arc so the Trends/Report tabs are non-empty on first open. Real local
48
- users (no env var) always start with a clean log."""
49
  if os.environ.get("WC_SEED_DEMO") != "1":
50
  return
51
  if conn.execute("SELECT COUNT(*) FROM entries").fetchone()[0] > 0:
@@ -58,15 +57,36 @@ def maybe_seed(conn) -> None:
58
  print(f"[seed] skipped: {e}")
59
 
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  def build_app():
62
  conn = init_db(DB_PATH)
63
  maybe_seed(conn)
64
  backend = make_backend()
65
- with gr.Blocks(title="What Changed") as demo:
66
- gr.Markdown(
67
- "# What Changed\n"
68
- "*A private, local tracker to show the doctor what's been happening at home. "
69
- "Not medical advice.*")
70
  build_log_tab(conn, backend)
71
  build_trends_tab(conn)
72
  build_report_tab(conn, backend)
 
44
 
45
  def maybe_seed(conn) -> None:
46
  """On the demo Space (WC_SEED_DEMO=1) only, populate an empty DB with a realistic
47
+ 60-day decline arc so Trends/Report are non-empty on first open."""
 
48
  if os.environ.get("WC_SEED_DEMO") != "1":
49
  return
50
  if conn.execute("SELECT COUNT(*) FROM entries").fetchone()[0] > 0:
 
57
  print(f"[seed] skipped: {e}")
58
 
59
 
60
+ # --- Off-Brand custom look: calm health palette, Inter, branded header, no Gradio footer ---
61
+ THEME = gr.themes.Soft(
62
+ primary_hue="teal", secondary_hue="cyan", neutral_hue="slate",
63
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
64
+ )
65
+ CSS = """
66
+ .gradio-container {max-width: 880px !important; margin: 0 auto !important;}
67
+ #wc-header {background: linear-gradient(135deg,#0d9488,#0e7490); color:#fff;
68
+ padding:22px 26px; border-radius:16px; margin-bottom:6px;}
69
+ #wc-header h1 {margin:0; font-size:26px; font-weight:700; color:#fff;}
70
+ #wc-header p {margin:6px 0 0; opacity:.92; font-size:14px; color:#fff;}
71
+ #wc-header .wc-pill {display:inline-block; background:rgba(255,255,255,.18);
72
+ padding:3px 11px; border-radius:999px; font-size:12px; margin-top:11px;}
73
+ footer {display:none !important;}
74
+ """
75
+ HEADER = (
76
+ '<div id="wc-header"><h1>📋 What Changed</h1>'
77
+ "<p>A private, on-device Parkinson's diary — describe the day, and a local fine-tuned 1B "
78
+ "structures it into a doctor-ready summary.</p>"
79
+ '<span class="wc-pill">Runs 100% locally · fine-tuned 1B · llama.cpp · not medical advice</span>'
80
+ "</div>"
81
+ )
82
+
83
+
84
  def build_app():
85
  conn = init_db(DB_PATH)
86
  maybe_seed(conn)
87
  backend = make_backend()
88
+ with gr.Blocks(title="What Changed", theme=THEME, css=CSS) as demo:
89
+ gr.HTML(HEADER)
 
 
 
90
  build_log_tab(conn, backend)
91
  build_trends_tab(conn)
92
  build_report_tab(conn, backend)
whatchanged/ui/log_tab.py CHANGED
@@ -2,27 +2,53 @@ from __future__ import annotations
2
  import datetime
3
  import gradio as gr
4
  from whatchanged.models import Entry, DOMAINS, EVENTS
5
- from whatchanged.db import upsert_entry, get_entry
6
  from whatchanged.inference import extract_note
7
  from whatchanged.trends import DOMAIN_LABELS, EVENT_LABELS
8
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  def build_log_tab(conn, backend):
11
- with gr.Tab("Log Today"):
12
- gr.Markdown("### How was today? Tap what applies — takes ~15 seconds.")
 
 
13
  date = gr.Textbox(label="Date", value=str(datetime.date.today()))
 
 
 
 
 
 
 
 
 
14
  sliders = {}
15
  for d in DOMAINS:
16
- sliders[d] = gr.Slider(1, 5, step=1, value=3, label=DOMAIN_LABELS[d])
 
 
 
17
  counters = {}
18
- for ev in EVENTS:
19
- counters[ev] = gr.Number(value=0, precision=0, label=EVENT_LABELS[ev],
20
- minimum=0)
21
- note = gr.Textbox(label="Optional note", lines=2,
22
- placeholder="e.g. more confused this evening, skipped lunch")
23
- extract_btn = gr.Button("Auto-fill from note (local AI)")
 
24
  save_btn = gr.Button("Save today", variant="primary")
25
- status = gr.Markdown()
26
 
27
  def do_extract(note_text, *slider_vals):
28
  data = extract_note(note_text, backend)
@@ -31,20 +57,24 @@ def build_log_tab(conn, backend):
31
  if d in data:
32
  new[i] = data[d]
33
  counter_updates = [data.get(ev, gr.update()) for ev in EVENTS]
34
- return (*new, *counter_updates,
35
- f"Filled {len(data)} field(s) from the note. Please review.")
 
 
 
 
 
36
 
37
  extract_btn.click(
38
  do_extract,
39
  inputs=[note] + [sliders[d] for d in DOMAINS],
40
  outputs=[sliders[d] for d in DOMAINS] + [counters[ev] for ev in EVENTS]
41
- + [status],
42
  )
43
 
44
  def do_save(date_val, note_text, *vals):
45
  n = len(DOMAINS)
46
- domain_vals = vals[:n]
47
- event_vals = vals[n:]
48
 
49
  def _to_int(v):
50
  return int(v) if v not in (None, "") else 0
@@ -59,5 +89,5 @@ def build_log_tab(conn, backend):
59
  do_save,
60
  inputs=[date, note] + [sliders[d] for d in DOMAINS]
61
  + [counters[ev] for ev in EVENTS],
62
- outputs=[status],
63
  )
 
2
  import datetime
3
  import gradio as gr
4
  from whatchanged.models import Entry, DOMAINS, EVENTS
5
+ from whatchanged.db import upsert_entry
6
  from whatchanged.inference import extract_note
7
  from whatchanged.trends import DOMAIN_LABELS, EVENT_LABELS
8
 
9
+ # Plain-language anchors so a caregiver knows what each 1-5 rating means (5 = best). These
10
+ # rate the *quality* of the day, not a number of hours/etc.
11
+ ANCHORS = {
12
+ "mobility": "1 = could barely move · 5 = moved freely & steadily",
13
+ "tremor": "1 = severe tremor · 5 = almost no tremor",
14
+ "stiffness": "1 = very stiff & rigid · 5 = loose, not stiff",
15
+ "mood": "1 = very low · 5 = bright & cheerful",
16
+ "sleep": "1 = barely slept · 5 = slept very well",
17
+ "alertness": "1 = very confused & foggy · 5 = sharp & clear",
18
+ }
19
+
20
 
21
  def build_log_tab(conn, backend):
22
+ with gr.Tab("📝 Log Today"):
23
+ gr.Markdown(
24
+ "### Just describe the day — the on-device AI fills the form, you review.\n"
25
+ "Type a sentence or two about how your parent did today, then tap **Auto-fill**.")
26
  date = gr.Textbox(label="Date", value=str(datetime.date.today()))
27
+ note = gr.Textbox(
28
+ label="Today's note", lines=3, autofocus=True,
29
+ placeholder='e.g. "Rough day — Dad froze in the doorway twice, his meds wore off '
30
+ 'before lunch, and he barely slept."')
31
+ extract_btn = gr.Button("✨ Auto-fill from note (local AI)",
32
+ variant="primary", size="lg")
33
+ extract_status = gr.Markdown()
34
+
35
+ gr.Markdown("#### Review & adjust")
36
  sliders = {}
37
  for d in DOMAINS:
38
+ sliders[d] = gr.Slider(1, 5, step=1, value=3, label=DOMAIN_LABELS[d],
39
+ info=ANCHORS[d])
40
+
41
+ gr.Markdown("**Events today** — how many times each happened")
42
  counters = {}
43
+ with gr.Row():
44
+ for ev in EVENTS[:3]:
45
+ counters[ev] = gr.Number(value=0, precision=0, label=EVENT_LABELS[ev], minimum=0)
46
+ with gr.Row():
47
+ for ev in EVENTS[3:]:
48
+ counters[ev] = gr.Number(value=0, precision=0, label=EVENT_LABELS[ev], minimum=0)
49
+
50
  save_btn = gr.Button("Save today", variant="primary")
51
+ save_status = gr.Markdown()
52
 
53
  def do_extract(note_text, *slider_vals):
54
  data = extract_note(note_text, backend)
 
57
  if d in data:
58
  new[i] = data[d]
59
  counter_updates = [data.get(ev, gr.update()) for ev in EVENTS]
60
+ if backend is None:
61
+ msg = "⚠️ The local model isn't loaded right now — enter values manually below."
62
+ elif not note_text.strip():
63
+ msg = "Write a note above first, then tap Auto-fill."
64
+ else:
65
+ msg = f"✓ Filled **{len(data)}** field(s) from your note — please review below."
66
+ return (*new, *counter_updates, msg)
67
 
68
  extract_btn.click(
69
  do_extract,
70
  inputs=[note] + [sliders[d] for d in DOMAINS],
71
  outputs=[sliders[d] for d in DOMAINS] + [counters[ev] for ev in EVENTS]
72
+ + [extract_status],
73
  )
74
 
75
  def do_save(date_val, note_text, *vals):
76
  n = len(DOMAINS)
77
+ domain_vals, event_vals = vals[:n], vals[n:]
 
78
 
79
  def _to_int(v):
80
  return int(v) if v not in (None, "") else 0
 
89
  do_save,
90
  inputs=[date, note] + [sliders[d] for d in DOMAINS]
91
  + [counters[ev] for ev in EVENTS],
92
+ outputs=[save_status],
93
  )
whatchanged/ui/report_tab.py CHANGED
@@ -6,7 +6,7 @@ from whatchanged.report import build_report, render_report_html
6
 
7
 
8
  def build_report_tab(conn, backend):
9
- with gr.Tab("Doctor Report"):
10
  gr.Markdown("### Generate a one-page summary to share with the doctor.")
11
  with gr.Row():
12
  start = gr.Textbox(
 
6
 
7
 
8
  def build_report_tab(conn, backend):
9
+ with gr.Tab("🩺 Doctor Report"):
10
  gr.Markdown("### Generate a one-page summary to share with the doctor.")
11
  with gr.Row():
12
  start = gr.Textbox(
whatchanged/ui/trends_tab.py CHANGED
@@ -7,7 +7,7 @@ from whatchanged.trends import detect_findings
7
 
8
 
9
  def build_trends_tab(conn):
10
- with gr.Tab("Trends"):
11
  window = gr.Radio([7, 30, 90], value=30, label="Window (days)")
12
  refresh = gr.Button("Refresh", variant="primary")
13
  plot = gr.Plot()
 
7
 
8
 
9
  def build_trends_tab(conn):
10
+ with gr.Tab("📈 Trends"):
11
  window = gr.Radio([7, 30, 90], value=30, label="Window (days)")
12
  refresh = gr.Button("Refresh", variant="primary")
13
  plot = gr.Plot()