Download learning/progress.py from Mrlongpro/autosubs: direct link, hf CLI and curl.
- Browser
- Download file 5.3 kB
-
https://huggingface.co/spaces/Mrlongpro/autosubs/resolve/main/learning/progress.py
- Command line
-
hf download hf://spaces/Mrlongpro/autosubs/learning/progress.py
-
curl -L -o progress.py https://huggingface.co/spaces/Mrlongpro/autosubs/resolve/main/learning/progress.py
5.3 kB
| """Local SQLite learning system for vocabulary, reviews and quiz results.""" | |
| from __future__ import annotations | |
| import sqlite3 | |
| from datetime import datetime, timedelta | |
| from pathlib import Path | |
| from typing import Any, Dict, List | |
| DB_PATH = Path(__import__("os").getenv("ENGLISH_DB", "english_lab.db")) | |
| def _db(): | |
| c = sqlite3.connect(DB_PATH) | |
| c.row_factory = sqlite3.Row | |
| return c | |
| def init_learning_db(): | |
| with _db() as c: | |
| c.executescript(""" | |
| CREATE TABLE IF NOT EXISTS vocabulary ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| video_id TEXT, | |
| sentence_index INTEGER, | |
| word TEXT NOT NULL, | |
| meaning TEXT DEFAULT '', | |
| word_type TEXT DEFAULT '', | |
| example TEXT DEFAULT '', | |
| ease REAL DEFAULT 2.5, | |
| interval_days INTEGER DEFAULT 0, | |
| repetitions INTEGER DEFAULT 0, | |
| due_at TEXT DEFAULT CURRENT_TIMESTAMP, | |
| last_reviewed TEXT, | |
| UNIQUE(video_id, sentence_index, word) | |
| ); | |
| CREATE TABLE IF NOT EXISTS quiz_results ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| video_id TEXT, | |
| sentence_index INTEGER, | |
| question TEXT, | |
| selected TEXT, | |
| answer TEXT, | |
| correct INTEGER DEFAULT 0, | |
| created_at TEXT DEFAULT CURRENT_TIMESTAMP | |
| ); | |
| """) | |
| init_learning_db() | |
| def save_teacher_result(video_id: str | None, sentence_index: int, result: Any) -> int: | |
| """Persist vocabulary returned by the local AI teacher.""" | |
| rows = result.vocabulary if result else [] | |
| saved = 0 | |
| with _db() as c: | |
| for item in rows: | |
| word = str(item.get("word", "")).strip().lower() | |
| if not word: | |
| continue | |
| c.execute("""INSERT INTO vocabulary(video_id,sentence_index,word,meaning,word_type,example) | |
| VALUES(?,?,?,?,?,?) | |
| ON CONFLICT(video_id,sentence_index,word) DO UPDATE SET | |
| meaning=excluded.meaning, word_type=excluded.word_type, example=excluded.example""", | |
| (video_id, int(sentence_index), word, str(item.get("meaning", "")), | |
| str(item.get("type", "")), str(item.get("example", "")))) | |
| saved += 1 | |
| return saved | |
| def due_words(limit: int = 20) -> List[Dict[str, Any]]: | |
| now = datetime.utcnow().isoformat(sep=" ", timespec="seconds") | |
| with _db() as c: | |
| rows = c.execute("""SELECT * FROM vocabulary | |
| WHERE due_at <= ? ORDER BY due_at ASC, repetitions ASC LIMIT ?""", (now, limit)).fetchall() | |
| return [dict(r) for r in rows] | |
| def all_words(limit: int = 100) -> List[Dict[str, Any]]: | |
| with _db() as c: | |
| rows = c.execute("SELECT * FROM vocabulary ORDER BY word ASC LIMIT ?", (limit,)).fetchall() | |
| return [dict(r) for r in rows] | |
| def review_word(vocab_id: int, quality: int) -> Dict[str, Any]: | |
| """SM-2 inspired scheduling. quality: 0..5.""" | |
| quality = max(0, min(5, int(quality))) | |
| with _db() as c: | |
| row = c.execute("SELECT * FROM vocabulary WHERE id=?", (vocab_id,)).fetchone() | |
| if not row: | |
| return {} | |
| ease = float(row["ease"]) | |
| reps = int(row["repetitions"]) | |
| interval = int(row["interval_days"]) | |
| if quality < 3: | |
| reps = 0 | |
| interval = 1 | |
| else: | |
| reps += 1 | |
| if reps == 1: | |
| interval = 1 | |
| elif reps == 2: | |
| interval = 3 | |
| else: | |
| interval = max(1, round(interval * ease)) | |
| ease = max(1.3, ease + (0.1 - (5-quality) * (0.08 + (5-quality) * 0.02))) | |
| due = datetime.utcnow() + timedelta(days=interval) | |
| c.execute("""UPDATE vocabulary SET ease=?,interval_days=?,repetitions=?,due_at=?,last_reviewed=? WHERE id=?""", | |
| (ease, interval, reps, due.isoformat(sep=" ", timespec="seconds"), | |
| datetime.utcnow().isoformat(sep=" ", timespec="seconds"), vocab_id)) | |
| return dict(c.execute("SELECT * FROM vocabulary WHERE id=?", (vocab_id,)).fetchone()) | |
| def save_quiz(video_id: str | None, sentence_index: int, question: str, selected: str, answer: str) -> bool: | |
| correct = int(str(selected).strip().lower() == str(answer).strip().lower()) | |
| with _db() as c: | |
| c.execute("INSERT INTO quiz_results(video_id,sentence_index,question,selected,answer,correct) VALUES(?,?,?,?,?,?)", | |
| (video_id, sentence_index, question, selected, answer, correct)) | |
| return bool(correct) | |
| def learning_stats() -> str: | |
| with _db() as c: | |
| total = c.execute("SELECT COUNT(*) n FROM vocabulary").fetchone()["n"] | |
| due = c.execute("SELECT COUNT(*) n FROM vocabulary WHERE due_at <= CURRENT_TIMESTAMP").fetchone()["n"] | |
| mastered = c.execute("SELECT COUNT(*) n FROM vocabulary WHERE repetitions >= 4").fetchone()["n"] | |
| quizzes = c.execute("SELECT COUNT(*) n FROM quiz_results").fetchone()["n"] | |
| correct = c.execute("SELECT COALESCE(SUM(correct),0) n FROM quiz_results").fetchone()["n"] | |
| accuracy = (correct / quizzes * 100) if quizzes else 0 | |
| return f"**{total}** từ · **{due}** từ cần ôn · **{mastered}** từ mastered · **{quizzes}** quiz · **{accuracy:.0f}%** quiz accuracy" | |