Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
# ================================
|
| 2 |
# πͺ MoodMirror+ β Text Emotion β’ Advice-only + brief intros & reasons
|
| 3 |
-
# -
|
| 4 |
-
# - Adds a "Breathing" guided exercise tab
|
| 5 |
# ================================
|
| 6 |
import os
|
| 7 |
import re
|
|
@@ -123,10 +122,19 @@ def get_conn():
|
|
| 123 |
|
| 124 |
def init_db():
|
| 125 |
conn = get_conn()
|
|
|
|
| 126 |
conn.execute("""CREATE TABLE IF NOT EXISTS sessions(
|
| 127 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 128 |
ts TEXT, country TEXT, user_text TEXT, main_emotion TEXT
|
| 129 |
)""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
conn.commit()
|
| 131 |
conn.close()
|
| 132 |
|
|
@@ -137,6 +145,77 @@ def log_session(country, msg, emotion):
|
|
| 137 |
conn.commit()
|
| 138 |
conn.close()
|
| 139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
# ---------------- Model ----------------
|
| 141 |
def load_goemotions_dataset():
|
| 142 |
ds = load_dataset("google-research-datasets/go_emotions", "simplified")
|
|
@@ -273,8 +352,7 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 273 |
|
| 274 |
# ---- Tab 3: Breathing ----
|
| 275 |
with gr.Tab("Breathing"):
|
| 276 |
-
gr.Markdown("#### π¬οΈ Guided breathing\nPick a pattern and cycles, then press **Start**. "
|
| 277 |
-
"If you feel dizzy, stop and breathe normally.")
|
| 278 |
with gr.Row():
|
| 279 |
pattern = gr.Dropdown(
|
| 280 |
choices=["4-7-8", "Box (4-4-4-4)", "Coherent (5-5, ~6 breaths/min)"],
|
|
@@ -302,6 +380,84 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 302 |
yield "β
Done. Notice how your body feels."
|
| 303 |
start_btn.click(run_breathing, inputs=[pattern, cycles], outputs=[breathe_out])
|
| 304 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
if __name__ == "__main__":
|
|
|
|
| 306 |
demo.queue()
|
| 307 |
demo.launch()
|
|
|
|
| 1 |
# ================================
|
| 2 |
# πͺ MoodMirror+ β Text Emotion β’ Advice-only + brief intros & reasons
|
| 3 |
+
# - Tabs: Advice β’ Emergency numbers β’ Breathing β’ Journal (with export & delete)
|
|
|
|
| 4 |
# ================================
|
| 5 |
import os
|
| 6 |
import re
|
|
|
|
| 122 |
|
| 123 |
def init_db():
|
| 124 |
conn = get_conn()
|
| 125 |
+
# sessions
|
| 126 |
conn.execute("""CREATE TABLE IF NOT EXISTS sessions(
|
| 127 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 128 |
ts TEXT, country TEXT, user_text TEXT, main_emotion TEXT
|
| 129 |
)""")
|
| 130 |
+
# journal
|
| 131 |
+
conn.execute("""CREATE TABLE IF NOT EXISTS journal(
|
| 132 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 133 |
+
ts TEXT NOT NULL,
|
| 134 |
+
emotion TEXT,
|
| 135 |
+
title TEXT,
|
| 136 |
+
content TEXT
|
| 137 |
+
)""")
|
| 138 |
conn.commit()
|
| 139 |
conn.close()
|
| 140 |
|
|
|
|
| 145 |
conn.commit()
|
| 146 |
conn.close()
|
| 147 |
|
| 148 |
+
# ---- Journal helpers ----
|
| 149 |
+
def journal_save(title: str, content: str, emotion: str):
|
| 150 |
+
title = (title or "").strip()
|
| 151 |
+
content = (content or "").strip()
|
| 152 |
+
if not content:
|
| 153 |
+
return False, "Please write something before saving."
|
| 154 |
+
ts = datetime.utcnow().isoformat(timespec='seconds')
|
| 155 |
+
conn = get_conn()
|
| 156 |
+
conn.execute("INSERT INTO journal(ts, emotion, title, content) VALUES (?,?,?,?)",
|
| 157 |
+
(ts, emotion or "", title, content))
|
| 158 |
+
conn.commit()
|
| 159 |
+
conn.close()
|
| 160 |
+
return True, f"Saved β ({ts} UTC)."
|
| 161 |
+
|
| 162 |
+
def journal_list(search: str = "", limit: int = 50):
|
| 163 |
+
q = "SELECT id, ts, emotion, title, content FROM journal"
|
| 164 |
+
params = []
|
| 165 |
+
if search:
|
| 166 |
+
q += " WHERE (LOWER(title) LIKE ? OR LOWER(content) LIKE ? OR LOWER(emotion) LIKE ?)"
|
| 167 |
+
s = f"%{search.lower()}%"
|
| 168 |
+
params = [s, s, s]
|
| 169 |
+
q += " ORDER BY ts DESC LIMIT ?"
|
| 170 |
+
params.append(int(limit))
|
| 171 |
+
conn = get_conn()
|
| 172 |
+
rows = list(conn.execute(q, params))
|
| 173 |
+
conn.close()
|
| 174 |
+
options = []
|
| 175 |
+
table = []
|
| 176 |
+
for (id_, ts, emo, title, content) in rows:
|
| 177 |
+
label = f"{ts} β [{(emo or 'neutral')}] {title or (content[:30] + ('β¦' if len(content) > 30 else ''))}"
|
| 178 |
+
options.append((label, id_))
|
| 179 |
+
preview = (content or "").replace("\n", " ")
|
| 180 |
+
if len(preview) > 120: preview = preview[:120] + "β¦"
|
| 181 |
+
table.append([ts, emo or "β", title or "β", preview])
|
| 182 |
+
return options, table
|
| 183 |
+
|
| 184 |
+
def journal_get(entry_id: int):
|
| 185 |
+
conn = get_conn()
|
| 186 |
+
cur = conn.execute("SELECT ts, emotion, title, content FROM journal WHERE id = ?", (int(entry_id),))
|
| 187 |
+
row = cur.fetchone()
|
| 188 |
+
conn.close()
|
| 189 |
+
if not row: return None
|
| 190 |
+
ts, emo, title, content = row
|
| 191 |
+
return {"ts": ts, "emotion": emo or "", "title": title or "", "content": content or ""}
|
| 192 |
+
|
| 193 |
+
def journal_delete(entry_id: int):
|
| 194 |
+
conn = get_conn()
|
| 195 |
+
cur = conn.execute("DELETE FROM journal WHERE id = ?", (int(entry_id),))
|
| 196 |
+
conn.commit()
|
| 197 |
+
changes = conn.total_changes
|
| 198 |
+
conn.close()
|
| 199 |
+
return changes > 0
|
| 200 |
+
|
| 201 |
+
def journal_export_txt(entry_id: int):
|
| 202 |
+
data = journal_get(entry_id)
|
| 203 |
+
if not data:
|
| 204 |
+
return None, "Entry not found."
|
| 205 |
+
fname = f"journal_{entry_id}_{data['ts'].replace(':','-')}.txt"
|
| 206 |
+
fpath = os.path.join(DATA_DIR, fname)
|
| 207 |
+
# Build plain text
|
| 208 |
+
lines = []
|
| 209 |
+
title = data["title"] or "(Untitled)"
|
| 210 |
+
lines.append(f"Title: {title}")
|
| 211 |
+
lines.append(f"Emotion: {data['emotion'] or '-'}")
|
| 212 |
+
lines.append(f"Saved (UTC): {data['ts']}")
|
| 213 |
+
lines.append("-" * 40)
|
| 214 |
+
lines.append(data["content"])
|
| 215 |
+
with open(fpath, "w", encoding="utf-8") as f:
|
| 216 |
+
f.write("\n".join(lines))
|
| 217 |
+
return fpath, f"Ready: {fname}"
|
| 218 |
+
|
| 219 |
# ---------------- Model ----------------
|
| 220 |
def load_goemotions_dataset():
|
| 221 |
ds = load_dataset("google-research-datasets/go_emotions", "simplified")
|
|
|
|
| 352 |
|
| 353 |
# ---- Tab 3: Breathing ----
|
| 354 |
with gr.Tab("Breathing"):
|
| 355 |
+
gr.Markdown("#### π¬οΈ Guided breathing\nPick a pattern and cycles, then press **Start**. If you feel dizzy, stop and breathe normally.")
|
|
|
|
| 356 |
with gr.Row():
|
| 357 |
pattern = gr.Dropdown(
|
| 358 |
choices=["4-7-8", "Box (4-4-4-4)", "Coherent (5-5, ~6 breaths/min)"],
|
|
|
|
| 380 |
yield "β
Done. Notice how your body feels."
|
| 381 |
start_btn.click(run_breathing, inputs=[pattern, cycles], outputs=[breathe_out])
|
| 382 |
|
| 383 |
+
# ---- Tab 4: Journal (with export & delete) ----
|
| 384 |
+
with gr.Tab("Journal"):
|
| 385 |
+
gr.Markdown("#### π Personal journal\nWrite freely for a couple of minutes, then save. Entries are stored locally.")
|
| 386 |
+
|
| 387 |
+
with gr.Row():
|
| 388 |
+
j_title = gr.Textbox(label="Title (optional)", placeholder="e.g., A tough day at work")
|
| 389 |
+
j_emotion = gr.Dropdown(
|
| 390 |
+
choices=["neutral","sadness","fear","anger","nervousness","boredom","grief","love","joy","curiosity","gratitude"],
|
| 391 |
+
value="neutral", label="Emotion tag"
|
| 392 |
+
)
|
| 393 |
+
j_text = gr.Textbox(lines=10, label="Your entry", placeholder="Write whatever you want to get off your chest...")
|
| 394 |
+
|
| 395 |
+
with gr.Row():
|
| 396 |
+
j_save = gr.Button("Save entry", variant="primary")
|
| 397 |
+
j_status = gr.Markdown()
|
| 398 |
+
|
| 399 |
+
gr.Markdown("##### Your entries")
|
| 400 |
+
with gr.Row():
|
| 401 |
+
j_search = gr.Textbox(label="Search (title, text or emotion)", placeholder="e.g., anxiety, work, joy")
|
| 402 |
+
j_refresh = gr.Button("Refresh list")
|
| 403 |
+
j_entries = gr.Dropdown(label="Entries (newest first)", choices=[], value=None, interactive=True)
|
| 404 |
+
j_table = gr.Dataframe(headers=["UTC time","Emotion","Title","Preview"], value=[], interactive=False, wrap=True, height=200)
|
| 405 |
+
j_view = gr.Markdown()
|
| 406 |
+
|
| 407 |
+
# Export + Delete UI
|
| 408 |
+
with gr.Row():
|
| 409 |
+
j_export_btn = gr.Button("Export .txt")
|
| 410 |
+
j_export_file = gr.File(label="Download exported entry", visible=True)
|
| 411 |
+
j_delete_btn = gr.Button("Delete entry", variant="stop")
|
| 412 |
+
|
| 413 |
+
# Backend glue
|
| 414 |
+
def _refresh_entries(search):
|
| 415 |
+
options, table = journal_list(search or "", 50)
|
| 416 |
+
return gr.Dropdown(choices=options, value=None), table
|
| 417 |
+
|
| 418 |
+
def _save_entry(title, text, emotion, search):
|
| 419 |
+
ok, msg = journal_save(title, text, emotion)
|
| 420 |
+
drop, table = _refresh_entries(search)
|
| 421 |
+
clear_text = "" if ok else text
|
| 422 |
+
clear_title = "" if ok else title
|
| 423 |
+
return msg, drop, table, clear_text, clear_title
|
| 424 |
+
|
| 425 |
+
def _load_entry(entry_id):
|
| 426 |
+
if entry_id is None: return ""
|
| 427 |
+
data = journal_get(entry_id)
|
| 428 |
+
if not data: return "Entry not found."
|
| 429 |
+
title_line = f"### {data['title']}" if data['title'] else "### (Untitled)"
|
| 430 |
+
emo_line = f"**Emotion:** {data['emotion'] or 'β'} \n**Saved (UTC):** {data['ts']}"
|
| 431 |
+
return f"{title_line}\n\n{emo_line}\n\n---\n\n{data['content']}"
|
| 432 |
+
|
| 433 |
+
def _export_entry(entry_id):
|
| 434 |
+
if entry_id is None:
|
| 435 |
+
return None, "Select an entry to export."
|
| 436 |
+
path, msg = journal_export_txt(int(entry_id))
|
| 437 |
+
return path, msg
|
| 438 |
+
|
| 439 |
+
def _delete_entry(entry_id, search):
|
| 440 |
+
if entry_id is None:
|
| 441 |
+
status = "Select an entry to delete."
|
| 442 |
+
drop, table = _refresh_entries(search)
|
| 443 |
+
return status, drop, table, ""
|
| 444 |
+
ok = journal_delete(int(entry_id))
|
| 445 |
+
status = "Deleted β" if ok else "Entry not found."
|
| 446 |
+
drop, table = _refresh_entries(search)
|
| 447 |
+
return status, drop, table, ""
|
| 448 |
+
|
| 449 |
+
# Wire actions
|
| 450 |
+
j_save.click(_save_entry, inputs=[j_title, j_text, j_emotion, j_search],
|
| 451 |
+
outputs=[j_status, j_entries, j_table, j_text, j_title])
|
| 452 |
+
j_refresh.click(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
| 453 |
+
j_search.submit(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
| 454 |
+
j_entries.change(_load_entry, inputs=[j_entries], outputs=[j_view])
|
| 455 |
+
|
| 456 |
+
j_export_btn.click(_export_entry, inputs=[j_entries], outputs=[j_export_file, j_status])
|
| 457 |
+
j_delete_btn.click(_delete_entry, inputs=[j_entries, j_search],
|
| 458 |
+
outputs=[j_status, j_entries, j_table, j_view])
|
| 459 |
+
|
| 460 |
if __name__ == "__main__":
|
| 461 |
+
gr.set_theme("default")
|
| 462 |
demo.queue()
|
| 463 |
demo.launch()
|