Add spell practice, memory analytics, auth persistence, and word library UX.

Includes per-word training stats and curves, quiz session auto-save, remember-login,
paginated word list with floating page arrows, and Obsidian-style relationship graph baseline.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
John
2026-06-04 14:57:39 -07:00
parent bd7635986a
commit 68c5efe573
30 changed files with 2560 additions and 63 deletions
+32 -1
View File
@@ -118,6 +118,27 @@ class QuizService:
correct_answer=correct,
)
def build_spell_question(self, word: Word) -> QuizQuestion:
zh = word.source_text if word.source_lang == "zh" else word.target_text
en = word.target_text if word.source_lang == "zh" else word.source_text
return QuizQuestion(
word_id=word.id,
question_type="spell",
prompt=zh,
phonetic=word.phonetic,
options=[],
correct_answer=en,
)
def get_spell_quiz(self, db: Session, user: User) -> dict:
settings = self.get_settings(db, user)
words = self.select_daily_words(db, user, settings.daily_target)
questions = [self.build_spell_question(w) for w in words]
return {
"questions": questions,
"total": len(questions),
}
def get_daily_quiz(self, db: Session, user: User) -> dict:
settings = self.get_settings(db, user)
words = self.select_daily_words(db, user, settings.daily_target)
@@ -143,6 +164,7 @@ class QuizService:
question_type: str,
user_answer: str,
correct_answer: str,
duration_seconds: int = 0,
) -> dict:
word = db.query(Word).filter(Word.id == word_id, Word.user_id == user.id).first()
if not word:
@@ -150,9 +172,15 @@ class QuizService:
raise HTTPException(status_code=404, detail="单词不存在")
settings = self.get_settings(db, user)
is_correct = user_answer.strip() == correct_answer.strip()
if question_type == "spell":
is_correct = (
user_answer.strip().lower() == correct_answer.strip().lower()
)
else:
is_correct = user_answer.strip() == correct_answer.strip()
now = utc_now_iso()
today = today_str()
duration_seconds = max(0, min(int(duration_seconds or 0), 3600))
if is_correct:
word.correct_count += 1
@@ -175,6 +203,8 @@ class QuizService:
word.mastery_score = calc_mastery_score(word.correct_count, word.wrong_count)
word.last_reviewed_at = now
word.train_count += 1
word.total_train_seconds += duration_seconds
record = QuizRecord(
user_id=user.id,
@@ -183,6 +213,7 @@ class QuizService:
user_answer=user_answer,
correct_answer=correct_answer,
is_correct=1 if is_correct else 0,
duration_seconds=duration_seconds,
created_at=now,
)
db.add(record)