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:
@@ -0,0 +1,52 @@
|
||||
"""启动时补齐新增列(已有库无需手工迁移)。"""
|
||||
|
||||
from sqlalchemy import inspect, text
|
||||
|
||||
from database import engine
|
||||
|
||||
|
||||
def run_migrations() -> None:
|
||||
insp = inspect(engine)
|
||||
with engine.begin() as conn:
|
||||
if insp.has_table("words"):
|
||||
cols = {c["name"] for c in insp.get_columns("words")}
|
||||
if "total_train_seconds" not in cols:
|
||||
conn.execute(
|
||||
text(
|
||||
"ALTER TABLE words ADD COLUMN total_train_seconds "
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
)
|
||||
)
|
||||
if "train_count" not in cols:
|
||||
conn.execute(
|
||||
text("ALTER TABLE words ADD COLUMN train_count INTEGER NOT NULL DEFAULT 0")
|
||||
)
|
||||
|
||||
if insp.has_table("quiz_records"):
|
||||
cols = {c["name"] for c in insp.get_columns("quiz_records")}
|
||||
if "duration_seconds" not in cols:
|
||||
conn.execute(
|
||||
text(
|
||||
"ALTER TABLE quiz_records ADD COLUMN duration_seconds "
|
||||
"INTEGER NOT NULL DEFAULT 0"
|
||||
)
|
||||
)
|
||||
|
||||
if insp.has_table("words") and insp.has_table("quiz_records"):
|
||||
conn.execute(
|
||||
text(
|
||||
"""
|
||||
UPDATE words w SET
|
||||
train_count = (
|
||||
SELECT COUNT(*) FROM quiz_records q WHERE q.word_id = w.id
|
||||
),
|
||||
total_train_seconds = (
|
||||
SELECT COALESCE(SUM(duration_seconds), 0)
|
||||
FROM quiz_records q WHERE q.word_id = w.id
|
||||
)
|
||||
WHERE train_count = 0 AND EXISTS (
|
||||
SELECT 1 FROM quiz_records q WHERE q.word_id = w.id
|
||||
)
|
||||
"""
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user