Ship native iOS app, Wiki/TTS backend, and skeleton loading UX.
Replace the WebView shell with SwiftUI screens, add account-scoped Wiki and TTS APIs with adaptive review and photo scan support, and keep web/iOS pages usable while data loads asynchronously. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,6 +6,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from models import QuizRecord, User, Word
|
||||
from schemas import CoachSessionResponse, CoachTurnResponse, CoachWordBrief, MemoryToken
|
||||
from services.adaptive_review_service import adaptive_review_service, retrievability
|
||||
from services.memory_visual_service import (
|
||||
memory_visual_service,
|
||||
parse_iso,
|
||||
@@ -15,6 +16,7 @@ from services.memory_visual_service import (
|
||||
word_zh,
|
||||
)
|
||||
from services.quiz_service import quiz_service
|
||||
from services.text_utils import en_answers_match
|
||||
from services.word_service import word_service
|
||||
|
||||
|
||||
@@ -225,12 +227,16 @@ class MemoryCoachService:
|
||||
return CoachSessionResponse(words=[], total=0)
|
||||
book_service.ensure_user_book_words(db, user, book)
|
||||
practice = book_service.practice_settings_dict(db, user, book)
|
||||
words = quiz_service.select_book_words(db, user, book, practice["daily_target"])
|
||||
book_id = book.id
|
||||
words = quiz_service.select_coach_words(
|
||||
db, user, book_id, practice["daily_target"]
|
||||
)
|
||||
else:
|
||||
settings = quiz_service.get_settings(db, user)
|
||||
words = quiz_service.select_accumulation_words(db, user, settings.daily_target)
|
||||
book_id = 0
|
||||
words = quiz_service.select_coach_words(
|
||||
db, user, book_id, settings.daily_target
|
||||
)
|
||||
if not words:
|
||||
return CoachSessionResponse(words=[], total=0)
|
||||
|
||||
@@ -245,6 +251,8 @@ class MemoryCoachService:
|
||||
retention_now=risk_map.get(w.id, _retention_now(w)),
|
||||
mastery_score=w.mastery_score,
|
||||
status=w.status,
|
||||
retrievability=round(retrievability(w) * 100.0, 1),
|
||||
next_review_at=w.next_review_at,
|
||||
)
|
||||
for w in words
|
||||
]
|
||||
@@ -272,6 +280,9 @@ class MemoryCoachService:
|
||||
word_complete = False
|
||||
next_stage = stage
|
||||
input_hint = f"请输入「{zh}」的英文"
|
||||
review_message: Optional[str] = None
|
||||
review_retrievability: Optional[float] = None
|
||||
next_review_at: Optional[str] = None
|
||||
|
||||
if stage == "intro":
|
||||
next_stage = "derive"
|
||||
@@ -283,8 +294,10 @@ class MemoryCoachService:
|
||||
expect_input = True
|
||||
next_stage = "derive"
|
||||
else:
|
||||
is_correct = answer.lower() == en.lower()
|
||||
is_correct = en_answers_match(answer, en)
|
||||
if is_correct:
|
||||
attempts = hints_used + 1
|
||||
before_r = retrievability(word)
|
||||
tokens = self._reveal_all_v(tokens)
|
||||
messages.append(f"正确:{en}")
|
||||
quiz_service.submit_answer(
|
||||
@@ -296,10 +309,35 @@ class MemoryCoachService:
|
||||
en,
|
||||
duration_seconds,
|
||||
)
|
||||
update = adaptive_review_service.update_word(
|
||||
word,
|
||||
is_correct=True,
|
||||
attempts=attempts,
|
||||
hints_used=hints_used,
|
||||
duration_seconds=duration_seconds,
|
||||
observed_retrievability=before_r,
|
||||
)
|
||||
db.commit()
|
||||
if adaptive_review_service.is_plan_completed(word):
|
||||
review_message = "本词已练会,已移出当前练习计划;可在学习设置中重置后继续复习"
|
||||
else:
|
||||
review_message = update.message
|
||||
review_retrievability = update.retrievability
|
||||
next_review_at = update.next_review_at
|
||||
quiz_recorded = True
|
||||
word_complete = True
|
||||
next_stage = "done"
|
||||
else:
|
||||
before_r = retrievability(word)
|
||||
quiz_service.record_attempt(
|
||||
db,
|
||||
user,
|
||||
word.id,
|
||||
"memory_coach",
|
||||
answer,
|
||||
en,
|
||||
duration_seconds,
|
||||
)
|
||||
hints_used += 1
|
||||
tokens = self.build_tokens(db, word, reveal_extra=hints_used)
|
||||
tokens = self._reveal_q_tokens(tokens, 1)
|
||||
@@ -308,15 +346,23 @@ class MemoryCoachService:
|
||||
if hidden_left == 0:
|
||||
tokens = self._reveal_all_v(tokens)
|
||||
messages.append(f"答案:{en}")
|
||||
quiz_service.submit_answer(
|
||||
db,
|
||||
user,
|
||||
word.id,
|
||||
"memory_coach",
|
||||
answer,
|
||||
en,
|
||||
duration_seconds,
|
||||
update = adaptive_review_service.update_word(
|
||||
word,
|
||||
is_correct=False,
|
||||
attempts=hints_used,
|
||||
hints_used=hints_used,
|
||||
duration_seconds=duration_seconds,
|
||||
observed_retrievability=before_r,
|
||||
)
|
||||
word.consecutive_correct_count = 0
|
||||
word.status = "weak"
|
||||
word.last_reviewed_at = datetime.now(timezone.utc).strftime(
|
||||
"%Y-%m-%dT%H:%M:%SZ"
|
||||
)
|
||||
db.commit()
|
||||
review_message = update.message
|
||||
review_retrievability = update.retrievability
|
||||
next_review_at = update.next_review_at
|
||||
quiz_recorded = True
|
||||
word_complete = True
|
||||
next_stage = "done"
|
||||
@@ -346,6 +392,9 @@ class MemoryCoachService:
|
||||
hints_used=hints_used,
|
||||
target_en=en if word_complete else None,
|
||||
target_zh=zh if word_complete else None,
|
||||
review_message=review_message,
|
||||
retrievability=review_retrievability,
|
||||
next_review_at=next_review_at,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user