Files
Code/python/persian-tutor/modules/dashboard.py
dl92 3a8705ece8 Fix bugs, N+1 queries, and wire settings in persian-tutor
- Replace inline __import__("datetime").timedelta hack with proper import
- Remove unused import random in anki_export.py
- Add error handling for Claude CLI subprocess failures in ai.py
- Fix hardcoded absolute path in stt.py with relative Path resolution
- Fix N+1 DB queries in vocab.get_flashcard_batch and dashboard.get_category_breakdown
  by adding db.get_all_word_progress() batch query
- Wire Ollama model and Whisper size settings to actually update config
  via ai.set_ollama_model() and stt.set_whisper_size()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 15:40:24 +00:00

85 lines
2.5 KiB
Python

"""Dashboard: progress stats, charts, and overview."""
import db
from modules.vocab import load_vocab, get_categories
def get_overview():
"""Return overview stats: total words, seen, mastered, due today."""
vocab = load_vocab()
counts = db.get_word_counts(total_vocab_size=len(vocab))
stats = db.get_stats()
counts["streak"] = stats["streak"]
counts["total_reviews"] = stats["total_reviews"]
counts["total_quizzes"] = stats["total_quizzes"]
return counts
def get_category_breakdown():
"""Return progress per category as list of dicts."""
vocab = load_vocab()
categories = get_categories()
all_progress = db.get_all_word_progress()
breakdown = []
for cat in categories:
cat_words = [e for e in vocab if e["category"] == cat]
total = len(cat_words)
seen = 0
mastered = 0
for e in cat_words:
progress = all_progress.get(e["id"])
if progress:
seen += 1
if progress["stability"] and progress["stability"] > 10:
mastered += 1
breakdown.append({
"Category": cat,
"Total": total,
"Seen": seen,
"Mastered": mastered,
"Progress": f"{seen}/{total}" if total > 0 else "0/0",
})
return breakdown
def get_recent_quizzes(limit=10):
"""Return recent quiz results as list of dicts for display."""
stats = db.get_stats()
quizzes = stats["recent_quizzes"][:limit]
result = []
for q in quizzes:
result.append({
"Date": q["timestamp"],
"Category": q["category"] or "All",
"Score": f"{q['correct']}/{q['total_questions']}",
"Duration": f"{q['duration_seconds'] or 0}s",
})
return result
def format_overview_markdown():
"""Format overview stats as a markdown string for display."""
o = get_overview()
pct = (o["seen"] / o["total"] * 100) if o["total"] > 0 else 0
bar_filled = int(pct / 5)
bar_empty = 20 - bar_filled
progress_bar = "" * bar_filled + "" * bar_empty
lines = [
"## Dashboard",
"",
f"**Words studied:** {o['seen']} / {o['total']} ({pct:.0f}%)",
f"`{progress_bar}`",
"",
f"**Due today:** {o['due']}",
f"**Mastered:** {o['mastered']}",
f"**Daily streak:** {o['streak']} day{'s' if o['streak'] != 1 else ''}",
f"**Total reviews:** {o['total_reviews']}",
f"**Quiz sessions:** {o['total_quizzes']}",
]
return "\n".join(lines)