Spaces:
Running
Running
| """ | |
| database.py — SQLite schema + helpers for Teacher AI progress tracking. | |
| Tables: students, sessions, attempts, topic_mastery, error_log | |
| """ | |
| import sqlite3 | |
| import os | |
| from contextlib import contextmanager | |
| from pathlib import Path | |
| # Store DB inside the app folder — persists between restarts | |
| # Path: /code/app/data/teacher_ai.db | |
| BASE_DIR = Path(__file__).parent | |
| DATA_DIR = BASE_DIR / "data" | |
| DATA_DIR.mkdir(exist_ok=True) # create /code/app/data/ if it doesn't exist | |
| DB_PATH = str(DATA_DIR / "teacher_ai.db") | |
| def get_conn() -> sqlite3.Connection: | |
| conn = sqlite3.connect(DB_PATH, check_same_thread=False) | |
| conn.row_factory = sqlite3.Row | |
| conn.execute("PRAGMA journal_mode=WAL") | |
| conn.execute("PRAGMA foreign_keys=ON") | |
| return conn | |
| def db(): | |
| conn = get_conn() | |
| try: | |
| yield conn | |
| conn.commit() | |
| except Exception: | |
| conn.rollback() | |
| raise | |
| finally: | |
| conn.close() | |
| def init_db(): | |
| with db() as conn: | |
| conn.executescript(""" | |
| CREATE TABLE IF NOT EXISTS students ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL, | |
| role TEXT NOT NULL DEFAULT 'student', | |
| level TEXT NOT NULL DEFAULT 'high_school', | |
| streak INTEGER NOT NULL DEFAULT 0, | |
| last_active TEXT, | |
| created_at TEXT NOT NULL DEFAULT (datetime('now')) | |
| ); | |
| CREATE TABLE IF NOT EXISTS attempts ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| student_id INTEGER NOT NULL REFERENCES students(id), | |
| question TEXT NOT NULL, | |
| topic TEXT NOT NULL, | |
| error_type TEXT, | |
| response TEXT, | |
| difficulty INTEGER NOT NULL DEFAULT 5, | |
| mode TEXT NOT NULL DEFAULT 'text', | |
| explain_level TEXT NOT NULL DEFAULT 'normal', | |
| created_at TEXT NOT NULL DEFAULT (datetime('now')) | |
| ); | |
| CREATE TABLE IF NOT EXISTS topic_mastery ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| student_id INTEGER NOT NULL REFERENCES students(id), | |
| topic TEXT NOT NULL, | |
| attempts INTEGER NOT NULL DEFAULT 0, | |
| correct INTEGER NOT NULL DEFAULT 0, | |
| mastery_score REAL NOT NULL DEFAULT 0.0, | |
| last_updated TEXT NOT NULL DEFAULT (datetime('now')), | |
| UNIQUE(student_id, topic) | |
| ); | |
| CREATE TABLE IF NOT EXISTS error_log ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| student_id INTEGER NOT NULL REFERENCES students(id), | |
| topic TEXT NOT NULL, | |
| error_type TEXT NOT NULL, | |
| count INTEGER NOT NULL DEFAULT 1, | |
| last_seen TEXT NOT NULL DEFAULT (datetime('now')), | |
| UNIQUE(student_id, topic, error_type) | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_attempts_student ON attempts(student_id); | |
| CREATE INDEX IF NOT EXISTS idx_attempts_topic ON attempts(topic); | |
| CREATE INDEX IF NOT EXISTS idx_mastery_student ON topic_mastery(student_id); | |
| """) | |
| # Always ensure the default Guest student (id=1) exists. | |
| # All unauthenticated requests use student_id=1. | |
| conn.execute(""" | |
| INSERT OR IGNORE INTO students (id, name, role, level) | |
| VALUES (1, 'Guest', 'student', 'high_school') | |
| """) | |
| print(f"[DB] Initialised at {DB_PATH}") | |