feat(exam-prep F0): миграция 022 + импорт-скрипт (800 задач math9, 76% автопроверяемые)
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 022: Exam Preparation Module
|
||||
--
|
||||
-- Generic exam preparation infrastructure parameterized by exam_key.
|
||||
-- Supports multiple exams (math9, phys9, chem9, math11ce, etc.) sharing
|
||||
-- the same task bank, attempts tracking, mock sessions, and study plans.
|
||||
--
|
||||
-- See docs/exam-prep-plan.md for full architecture.
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
-- ── Exam tracks registry ────────────────────────────────────────
|
||||
CREATE TABLE exam_tracks (
|
||||
exam_key TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
subject_slug TEXT NOT NULL,
|
||||
grade INTEGER NOT NULL,
|
||||
duration_min INTEGER NOT NULL,
|
||||
tasks_per_variant INTEGER NOT NULL,
|
||||
variants_count INTEGER NOT NULL,
|
||||
scoring_json TEXT,
|
||||
intro_html TEXT,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- ── Task bank (one row per task across all variants) ────────────
|
||||
CREATE TABLE exam_tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
exam_key TEXT NOT NULL REFERENCES exam_tracks(exam_key) ON DELETE CASCADE,
|
||||
variant INTEGER NOT NULL,
|
||||
task_idx INTEGER NOT NULL,
|
||||
task_type TEXT NOT NULL CHECK (task_type IN ('mc','open','long')),
|
||||
text_html TEXT NOT NULL,
|
||||
figure_html TEXT,
|
||||
opts_json TEXT,
|
||||
answer TEXT,
|
||||
solution_html TEXT NOT NULL,
|
||||
topic TEXT,
|
||||
subtopic TEXT,
|
||||
difficulty INTEGER,
|
||||
UNIQUE(exam_key, variant, task_idx)
|
||||
);
|
||||
CREATE INDEX idx_exam_tasks_topic ON exam_tasks(exam_key, topic);
|
||||
CREATE INDEX idx_exam_tasks_variant ON exam_tasks(exam_key, variant);
|
||||
CREATE INDEX idx_exam_tasks_type ON exam_tasks(exam_key, task_type);
|
||||
|
||||
-- ── User attempts ───────────────────────────────────────────────
|
||||
CREATE TABLE exam_attempts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
exam_task_id INTEGER NOT NULL REFERENCES exam_tasks(id) ON DELETE CASCADE,
|
||||
user_answer TEXT,
|
||||
is_correct INTEGER,
|
||||
time_ms INTEGER,
|
||||
mode TEXT NOT NULL CHECK (mode IN ('practice','variant','topic','mock')),
|
||||
session_id INTEGER,
|
||||
hint_used INTEGER NOT NULL DEFAULT 0,
|
||||
solution_viewed INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
CREATE INDEX idx_exam_attempts_user_time ON exam_attempts(user_id, created_at DESC);
|
||||
CREATE INDEX idx_exam_attempts_task ON exam_attempts(exam_task_id);
|
||||
CREATE INDEX idx_exam_attempts_session ON exam_attempts(session_id);
|
||||
CREATE INDEX idx_exam_attempts_user_task ON exam_attempts(user_id, exam_task_id, created_at DESC);
|
||||
|
||||
-- ── Mock exam sessions ──────────────────────────────────────────
|
||||
CREATE TABLE exam_mock_sessions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
exam_key TEXT NOT NULL,
|
||||
variant INTEGER,
|
||||
source TEXT NOT NULL CHECK (source IN ('variant','random','weak-topics')),
|
||||
task_ids_json TEXT NOT NULL,
|
||||
started_at INTEGER NOT NULL,
|
||||
finished_at INTEGER,
|
||||
duration_planned_min INTEGER NOT NULL,
|
||||
score INTEGER,
|
||||
total_correct INTEGER,
|
||||
total_tasks INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','finished','abandoned'))
|
||||
);
|
||||
CREATE INDEX idx_mock_user ON exam_mock_sessions(user_id, started_at DESC);
|
||||
|
||||
-- ── User preparation plan ───────────────────────────────────────
|
||||
CREATE TABLE exam_user_plan (
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
exam_key TEXT NOT NULL,
|
||||
exam_date TEXT NOT NULL,
|
||||
daily_target INTEGER NOT NULL DEFAULT 10,
|
||||
weak_focus INTEGER NOT NULL DEFAULT 1,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, exam_key)
|
||||
);
|
||||
|
||||
-- ── Topic dictionary (populated in F6) ──────────────────────────
|
||||
CREATE TABLE exam_topics (
|
||||
slug TEXT PRIMARY KEY,
|
||||
exam_key TEXT NOT NULL,
|
||||
parent_slug TEXT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
CREATE INDEX idx_topics_exam ON exam_topics(exam_key, parent_slug);
|
||||
|
||||
-- ── Seed: math9 track ───────────────────────────────────────────
|
||||
-- Placeholder scoring grid (simple proportional). Replace with the actual
|
||||
-- Belarus 9th-grade math exam grid once published per academic year.
|
||||
INSERT INTO exam_tracks (
|
||||
exam_key, title, subject_slug, grade, duration_min,
|
||||
tasks_per_variant, variants_count, scoring_json, intro_html, enabled, sort_order
|
||||
) VALUES (
|
||||
'math9',
|
||||
'Экзамен 9 класс — Математика',
|
||||
'math',
|
||||
9,
|
||||
180,
|
||||
10,
|
||||
80,
|
||||
'[{"correct":30,"score":10},{"correct":27,"score":9},{"correct":24,"score":8},{"correct":21,"score":7},{"correct":18,"score":6},{"correct":15,"score":5},{"correct":12,"score":4},{"correct":9,"score":3},{"correct":6,"score":2},{"correct":3,"score":1},{"correct":0,"score":0}]',
|
||||
'<p>Полная подготовка к выпускному экзамену по математике за 9 класс. 80 реальных вариантов, разбор каждого задания, тренажёр по темам, пробные экзамены с таймером.</p>',
|
||||
1,
|
||||
10
|
||||
);
|
||||
Reference in New Issue
Block a user