feat(ct-math): каркас курса ЦЭ/ЦТ на банке questions (темы + draft-курс + секции)
- backend/scripts/seed_ctmath_course.js — идемпотентный аддитивный seed: +6 тем (Преобразование выражений/Модуль/Иррациональные ур./Показательные ур./ Производная/Параметры), DRAFT-курс «ЦЭ/ЦТ — Математика» + 9 секций. Применён на живой БД: course id=13 (is_published=0), topics 72-77, sections 27-35. Существующие данные не тронуты; повторный запуск ничего не дублирует. - BUILD_ON_QUESTIONS.md: уточнения инспекции банка (year=2025 = «Экзамен 9», без тем; реальный ЦТ-11 = ~733 размеч., Часть B = fill-blank → гоча mode='ct') + блок «Состояние реализации». - README: статус каркаса. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
/*
|
||||
* Каркас курса «ЦЭ/ЦТ — Математика» на существующем банке questions.
|
||||
* План: plans/ct-math/ (BUILD_ON_QUESTIONS.md).
|
||||
* ИДЕМПОТЕНТЕН и АДДИТИВЕН: добавляет недостающие темы (topics),
|
||||
* создаёт DRAFT-курс (is_published=0) + 9 секций. Существующие данные не трогает.
|
||||
* Запуск: node backend/scripts/seed_ctmath_course.js (применить)
|
||||
* node backend/scripts/seed_ctmath_course.js --dry (только показать план)
|
||||
*/
|
||||
const db = require('../src/db/db');
|
||||
const DRY = process.argv.includes('--dry');
|
||||
const MATH_ID = 3;
|
||||
|
||||
// 1) Недостающие темы под модульную карту (см. BUILD_ON_QUESTIONS §3)
|
||||
const NEW_TOPICS = [
|
||||
'Преобразование выражений',
|
||||
'Модуль',
|
||||
'Иррациональные уравнения',
|
||||
'Показательные уравнения',
|
||||
'Производная',
|
||||
'Параметры',
|
||||
];
|
||||
|
||||
// 2) Секции курса = 9 блоков (PLAN §3)
|
||||
const SECTIONS = [
|
||||
'Числа и вычисления',
|
||||
'Алгебраические преобразования',
|
||||
'Уравнения и неравенства',
|
||||
'Функции и производная',
|
||||
'Тригонометрия',
|
||||
'Прогрессии и текстовые задачи',
|
||||
'Планиметрия',
|
||||
'Стереометрия',
|
||||
'Продвинутое и комбинированное',
|
||||
];
|
||||
|
||||
const COURSE_TITLE = 'ЦЭ/ЦТ — Математика';
|
||||
const COURSE_DESC = 'Подготовка к ЦЭ/ЦТ по математике: 30 заданий (часть А — А1–А10, часть В — В1–В20). Теория по темам, тренажёр на банке заданий прошлых лет, карточки формул, пробные варианты.';
|
||||
|
||||
function topicExists(name) {
|
||||
return db.prepare('SELECT id FROM topics WHERE subject_id=? AND LOWER(name)=LOWER(?)').get(MATH_ID, name);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
let addedTopics = 0, skippedTopics = 0;
|
||||
console.log(DRY ? '[DRY-RUN] план изменений:' : '[APPLY] вношу изменения:');
|
||||
|
||||
console.log('\n— Темы (topics) —');
|
||||
for (const name of NEW_TOPICS) {
|
||||
const ex = topicExists(name);
|
||||
if (ex) { console.log(` есть: ${name} (id ${ex.id})`); skippedTopics++; continue; }
|
||||
if (DRY) { console.log(` + добавить: ${name}`); addedTopics++; continue; }
|
||||
const id = db.prepare('INSERT INTO topics (subject_id,name) VALUES (?,?)').run(MATH_ID, name).lastInsertRowid;
|
||||
console.log(` + добавлено: ${name} (id ${id})`);
|
||||
addedTopics++;
|
||||
}
|
||||
|
||||
console.log('\n— Курс (courses) —');
|
||||
let course = db.prepare("SELECT id,is_published FROM courses WHERE subject_slug='math' AND title=?").get(COURSE_TITLE);
|
||||
let courseId;
|
||||
if (course) {
|
||||
courseId = course.id;
|
||||
console.log(` есть курс «${COURSE_TITLE}» (id ${courseId}, ${course.is_published ? 'published' : 'draft'})`);
|
||||
} else if (DRY) {
|
||||
console.log(` + создать DRAFT-курс «${COURSE_TITLE}» (created_by=${adminId()})`);
|
||||
} else {
|
||||
const by = adminId();
|
||||
// cover_emoji не указываем — применится дефолт схемы; в коде эмодзи не вводим
|
||||
courseId = db.prepare(
|
||||
'INSERT INTO courses (subject_slug,title,description,is_published,created_by) VALUES (?,?,?,0,?)'
|
||||
).run('math', COURSE_TITLE, COURSE_DESC, by).lastInsertRowid;
|
||||
console.log(` + создан DRAFT-курс «${COURSE_TITLE}» (id ${courseId}, created_by=${by})`);
|
||||
}
|
||||
|
||||
console.log('\n— Секции (course_sections) —');
|
||||
if (!courseId && DRY) {
|
||||
SECTIONS.forEach((t, i) => console.log(` + секция [${i + 1}] ${t}`));
|
||||
} else if (courseId) {
|
||||
SECTIONS.forEach((title, i) => {
|
||||
const ex = db.prepare('SELECT id FROM course_sections WHERE course_id=? AND title=?').get(courseId, title);
|
||||
if (ex) { console.log(` есть: [${i + 1}] ${title} (id ${ex.id})`); return; }
|
||||
if (DRY) { console.log(` + секция [${i + 1}] ${title}`); return; }
|
||||
const id = db.prepare('INSERT INTO course_sections (course_id,title,order_index) VALUES (?,?,?)').run(courseId, title, i + 1).lastInsertRowid;
|
||||
console.log(` + секция [${i + 1}] ${title} (id ${id})`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`\nИтог: темы +${addedTopics} (есть ${skippedTopics}); курс id=${courseId || '(dry)'}; секций ${SECTIONS.length}.`);
|
||||
console.log(DRY ? 'DRY-RUN: ничего не записано.' : 'Готово. Курс создан как ЧЕРНОВИК (is_published=0) — ученикам не виден до публикации.');
|
||||
@@ -13,6 +13,17 @@
|
||||
|
||||
---
|
||||
|
||||
## 0. Состояние реализации
|
||||
|
||||
**Сделано (скрипт `backend/scripts/seed_ctmath_course.js`, идемпотентный, применён на живой БД 2026-06-14):**
|
||||
- ✅ Добавлены 6 тем (`topics`, subject_id=3): Преобразование выражений (72), Модуль (73), Иррациональные уравнения (74), Показательные уравнения (75), Производная (76), Параметры (77).
|
||||
- ✅ Создан **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.
|
||||
|
||||
---
|
||||
|
||||
## 1. Что уже есть (проверено чтением БД)
|
||||
|
||||
| Таблица | Роль | Факт |
|
||||
@@ -26,6 +37,12 @@
|
||||
| `courses`/`course_sections`/`lessons`/`lesson_blocks` | теория | общий слой контента |
|
||||
| `flashcard_*` | карточки + SR | для формул |
|
||||
|
||||
### 1.1. Уточнение по инспекции (2026-06-14) — ВАЖНО
|
||||
|
||||
Из 1753 матем. заданий:
|
||||
- **~733 — реальный банк ЦЭ/ЦТ-11** (2011–2024), **размечены по темам**. Часть A = `single`, **Часть B = `fill-blank`** (198 шт.), `short_answer` у них НЕТ. **Это база курса.**
|
||||
- **1020 — набор `year=2025`, `source_type='экзамен 9'` = «Экзамен 9 класс», БЕЗ тем** (`topic_id` пустой), все `difficulty=1`, типы `single`+`short_answer`. Это контент 9-класса (из него собраны тесты «Экзамен 9 — Вариант N»). **Для курса ЦЭ/ЦТ-11 — НЕ основа** (другой экзамен/класс; не размечен).
|
||||
|
||||
**Типы заданий (`questions.type`)**: `single`, `multi`, `true_false`, `short_answer`, `matching`, `fill-blank`.
|
||||
**Сложность**: `difficulty` 1–3 (CHECK), 1=базовый/2=средний/3=продвинутый. (В [PLAN.md](PLAN.md) шкала 1–5 — привести к 1–3: А-часть и лёгкая В = 1, средняя В = 2, сложная В14–В20 = 3.)
|
||||
**Год**: `year` (2011…2025) — фильтр по году/варианту.
|
||||
@@ -39,7 +56,7 @@
|
||||
|
||||
Логика `assignmentController.startAssignment` (подтверждено по коду):
|
||||
|
||||
- **`mode='ct'`** — собирает ЦТ-вариант: ~половина из `type IN ('single','true_false')` (Часть A) + остаток из `type IN ('multi','short_answer')` (Часть B), добор при нехватке. **Это готовый «пробник» формата ЦТ.** Можно ограничить `topic_id`.
|
||||
- **`mode='ct'`** — собирает ЦТ-вариант: ~половина из `type IN ('single','true_false')` (Часть A) + остаток из `type IN ('multi','short_answer')` (Часть B), добор при нехватке. ⚠️ **Гоча:** реальный банк ЦТ-11 имеет Часть B типа **`fill-blank`**, который `mode='ct'` НЕ выбирает (он ждёт `short_answer`) → для ЦТ-11 Часть B соберётся только «добором». `mode='ct'` хорошо ложится на набор «Экзамен 9» (там Часть B = `short_answer`). **Для ЦТ-11 надёжнее `mode='topic'` или ручная сборка `test` (single + fill-blank).**
|
||||
- **`mode='topic'`** — `SELECT id FROM questions WHERE subject_id=3 AND topic_id=? ORDER BY RANDOM() LIMIT count`. **Тренажёр по теме модуля.**
|
||||
- **`mode='exam'|'practice'|'repeat'`** — случайные `count` по предмету (или теме).
|
||||
- **`test_id` задан** — берутся ИМЕННО вопросы теста в его порядке (перекрывает режим). **Так делается фиксированная диагностика.**
|
||||
|
||||
+10
-8
@@ -40,12 +40,14 @@
|
||||
|
||||
## Статус
|
||||
|
||||
ПЛАН переориентирован на банк `questions` (пивот). Закоммичено в master (`7eb6cb2`): PLAN, TOPICS_SEED,
|
||||
два пилота, DIGITIZATION_SPEC, RESOURCES, миграция 077. Добавлен BUILD_ON_QUESTIONS.md + примечания-пивот
|
||||
(этот коммит). Миграция 077 в БД **не применялась**; ничего в живой БД не менялось.
|
||||
ПЛАН на банке `questions` (пивот). **Каркас курса создан в живой БД** (скрипт
|
||||
`backend/scripts/seed_ctmath_course.js`, идемпотентный): 6 новых тем (id 72–77), DRAFT-курс
|
||||
«ЦЭ/ЦТ — Математика» (`courses.id=13`, не опубликован) + 9 секций (id 27–35). Существующие данные
|
||||
не тронуты. Миграция 077 (exam-prep) в БД не применялась.
|
||||
|
||||
Следующий конкретный шаг на выбор:
|
||||
- добавить недостающие `topics` (миграция/скрипт) и собрать каркас курса;
|
||||
- собрать диагностический `test` из реальных вопросов банка;
|
||||
- детализировать ещё блок теории (уравнения/неравенства или функции+производная);
|
||||
- закоммитить изменения этого захода.
|
||||
Реализация (BUILD_ON_QUESTIONS §8): ✅1 темы · ✅2 каркас курса · ⬜3 диагностика · ⬜4 уроки · ⬜5 пробники · ⬜6 карточки/публикация.
|
||||
|
||||
Следующий шаг на выбор:
|
||||
- собрать диагностический `test` из реальных вопросов банка (по 1 на тему);
|
||||
- наполнить уроки первого блока (стерео/тригонометрия) по пилотам;
|
||||
- настроить практику `mode='topic'` по темам.
|
||||
|
||||
Reference in New Issue
Block a user