feat(ct-math): диагностический тест из реальных вопросов банка (tests.id=164)
- backend/scripts/seed_ctmath_diagnostic.js — идемпотентный сбор ОДНОГО test «Диагностика ЦЭ/ЦТ — Математика» из размеченных вопросов ЦТ-11 (в осн. 2024): 5 single (базовые) + 10 fill-blank (средние/сложные), по 1 на ключевую тему. Новых вопросов не авторит. Применён: test id=164, 15 вопросов, лимит 40 мин. Выдать = assignment с test_id=164. - BUILD_ON_QUESTIONS.md / README: отметка о готовой диагностике, статус. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
/*
|
||||
* Входная диагностика для курса «ЦЭ/ЦТ — Математика».
|
||||
* Собирает ОДИН test из РЕАЛЬНЫХ размеченных вопросов ЦТ-11 (banks 2011–2024):
|
||||
* по 1 заданию на ключевую тему, смесь уровней (single 🟢 → fill-blank 🔴).
|
||||
* Новых вопросов НЕ авторит — только группирует существующие.
|
||||
* ИДЕМПОТЕНТЕН: если test с таким title есть — не дублирует.
|
||||
* Запуск: node backend/scripts/seed_ctmath_diagnostic.js (применить)
|
||||
* node backend/scripts/seed_ctmath_diagnostic.js --dry (показать выбор)
|
||||
*/
|
||||
const db = require('../src/db/db');
|
||||
const DRY = process.argv.includes('--dry');
|
||||
const MATH_ID = 3;
|
||||
const TITLE = 'Диагностика ЦЭ/ЦТ — Математика';
|
||||
const DESC = 'Входная диагностика: задания по ключевым темам (от базовых до сложных) для определения уровня и приоритетных тем подготовки к ЦЭ/ЦТ.';
|
||||
|
||||
// Слоты: тема (по имени) + предпочтительный тип + уровень-зонд.
|
||||
// Исключаем набор year=2025 («Экзамен 9»): берём только размеченные ЦТ-11 (year<=2024).
|
||||
const SLOTS = [
|
||||
['Теория чисел', 'single', 'base'],
|
||||
['Арифметика и степени', 'single', 'base'],
|
||||
['Квадратные уравнения', 'single', 'base'],
|
||||
['Тригонометрия', 'single', 'base'],
|
||||
['Числовые промежутки', 'single', 'base'],
|
||||
['Словесные задачи', 'fill-blank', 'mid'],
|
||||
['Прогрессии', 'fill-blank', 'mid'],
|
||||
['Функции', 'fill-blank', 'mid'],
|
||||
['Геометрия', 'fill-blank', 'mid'],
|
||||
['Окружность и круг', 'single', 'mid'],
|
||||
['Стереометрия', 'fill-blank', 'mid'],
|
||||
['Логарифмы', 'fill-blank', 'hard'],
|
||||
['Неравенства', 'fill-blank', 'hard'],
|
||||
['Уравнения', 'fill-blank', 'hard'],
|
||||
['Показательные неравенства','fill-blank', 'hard'],
|
||||
];
|
||||
|
||||
function topicId(name) {
|
||||
const r = db.prepare('SELECT id FROM topics WHERE subject_id=? AND LOWER(name)=LOWER(?)').get(MATH_ID, name);
|
||||
return r && r.id;
|
||||
}
|
||||
function adminId() {
|
||||
const u = db.prepare("SELECT id FROM users WHERE role='admin' ORDER BY id LIMIT 1").get()
|
||||
|| db.prepare('SELECT id FROM users ORDER BY id LIMIT 1').get();
|
||||
return u && u.id;
|
||||
}
|
||||
|
||||
// Кандидаты по теме: сперва предпочт. тип, потом любой; только размеченные ЦТ-11 (year<=2024 или not null),
|
||||
// исключая набор «Экзамен 9» (source_type='экзамен 9'); 2024 в приоритете, затем свежие.
|
||||
function candidates(tid, type) {
|
||||
const order = "ORDER BY (year=2024) DESC, year DESC, id";
|
||||
const base = `SELECT id, type, year, substr(text,1,70) AS t FROM questions
|
||||
WHERE subject_id=${MATH_ID} AND topic_id=${tid}
|
||||
AND (source_type IS NULL OR source_type <> 'экзамен 9')`;
|
||||
const pref = db.prepare(`${base} AND type=? ${order} LIMIT 8`).all(type);
|
||||
const any = db.prepare(`${base} ${order} LIMIT 8`).all();
|
||||
// предпочт. тип впереди, затем остальные (для фолбэка)
|
||||
const seen = new Set(pref.map(r => r.id));
|
||||
return [...pref, ...any.filter(r => !seen.has(r.id))];
|
||||
}
|
||||
|
||||
const used = new Set();
|
||||
const picks = [];
|
||||
for (const [name, type, level] of SLOTS) {
|
||||
const tid = topicId(name);
|
||||
if (!tid) { console.log(` [skip] нет темы: ${name}`); continue; }
|
||||
const cand = candidates(tid, type).find(r => !used.has(r.id));
|
||||
if (!cand) { console.log(` [skip] нет вопросов: ${name}`); continue; }
|
||||
used.add(cand.id);
|
||||
picks.push({ name, level, ...cand });
|
||||
}
|
||||
|
||||
console.log(DRY ? '[DRY-RUN] выбранные вопросы диагностики:' : '[APPLY] диагностика:');
|
||||
const mark = { base: 'базовый', mid: 'средний', hard: 'сложный' };
|
||||
picks.forEach((p, i) => console.log(
|
||||
` ${String(i + 1).padStart(2)}. [${mark[p.level]}] ${p.name} | qid ${p.id} (${p.type}, ${p.year || '—'}) — ${p.t.replace(/\s+/g, ' ')}…`
|
||||
));
|
||||
console.log(`\nВсего отобрано: ${picks.length} заданий.`);
|
||||
|
||||
const existing = db.prepare("SELECT id FROM tests WHERE subject_slug='math' AND title=?").get(TITLE);
|
||||
if (existing) {
|
||||
console.log(`\nТест «${TITLE}» уже существует (id ${existing.id}) — не дублирую.`);
|
||||
} else if (DRY) {
|
||||
console.log(`\nDRY-RUN: тест НЕ создан. Будет создан с ${picks.length} вопросами.`);
|
||||
} else {
|
||||
const by = adminId();
|
||||
const testId = db.prepare(
|
||||
'INSERT INTO tests (title, subject_slug, description, show_answers, time_limit, created_by) VALUES (?,?,?,?,?,?)'
|
||||
).run(TITLE, 'math', DESC, 1, 40, by).lastInsertRowid;
|
||||
const ins = db.prepare('INSERT INTO test_questions (test_id, question_id, order_index) VALUES (?,?,?)');
|
||||
picks.forEach((p, i) => ins.run(testId, p.id, i));
|
||||
console.log(`\nСоздан тест «${TITLE}» (id ${testId}, ${picks.length} вопросов, лимит 40 мин).`);
|
||||
console.log('Выдать классу/ученику: assignment с test_id=' + testId + ' (mode неважен, test_id перекрывает выбор).');
|
||||
}
|
||||
@@ -20,7 +20,10 @@
|
||||
- ✅ Создан **DRAFT-курс** «ЦЭ/ЦТ — Математика» (`courses.id=13`, `is_published=0`, created_by=2) — ученикам НЕ виден до публикации.
|
||||
- ✅ 9 секций (`course_sections.id=27..35`) = блоки I–IX.
|
||||
|
||||
**Дальше (не сделано):** уроки (`lesson_blocks`) по пилотам; диагностический `test`; assignment-практика `mode='topic'`; колоды формул; публикация курса. См. §8.
|
||||
**Сделано (скрипт `backend/scripts/seed_ctmath_diagnostic.js`, применён 2026-06-14):**
|
||||
- ✅ **Диагностический `test`** «Диагностика ЦЭ/ЦТ — Математика» (`tests.id=164`, 15 вопросов, лимит 40 мин, `show_answers=1`) — собран из РЕАЛЬНЫХ размеченных вопросов ЦТ-11 (в осн. 2024): 5 `single` (базовые, по 1 на тему Теория чисел/Арифметика/Квадратные/Тригонометрия/Промежутки) + 10 `fill-blank` (средние/сложные: Словесные/Прогрессии/Функции/Геометрия/Окружность/Стереометрия/Логарифмы/Неравенства/Уравнения/Показательные). Новых вопросов не авторили. Выдать классу/ученику: assignment с `test_id=164`.
|
||||
|
||||
**Дальше (не сделано):** уроки (`lesson_blocks`) по пилотам; assignment-практика `mode='topic'`; колоды формул; публикация курса; выдача диагностики классу (assignment `test_id=164`). См. §8.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@
|
||||
«ЦЭ/ЦТ — Математика» (`courses.id=13`, не опубликован) + 9 секций (id 27–35). Существующие данные
|
||||
не тронуты. Миграция 077 (exam-prep) в БД не применялась.
|
||||
|
||||
Реализация (BUILD_ON_QUESTIONS §8): ✅1 темы · ✅2 каркас курса · ⬜3 диагностика · ⬜4 уроки · ⬜5 пробники · ⬜6 карточки/публикация.
|
||||
Реализация (BUILD_ON_QUESTIONS §8): ✅1 темы · ✅2 каркас курса · ✅3 диагностика (`tests.id=164`, 15 вопросов) · ⬜4 уроки · ⬜5 пробники/практика · ⬜6 карточки/публикация.
|
||||
|
||||
Следующий шаг на выбор:
|
||||
- собрать диагностический `test` из реальных вопросов банка (по 1 на тему);
|
||||
- наполнить уроки первого блока (стерео/тригонометрия) по пилотам;
|
||||
- настроить практику `mode='topic'` по темам.
|
||||
- настроить практику `mode='topic'` по темам (и выдать диагностику классу — assignment `test_id=164`);
|
||||
- собрать пробный вариант (`test` single+fill-blank на 30 заданий).
|
||||
|
||||
Reference in New Issue
Block a user