Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# ================================
|
| 2 |
# πͺ MoodMirror+ β Text Emotion β’ Advice-only + brief intros & reasons
|
| 3 |
-
# - Tabs: Advice β’ Emergency numbers β’ Breathing β’ Journal (export
|
| 4 |
# - Gradio + sklearn compatibility fixes
|
| 5 |
# ================================
|
| 6 |
import os
|
|
@@ -21,6 +21,15 @@ from sklearn.linear_model import LogisticRegression
|
|
| 21 |
from sklearn.multiclass import OneVsRestClassifier
|
| 22 |
from sklearn.pipeline import Pipeline
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
# ---------------- Storage paths ----------------
|
| 25 |
def _pick_data_dir():
|
| 26 |
if os.path.isdir("/data") and os.access("/data", os.W_OK):
|
|
@@ -192,6 +201,19 @@ def journal_get(entry_id: int):
|
|
| 192 |
ts, emo, title, content = row
|
| 193 |
return {"ts": ts, "emotion": emo or "", "title": title or "", "content": content or ""}
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
def journal_delete(entry_id: int):
|
| 196 |
conn = get_conn()
|
| 197 |
cur = conn.execute("DELETE FROM journal WHERE id = ?", (int(entry_id),))
|
|
@@ -200,43 +222,111 @@ def journal_delete(entry_id: int):
|
|
| 200 |
conn.close()
|
| 201 |
return changes > 0
|
| 202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
def journal_export_txt(entry_id: int):
|
| 204 |
data = journal_get(entry_id)
|
| 205 |
if not data:
|
| 206 |
return None, "Entry not found."
|
| 207 |
fname = f"journal_{entry_id}_{data['ts'].replace(':','-')}.txt"
|
| 208 |
fpath = os.path.join(DATA_DIR, fname)
|
| 209 |
-
lines = [
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
with open(fpath, "w", encoding="utf-8") as f:
|
| 217 |
f.write("\n".join(lines))
|
| 218 |
return fpath, f"Ready: {fname}"
|
| 219 |
|
| 220 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
conn = get_conn()
|
| 222 |
-
rows = list(conn.execute("SELECT id
|
| 223 |
conn.close()
|
| 224 |
if not rows:
|
| 225 |
return None, "No entries to export."
|
|
|
|
|
|
|
| 226 |
zip_name = os.path.join(DATA_DIR, "journal_all.zip")
|
| 227 |
with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 228 |
-
for (
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
f"{
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
|
| 241 |
# ---------------- Model ----------------
|
| 242 |
def load_goemotions_dataset():
|
|
@@ -256,12 +346,7 @@ def train_or_load_model():
|
|
| 256 |
("tfidf", TfidfVectorizer(lowercase=True, ngram_range=(1,2), min_df=2, max_df=0.9, strip_accents="unicode")),
|
| 257 |
("ovr", OneVsRestClassifier(
|
| 258 |
LogisticRegression(
|
| 259 |
-
solver="saga",
|
| 260 |
-
penalty="l2",
|
| 261 |
-
C=0.5, # stronger regularization to help convergence
|
| 262 |
-
tol=1e-3, # slightly looser tolerance
|
| 263 |
-
max_iter=5000, # more iterations
|
| 264 |
-
class_weight="balanced"
|
| 265 |
),
|
| 266 |
n_jobs=-1
|
| 267 |
))
|
|
@@ -412,12 +497,13 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 412 |
yield "β
Done. Notice how your body feels."
|
| 413 |
start_btn.click(run_breathing, inputs=[pattern, cycles], outputs=[breathe_out])
|
| 414 |
|
| 415 |
-
# ---- Tab 4: Journal (export
|
| 416 |
with gr.Tab("Journal"):
|
| 417 |
-
gr.Markdown("#### π Personal journal\nWrite freely, then save.
|
| 418 |
|
|
|
|
| 419 |
with gr.Row():
|
| 420 |
-
j_title = gr.Textbox(label="Title
|
| 421 |
j_emotion = gr.Dropdown(
|
| 422 |
choices=["neutral","sadness","fear","anger","nervousness","boredom","grief","love","joy","curiosity","gratitude"],
|
| 423 |
value="neutral", label="Emotion tag"
|
|
@@ -425,9 +511,13 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 425 |
j_text = gr.Textbox(lines=10, label="Your entry", placeholder="Write whatever you want to get off your chest...")
|
| 426 |
|
| 427 |
with gr.Row():
|
| 428 |
-
j_save = gr.Button("Save
|
| 429 |
-
|
|
|
|
| 430 |
|
|
|
|
|
|
|
|
|
|
| 431 |
gr.Markdown("##### Your entries")
|
| 432 |
with gr.Row():
|
| 433 |
j_search = gr.Textbox(label="Search (title, text or emotion)", placeholder="e.g., anxiety, work, joy")
|
|
@@ -436,13 +526,14 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 436 |
j_table = gr.Dataframe(headers=["UTC time","Emotion","Title","Preview"], value=[], interactive=False)
|
| 437 |
j_view = gr.Markdown()
|
| 438 |
|
| 439 |
-
# Export
|
| 440 |
with gr.Row():
|
| 441 |
-
|
| 442 |
-
|
|
|
|
| 443 |
j_delete_btn = gr.Button("Delete entry", variant="stop")
|
| 444 |
with gr.Row():
|
| 445 |
-
j_export_all_btn = gr.Button("β¬οΈ Export
|
| 446 |
j_export_all_file = gr.File(label="Download all entries", visible=True)
|
| 447 |
|
| 448 |
# --------------- Backend ---------------
|
|
@@ -457,20 +548,38 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 457 |
clear_title = "" if ok else title
|
| 458 |
return msg, drop, table, clear_text, clear_title
|
| 459 |
|
| 460 |
-
def
|
| 461 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
data = journal_get(entry_id)
|
| 463 |
-
if not data:
|
|
|
|
| 464 |
title_line = f"### {data['title']}" if data['title'] else "### (Untitled)"
|
| 465 |
emo_line = f"**Emotion:** {data['emotion'] or 'β'} \n**Saved (UTC):** {data['ts']}"
|
| 466 |
-
|
|
|
|
| 467 |
|
| 468 |
-
def
|
|
|
|
|
|
|
|
|
|
| 469 |
if entry_id is None:
|
| 470 |
-
return None, "Select an entry to export."
|
| 471 |
path, msg = journal_export_txt(int(entry_id))
|
| 472 |
return path, msg
|
| 473 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
def _delete_entry(entry_id, search):
|
| 475 |
if entry_id is None:
|
| 476 |
status = "Select an entry to delete."
|
|
@@ -482,19 +591,31 @@ with gr.Blocks(title="πͺ MoodMirror+ β Text Emotion β’ Advice-only") as dem
|
|
| 482 |
return status, drop, table, ""
|
| 483 |
|
| 484 |
def _export_all():
|
| 485 |
-
path, msg = journal_export_all_zip()
|
| 486 |
return path, msg
|
| 487 |
|
| 488 |
# Wire actions
|
| 489 |
j_save.click(_save_entry, inputs=[j_title, j_text, j_emotion, j_search],
|
| 490 |
outputs=[j_status, j_entries, j_table, j_text, j_title])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
j_refresh.click(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
| 492 |
j_search.submit(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
| 493 |
-
j_entries.change(_load_entry, inputs=[j_entries], outputs=[j_view])
|
| 494 |
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
j_delete_btn.click(_delete_entry, inputs=[j_entries, j_search],
|
| 497 |
outputs=[j_status, j_entries, j_table, j_view])
|
|
|
|
| 498 |
j_export_all_btn.click(_export_all, outputs=[j_export_all_file, j_status])
|
| 499 |
|
| 500 |
if __name__ == "__main__":
|
|
|
|
| 1 |
# ================================
|
| 2 |
# πͺ MoodMirror+ β Text Emotion β’ Advice-only + brief intros & reasons
|
| 3 |
+
# - Tabs: Advice β’ Emergency numbers β’ Breathing β’ Journal (edit + PDF export + export all)
|
| 4 |
# - Gradio + sklearn compatibility fixes
|
| 5 |
# ================================
|
| 6 |
import os
|
|
|
|
| 21 |
from sklearn.multiclass import OneVsRestClassifier
|
| 22 |
from sklearn.pipeline import Pipeline
|
| 23 |
|
| 24 |
+
# --- Optional PDF deps ---
|
| 25 |
+
try:
|
| 26 |
+
from reportlab.lib.pagesizes import A4
|
| 27 |
+
from reportlab.pdfgen import canvas
|
| 28 |
+
from reportlab.lib.units import mm
|
| 29 |
+
REPORTLAB_OK = True
|
| 30 |
+
except Exception:
|
| 31 |
+
REPORTLAB_OK = False
|
| 32 |
+
|
| 33 |
# ---------------- Storage paths ----------------
|
| 34 |
def _pick_data_dir():
|
| 35 |
if os.path.isdir("/data") and os.access("/data", os.W_OK):
|
|
|
|
| 201 |
ts, emo, title, content = row
|
| 202 |
return {"ts": ts, "emotion": emo or "", "title": title or "", "content": content or ""}
|
| 203 |
|
| 204 |
+
def journal_update(entry_id: int, title: str, content: str, emotion: str):
|
| 205 |
+
if entry_id is None: return False, "No entry selected."
|
| 206 |
+
title = (title or "").strip()
|
| 207 |
+
content = (content or "").strip()
|
| 208 |
+
if not content:
|
| 209 |
+
return False, "Entry content cannot be empty."
|
| 210 |
+
conn = get_conn()
|
| 211 |
+
cur = conn.execute("UPDATE journal SET title=?, content=?, emotion=? WHERE id=?", (title, content, emotion or "", int(entry_id)))
|
| 212 |
+
conn.commit()
|
| 213 |
+
ok = (cur.rowcount or 0) > 0
|
| 214 |
+
conn.close()
|
| 215 |
+
return ok, ("Updated β" if ok else "Entry not found.")
|
| 216 |
+
|
| 217 |
def journal_delete(entry_id: int):
|
| 218 |
conn = get_conn()
|
| 219 |
cur = conn.execute("DELETE FROM journal WHERE id = ?", (int(entry_id),))
|
|
|
|
| 222 |
conn.close()
|
| 223 |
return changes > 0
|
| 224 |
|
| 225 |
+
# --- PDF helpers ---
|
| 226 |
+
def _wrap_text(text, max_chars=90):
|
| 227 |
+
lines = []
|
| 228 |
+
for para in (text or "").split("\n"):
|
| 229 |
+
para = para.rstrip()
|
| 230 |
+
while len(para) > max_chars:
|
| 231 |
+
cut = para.rfind(" ", 0, max_chars)
|
| 232 |
+
if cut == -1: cut = max_chars
|
| 233 |
+
lines.append(para[:cut])
|
| 234 |
+
para = para[cut:].lstrip()
|
| 235 |
+
lines.append(para)
|
| 236 |
+
return lines
|
| 237 |
+
|
| 238 |
+
def _pdf_from_entry(path, data):
|
| 239 |
+
# Minimal PDF render with reportlab
|
| 240 |
+
c = canvas.Canvas(path, pagesize=A4)
|
| 241 |
+
width, height = A4
|
| 242 |
+
x_margin = 20*mm
|
| 243 |
+
y_margin = 20*mm
|
| 244 |
+
y = height - y_margin
|
| 245 |
+
def draw_line(s, size=11, bold=False, leading=14):
|
| 246 |
+
nonlocal y
|
| 247 |
+
if y < 30*mm:
|
| 248 |
+
c.showPage()
|
| 249 |
+
y = height - y_margin
|
| 250 |
+
c.setFont("Helvetica-Bold" if bold else "Helvetica", size)
|
| 251 |
+
c.drawString(x_margin, y, s)
|
| 252 |
+
y -= leading
|
| 253 |
+
# Header
|
| 254 |
+
title = data["title"] or "(Untitled)"
|
| 255 |
+
draw_line(f"Title: {title}", size=14, bold=True, leading=18)
|
| 256 |
+
draw_line(f"Emotion: {data['emotion'] or '-'}")
|
| 257 |
+
draw_line(f"Saved (UTC): {data['ts']}")
|
| 258 |
+
draw_line("-"*80)
|
| 259 |
+
# Body
|
| 260 |
+
for ln in _wrap_text(data["content"], 95):
|
| 261 |
+
draw_line(ln)
|
| 262 |
+
c.showPage()
|
| 263 |
+
c.save()
|
| 264 |
+
|
| 265 |
def journal_export_txt(entry_id: int):
|
| 266 |
data = journal_get(entry_id)
|
| 267 |
if not data:
|
| 268 |
return None, "Entry not found."
|
| 269 |
fname = f"journal_{entry_id}_{data['ts'].replace(':','-')}.txt"
|
| 270 |
fpath = os.path.join(DATA_DIR, fname)
|
| 271 |
+
lines = [
|
| 272 |
+
f"Title: {data['title'] or '(Untitled)'}",
|
| 273 |
+
f"Emotion: {data['emotion'] or '-'}",
|
| 274 |
+
f"Saved (UTC): {data['ts']}",
|
| 275 |
+
"-" * 40,
|
| 276 |
+
data["content"] or ""
|
| 277 |
+
]
|
| 278 |
with open(fpath, "w", encoding="utf-8") as f:
|
| 279 |
f.write("\n".join(lines))
|
| 280 |
return fpath, f"Ready: {fname}"
|
| 281 |
|
| 282 |
+
def journal_export_pdf(entry_id: int):
|
| 283 |
+
if not REPORTLAB_OK:
|
| 284 |
+
return None, "PDF export requires 'reportlab' (pip install reportlab)."
|
| 285 |
+
data = journal_get(entry_id)
|
| 286 |
+
if not data:
|
| 287 |
+
return None, "Entry not found."
|
| 288 |
+
safe_title = re.sub(r"[^a-zA-Z0-9_\- ]", "_", data["title"] or "untitled")
|
| 289 |
+
fname = f"journal_{entry_id}_{data['ts'][:19].replace(':','-')} - {safe_title}.pdf"
|
| 290 |
+
fpath = os.path.join(DATA_DIR, fname)
|
| 291 |
+
_pdf_from_entry(fpath, data)
|
| 292 |
+
return fpath, f"PDF ready: {fname}"
|
| 293 |
+
|
| 294 |
+
def journal_export_all_zip(pdf_first=True):
|
| 295 |
conn = get_conn()
|
| 296 |
+
rows = list(conn.execute("SELECT id FROM journal ORDER BY ts"))
|
| 297 |
conn.close()
|
| 298 |
if not rows:
|
| 299 |
return None, "No entries to export."
|
| 300 |
+
# Try PDFs if requested & available, else fall back to txt
|
| 301 |
+
use_pdf = pdf_first and REPORTLAB_OK
|
| 302 |
zip_name = os.path.join(DATA_DIR, "journal_all.zip")
|
| 303 |
with zipfile.ZipFile(zip_name, "w", zipfile.ZIP_DEFLATED) as zf:
|
| 304 |
+
for (entry_id,) in rows:
|
| 305 |
+
data = journal_get(entry_id)
|
| 306 |
+
if not data:
|
| 307 |
+
continue
|
| 308 |
+
safe_title = re.sub(r"[^a-zA-Z0-9_\- ]", "_", data["title"] or "untitled")
|
| 309 |
+
stamp = data["ts"][:19].replace(":", "-")
|
| 310 |
+
if use_pdf:
|
| 311 |
+
# write to a temp pdf then add
|
| 312 |
+
tmp_pdf = os.path.join(DATA_DIR, f"_tmp_{entry_id}.pdf")
|
| 313 |
+
_pdf_from_entry(tmp_pdf, data)
|
| 314 |
+
arcname = f"{stamp} - {safe_title}.pdf"
|
| 315 |
+
zf.write(tmp_pdf, arcname=arcname)
|
| 316 |
+
try: os.remove(tmp_pdf)
|
| 317 |
+
except: pass
|
| 318 |
+
else:
|
| 319 |
+
txt = (
|
| 320 |
+
f"Title: {data['title'] or '(Untitled)'}\n"
|
| 321 |
+
f"Emotion: {data['emotion'] or '-'}\n"
|
| 322 |
+
f"Saved (UTC): {data['ts']}\n"
|
| 323 |
+
f"{'-'*40}\n"
|
| 324 |
+
f"{data['content'] or ''}"
|
| 325 |
+
)
|
| 326 |
+
arcname = f"{stamp} - {safe_title}.txt"
|
| 327 |
+
zf.writestr(arcname, txt)
|
| 328 |
+
msg = "Exported all as PDF." if use_pdf else "Exported all as TXT (install 'reportlab' for PDF)."
|
| 329 |
+
return zip_name, msg
|
| 330 |
|
| 331 |
# ---------------- Model ----------------
|
| 332 |
def load_goemotions_dataset():
|
|
|
|
| 346 |
("tfidf", TfidfVectorizer(lowercase=True, ngram_range=(1,2), min_df=2, max_df=0.9, strip_accents="unicode")),
|
| 347 |
("ovr", OneVsRestClassifier(
|
| 348 |
LogisticRegression(
|
| 349 |
+
solver="saga", penalty="l2", C=0.5, tol=1e-3, max_iter=5000, class_weight="balanced"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 350 |
),
|
| 351 |
n_jobs=-1
|
| 352 |
))
|
|
|
|
| 497 |
yield "β
Done. Notice how your body feels."
|
| 498 |
start_btn.click(run_breathing, inputs=[pattern, cycles], outputs=[breathe_out])
|
| 499 |
|
| 500 |
+
# ---- Tab 4: Journal (edit + PDF export + export all) ----
|
| 501 |
with gr.Tab("Journal"):
|
| 502 |
+
gr.Markdown("#### π Personal journal\nWrite freely, then save. Re-open to edit. Export as PDF or all in a ZIP.")
|
| 503 |
|
| 504 |
+
# Editor
|
| 505 |
with gr.Row():
|
| 506 |
+
j_title = gr.Textbox(label="Title", placeholder="e.g., A tough day at work")
|
| 507 |
j_emotion = gr.Dropdown(
|
| 508 |
choices=["neutral","sadness","fear","anger","nervousness","boredom","grief","love","joy","curiosity","gratitude"],
|
| 509 |
value="neutral", label="Emotion tag"
|
|
|
|
| 511 |
j_text = gr.Textbox(lines=10, label="Your entry", placeholder="Write whatever you want to get off your chest...")
|
| 512 |
|
| 513 |
with gr.Row():
|
| 514 |
+
j_save = gr.Button("Save as NEW", variant="primary")
|
| 515 |
+
j_update = gr.Button("Update selected", variant="secondary")
|
| 516 |
+
j_new = gr.Button("New (clear)")
|
| 517 |
|
| 518 |
+
j_status = gr.Markdown()
|
| 519 |
+
|
| 520 |
+
# Listing / search / preview
|
| 521 |
gr.Markdown("##### Your entries")
|
| 522 |
with gr.Row():
|
| 523 |
j_search = gr.Textbox(label="Search (title, text or emotion)", placeholder="e.g., anxiety, work, joy")
|
|
|
|
| 526 |
j_table = gr.Dataframe(headers=["UTC time","Emotion","Title","Preview"], value=[], interactive=False)
|
| 527 |
j_view = gr.Markdown()
|
| 528 |
|
| 529 |
+
# Export / Delete
|
| 530 |
with gr.Row():
|
| 531 |
+
j_export_txt_btn = gr.Button("Export selected .txt")
|
| 532 |
+
j_export_pdf_btn = gr.Button("Export selected PDF")
|
| 533 |
+
j_export_file = gr.File(label="Download exported file", visible=True)
|
| 534 |
j_delete_btn = gr.Button("Delete entry", variant="stop")
|
| 535 |
with gr.Row():
|
| 536 |
+
j_export_all_btn = gr.Button("β¬οΈ Export ALL entries (.zip, PDFs if possible)")
|
| 537 |
j_export_all_file = gr.File(label="Download all entries", visible=True)
|
| 538 |
|
| 539 |
# --------------- Backend ---------------
|
|
|
|
| 548 |
clear_title = "" if ok else title
|
| 549 |
return msg, drop, table, clear_text, clear_title
|
| 550 |
|
| 551 |
+
def _update_entry(entry_id, title, text, emotion, search):
|
| 552 |
+
ok, msg = journal_update(entry_id, title, text, emotion)
|
| 553 |
+
drop, table = _refresh_entries(search)
|
| 554 |
+
return msg, drop, table
|
| 555 |
+
|
| 556 |
+
def _load_entry_fill(entry_id):
|
| 557 |
+
"""Load selected entry into editor + preview."""
|
| 558 |
+
if entry_id is None:
|
| 559 |
+
return "", "neutral", "", ""
|
| 560 |
data = journal_get(entry_id)
|
| 561 |
+
if not data:
|
| 562 |
+
return "", "neutral", "", "Entry not found."
|
| 563 |
title_line = f"### {data['title']}" if data['title'] else "### (Untitled)"
|
| 564 |
emo_line = f"**Emotion:** {data['emotion'] or 'β'} \n**Saved (UTC):** {data['ts']}"
|
| 565 |
+
preview = f"{title_line}\n\n{emo_line}\n\n---\n\n{data['content']}"
|
| 566 |
+
return data["title"], data["emotion"] or "neutral", data["content"], preview
|
| 567 |
|
| 568 |
+
def _new_entry():
|
| 569 |
+
return "", "neutral", "", "Cleared. You can write a new entry."
|
| 570 |
+
|
| 571 |
+
def _export_txt(entry_id):
|
| 572 |
if entry_id is None:
|
| 573 |
+
return None, "Select an entry to export (.txt)."
|
| 574 |
path, msg = journal_export_txt(int(entry_id))
|
| 575 |
return path, msg
|
| 576 |
|
| 577 |
+
def _export_pdf(entry_id):
|
| 578 |
+
if entry_id is None:
|
| 579 |
+
return None, "Select an entry to export (PDF)."
|
| 580 |
+
path, msg = journal_export_pdf(int(entry_id))
|
| 581 |
+
return path, msg
|
| 582 |
+
|
| 583 |
def _delete_entry(entry_id, search):
|
| 584 |
if entry_id is None:
|
| 585 |
status = "Select an entry to delete."
|
|
|
|
| 591 |
return status, drop, table, ""
|
| 592 |
|
| 593 |
def _export_all():
|
| 594 |
+
path, msg = journal_export_all_zip(pdf_first=True)
|
| 595 |
return path, msg
|
| 596 |
|
| 597 |
# Wire actions
|
| 598 |
j_save.click(_save_entry, inputs=[j_title, j_text, j_emotion, j_search],
|
| 599 |
outputs=[j_status, j_entries, j_table, j_text, j_title])
|
| 600 |
+
|
| 601 |
+
j_update.click(_update_entry, inputs=[j_entries, j_title, j_text, j_emotion, j_search],
|
| 602 |
+
outputs=[j_status, j_entries, j_table])
|
| 603 |
+
|
| 604 |
+
j_new.click(_new_entry, outputs=[j_title, j_emotion, j_text, j_view])
|
| 605 |
+
|
| 606 |
j_refresh.click(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
| 607 |
j_search.submit(_refresh_entries, inputs=[j_search], outputs=[j_entries, j_table])
|
|
|
|
| 608 |
|
| 609 |
+
# Selecting an entry both fills the editor and shows preview
|
| 610 |
+
j_entries.change(_load_entry_fill, inputs=[j_entries],
|
| 611 |
+
outputs=[j_title, j_emotion, j_text, j_view])
|
| 612 |
+
|
| 613 |
+
j_export_txt_btn.click(_export_txt, inputs=[j_entries], outputs=[j_export_file, j_status])
|
| 614 |
+
j_export_pdf_btn.click(_export_pdf, inputs=[j_entries], outputs=[j_export_file, j_status])
|
| 615 |
+
|
| 616 |
j_delete_btn.click(_delete_entry, inputs=[j_entries, j_search],
|
| 617 |
outputs=[j_status, j_entries, j_table, j_view])
|
| 618 |
+
|
| 619 |
j_export_all_btn.click(_export_all, outputs=[j_export_all_file, j_status])
|
| 620 |
|
| 621 |
if __name__ == "__main__":
|