From 228bd885ed59cf04c144bea7f8aef7fc28541be1 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Sun, 14 Jun 2026 22:16:27 +0300 Subject: [PATCH] =?UTF-8?q?feat(ct-math):=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=20=D0=B8=D0=B7=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=BB=D1=8C=D0=BD=D1=8B=D1=85=20=D0=B2=D0=BE=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=20=D0=B1=D0=B0=D0=BD=D0=BA=D0=B0=20(tests.id?= =?UTF-8?q?=3D164)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- backend/scripts/seed_ctmath_diagnostic.js | 93 +++++++++++++++++++++++ plans/ct-math/BUILD_ON_QUESTIONS.md | 5 +- plans/ct-math/README.md | 6 +- 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 backend/scripts/seed_ctmath_diagnostic.js diff --git a/backend/scripts/seed_ctmath_diagnostic.js b/backend/scripts/seed_ctmath_diagnostic.js new file mode 100644 index 0000000..0d8f425 --- /dev/null +++ b/backend/scripts/seed_ctmath_diagnostic.js @@ -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 перекрывает выбор).'); +} diff --git a/plans/ct-math/BUILD_ON_QUESTIONS.md b/plans/ct-math/BUILD_ON_QUESTIONS.md index 9b972ee..ded556a 100644 --- a/plans/ct-math/BUILD_ON_QUESTIONS.md +++ b/plans/ct-math/BUILD_ON_QUESTIONS.md @@ -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. --- diff --git a/plans/ct-math/README.md b/plans/ct-math/README.md index 1176ab9..f7e8504 100644 --- a/plans/ct-math/README.md +++ b/plans/ct-math/README.md @@ -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 заданий).