diff --git a/backend/scripts/seed_ctmath_lessons_trig.js b/backend/scripts/seed_ctmath_lessons_trig.js new file mode 100644 index 0000000..75bbeec --- /dev/null +++ b/backend/scripts/seed_ctmath_lessons_trig.js @@ -0,0 +1,136 @@ +'use strict'; +/* + * Уроки блока «Тригонометрия» курса «ЦЭ/ЦТ — Математика» (по PILOT_TRIGONOMETRY.md). + * Создаёт 3 урока (круг → тождества → уравнения) в секции «Тригонометрия» курса. + * Форматы блоков — РОВНО под рендер frontend/lesson.html (text/heading/callout + * экранируются → только текст; математика через $...$ / $$...$$; callout.style + * = info|warning|success|error). data хранится JSON-строкой (API её парсит). + * ИДЕМПОТЕНТЕН: урок с тем же title в курсе не создаётся повторно. + * Запуск: node backend/scripts/seed_ctmath_lessons_trig.js [--dry] + */ +const db = require('../src/db/db'); +const DRY = process.argv.includes('--dry'); + +const COURSE_TITLE = 'ЦЭ/ЦТ — Математика'; +const SECTION_TITLE = 'Тригонометрия'; + +const course = db.prepare("SELECT id FROM courses WHERE subject_slug='math' AND title=?").get(COURSE_TITLE); +if (!course) { console.error('Нет курса «' + COURSE_TITLE + '». Сначала: node backend/scripts/seed_ctmath_course.js'); process.exit(1); } +const section = db.prepare('SELECT id FROM course_sections WHERE course_id=? AND title=?').get(course.id, SECTION_TITLE); +if (!section) { console.error('Нет секции «' + SECTION_TITLE + '» в курсе ' + course.id); process.exit(1); } + +// helpers для краткости описания блоков +const H = (text, level = 2) => ['heading', { text, level }]; +const P = (text) => ['text', { text }]; +const F = (tex, label) => ['formula', label ? { label, tex } : { tex }]; +const CI = (text) => ['callout', { style: 'info', text }]; +const CW = (text) => ['callout', { style: 'warning', text }]; +const CS = (text) => ['callout', { style: 'success', text }]; +const SIM= (caption) => ['sim', { simId: 'trigcircle', caption }]; +const FC = (front, back) => ['flashcard', { front, back }]; +const QZ = (question, options, correctIndex) => ['quiz', { question, options, correctIndex }]; +const ORD= (question, items) => ['ordering', { question, items }]; +const MAT= (question, pairs) => ['matching', { question, pairs }]; +const ACC= (title, content) => ['accordion', { title, content }]; +const TBL= (header, rows) => ['table', { header, rows }]; + +// ── Урок 1: Тригонометрический круг и значения (А3, базовый) ── +const L1 = [ + H('Тригонометрический круг: смысл синуса и косинуса'), + P('Возьмём окружность радиуса 1 с центром в начале координат. При повороте на угол $\\alpha$ точка на этой окружности получает координаты $(\\cos\\alpha;\\ \\sin\\alpha)$. Это определение, из которого выводится вся тригонометрия: заучивать таблицы наизусть не нужно — нужно уметь «читать» круг.'), + F('\\cos\\alpha = x,\\quad \\sin\\alpha = y,\\quad \\operatorname{tg}\\alpha=\\dfrac{y}{x},\\quad \\operatorname{ctg}\\alpha=\\dfrac{x}{y}', 'Определения через единичную окружность'), + SIM('Покрутите угол и следите за координатами точки — это и есть $\\cos\\alpha$ и $\\sin\\alpha$'), + CI('Знаки по четвертям: I (+,+), II (−,+), III (−,−), IV (+,−). Косинус — это абсцисса, синус — ордината.'), + H('Значения для основных углов', 3), + TBL( + ['$\\alpha$', '$0$', '$\\tfrac{\\pi}{6}$', '$\\tfrac{\\pi}{4}$', '$\\tfrac{\\pi}{3}$', '$\\tfrac{\\pi}{2}$'], + [ + ['$\\sin\\alpha$', '$0$', '$\\tfrac{1}{2}$', '$\\tfrac{\\sqrt2}{2}$', '$\\tfrac{\\sqrt3}{2}$', '$1$'], + ['$\\cos\\alpha$', '$1$', '$\\tfrac{\\sqrt3}{2}$', '$\\tfrac{\\sqrt2}{2}$', '$\\tfrac{1}{2}$', '$0$'], + ['$\\operatorname{tg}\\alpha$', '$0$', '$\\tfrac{\\sqrt3}{3}$', '$1$', '$\\sqrt3$', '—'], + ] + ), + F('\\sin x = 0 \\iff x=\\pi k;\\qquad \\cos x = 0 \\iff x=\\tfrac{\\pi}{2}+\\pi k', 'Когда функция равна нулю'), + CW('Типичная ошибка — путать, где ноль у синуса (при $0,\\ \\pi,\\ 2\\pi,\\dots$) и у косинуса (при $\\tfrac{\\pi}{2},\\ \\tfrac{3\\pi}{2},\\dots$). На круге это видно сразу: синус — высота, косинус — горизонталь.'), + FC('$\\sin x = 0$ при каких $x$?', '$x = \\pi k,\\ k\\in\\mathbb{Z}$'), + FC('$\\cos x = 0$ при каких $x$?', '$x = \\tfrac{\\pi}{2}+\\pi k$'), + H('Разбор задания А3', 3), + P('Типичное А3: среди нескольких значений аргумента указать то, при котором функция равна нулю.'), + P('Пример. Среди $-\\tfrac{\\pi}{6};\\ \\tfrac{\\pi}{4};\\ \\tfrac{\\pi}{3};\\ -\\tfrac{3\\pi}{2};\\ -6\\pi$ укажите то, при котором $\\sin x = 0$.'), + P('Решение. $\\sin x=0$ только когда $x$ кратно $\\pi$. Из списка кратно $\\pi$ лишь $-6\\pi$.'), + CS('Ответ: $-6\\pi$.'), + QZ('При каком значении аргумента cos x = 1?', ['π/2', 'π', '0', '3π/2'], 2), + CI('Тренажёр по теме «Тригонометрия» (реальные задания А3 прошлых лет) — в практике курса. Цель освоения: не менее 90% на заданиях А3.'), +]; + +// ── Урок 2: Тождества и формулы (А8, В4, средний) ── +const L2 = [ + H('Тождества: как не учить 30 формул'), + F('\\sin^2\\alpha+\\cos^2\\alpha=1', 'Основное тригонометрическое тождество'), + P('Это теорема Пифагора для точки $(\\cos\\alpha;\\ \\sin\\alpha)$ на единичной окружности. Разделив его на $\\cos^2\\alpha$ и на $\\sin^2\\alpha$, получаем связи с тангенсом и котангенсом — их выводят на месте, а не заучивают.'), + F('1+\\operatorname{tg}^2\\alpha=\\dfrac{1}{\\cos^2\\alpha},\\qquad 1+\\operatorname{ctg}^2\\alpha=\\dfrac{1}{\\sin^2\\alpha}'), + ACC('Формулы сложения и двойного угла (раскрыть)', 'Сложение: $\\sin(\\alpha\\pm\\beta)=\\sin\\alpha\\cos\\beta\\pm\\cos\\alpha\\sin\\beta$; $\\cos(\\alpha\\pm\\beta)=\\cos\\alpha\\cos\\beta\\mp\\sin\\alpha\\sin\\beta$. Двойной угол: $\\sin 2\\alpha=2\\sin\\alpha\\cos\\alpha$; $\\cos 2\\alpha=\\cos^2\\alpha-\\sin^2\\alpha$. Все они следуют из формул сложения.'), + CI('Обратные функции и их области значений (на них ловят в А8): $\\arcsin x\\in[-\\tfrac{\\pi}{2};\\tfrac{\\pi}{2}]$, $\\arccos x\\in[0;\\pi]$, $\\operatorname{arctg} x\\in(-\\tfrac{\\pi}{2};\\tfrac{\\pi}{2})$.'), + MAT('Сопоставьте выражение и тождественно равное ему', [ + { left: '$\\sin 2\\alpha$', right: '$2\\sin\\alpha\\cos\\alpha$' }, + { left: '$\\cos 2\\alpha$', right: '$\\cos^2\\alpha-\\sin^2\\alpha$' }, + { left: '$1-\\cos 2\\alpha$', right: '$2\\sin^2\\alpha$' }, + ]), + H('Разбор А8 (обратные функции и модуль)', 3), + P('Пример. Найдите значение $\\dfrac{38}{\\pi}\\cdot\\arcsin(-1)-|-7|$.'), + P('Решение. $\\arcsin(-1)=-\\tfrac{\\pi}{2}$, поэтому $\\dfrac{38}{\\pi}\\cdot\\left(-\\tfrac{\\pi}{2}\\right)=-19$; далее $-19-7=-26$.'), + CS('Ответ: $-26$.'), + H('Разбор В4 (тождество)', 3), + P('Пример. Найдите $\\operatorname{ctg}^2\\alpha$, если $\\sin\\alpha=\\tfrac{1}{5}$.'), + P('Решение. $\\cos^2\\alpha=1-\\tfrac{1}{25}=\\tfrac{24}{25}$, поэтому $\\operatorname{ctg}^2\\alpha=\\dfrac{\\cos^2\\alpha}{\\sin^2\\alpha}=\\dfrac{24/25}{1/25}=24$.'), + CS('Ответ: $24$.'), + FC('$1+\\operatorname{tg}^2\\alpha$', '$\\dfrac{1}{\\cos^2\\alpha}$'), + FC('$\\cos 2\\alpha$', '$\\cos^2\\alpha-\\sin^2\\alpha=1-2\\sin^2\\alpha=2\\cos^2\\alpha-1$'), + FC('Область значений $\\arccos x$', '$[0;\\ \\pi]$'), + CI('Тренажёр: реальные задания А8 и В4 в практике курса. Цель освоения: не менее 85%.'), +]; + +// ── Урок 3: Уравнения и отбор корней (В15, продвинутый) ── +const L3 = [ + H('Тригонометрические уравнения и отбор корней'), + F('\\sin x=a\\Rightarrow x=(-1)^n\\arcsin a+\\pi n;\\quad \\cos x=a\\Rightarrow x=\\pm\\arccos a+2\\pi n;\\quad \\operatorname{tg} x=a\\Rightarrow x=\\operatorname{arctg} a+\\pi n', 'Формулы корней простейших уравнений'), + P('Стратегия В15: сначала свести уравнение к произведению или простейшему виду формулами преобразования; затем выписать общие формулы корней; затем отобрать корни, попадающие в заданный промежуток; и наконец выполнить требуемое (например, найти сумму корней).'), + ORD('Расставьте шаги решения В15 по порядку', [ + 'Преобразовать уравнение к произведению или простейшему виду', + 'Выписать общие формулы корней', + 'Подставить целые n и отобрать корни на заданном промежутке', + 'Сложить (или иначе обработать) отобранные корни', + ]), + SIM('Отбор корней: отметьте промежуток и проверьте, какие корни в него попадают'), + CW('Самая частая потеря баллов в В15 — неполный отбор корней и потеря ОДЗ (для $\\operatorname{tg}$ и $\\operatorname{ctg}$). Проверяйте оба семейства корней.'), + H('Разбор простого примера', 3), + P('Найдите (в градусах) сумму корней уравнения $\\cos 2x=0$ на промежутке $(0^\\circ;\\ 180^\\circ)$.'), + P('Решение. $\\cos 2x=0\\Rightarrow 2x=90^\\circ+180^\\circ k\\Rightarrow x=45^\\circ+90^\\circ k$. На промежутке лежат $45^\\circ$ и $135^\\circ$. Их сумма равна $180$.'), + CS('Ответ: $180$.'), + ACC('Более сложный пример (В15 из ЦЭ-2024) — раскрыть', 'Найдите сумму различных корней уравнения $2\\sin 3x\\cos 3x-\\sin 6x\\sin 10x=0$ на промежутке $(-150^\\circ;-55^\\circ)$. Идея: $2\\sin 3x\\cos 3x=\\sin 6x$, выносим общий множитель: $\\sin 6x\\,(1-\\sin 10x)=0$. Дальше решаем $\\sin 6x=0$ или $\\sin 10x=1$ и отбираем корни на промежутке.'), + FC('$\\sin x=a$ (корни)', '$x=(-1)^n\\arcsin a+\\pi n$'), + FC('$\\cos x=a$ (корни)', '$x=\\pm\\arccos a+2\\pi n$'), + FC('$\\operatorname{tg} x=a$ (корни)', '$x=\\operatorname{arctg} a+\\pi n$'), + CI('Тренажёр: тема «Тригонометрические уравнения» (В15) в практике курса. Цель освоения: не менее 70%, отбор корней без потерь.'), +]; + +const LESSONS = [ + { title: 'Тригонометрический круг и значения', read: 9, blocks: L1 }, + { title: 'Тождества и формулы', read: 10, blocks: L2 }, + { title: 'Уравнения и отбор корней', read: 11, blocks: L3 }, +]; + +console.log(DRY ? '[DRY-RUN]' : '[APPLY]', `курс id=${course.id}, секция «${SECTION_TITLE}» id=${section.id}`); +const insLesson = db.prepare('INSERT INTO lessons (course_id, title, order_index, is_published, section_id, read_time) VALUES (?,?,?,1,?,?)'); +const insBlock = db.prepare('INSERT INTO lesson_blocks (lesson_id, type, order_index, data) VALUES (?,?,?,?)'); + +LESSONS.forEach((L, i) => { + const ex = db.prepare('SELECT id FROM lessons WHERE course_id=? AND title=?').get(course.id, L.title); + if (ex) { console.log(` есть урок: «${L.title}» (id ${ex.id}) — пропуск`); return; } + if (DRY) { console.log(` + урок «${L.title}» (${L.blocks.length} блоков)`); return; } + const lid = insLesson.run(course.id, L.title, i + 1, section.id, L.read).lastInsertRowid; + L.blocks.forEach(([type, data], bi) => insBlock.run(lid, type, bi, JSON.stringify(data))); + console.log(` + урок «${L.title}» (id ${lid}, ${L.blocks.length} блоков)`); +}); + +console.log(DRY ? 'DRY-RUN: ничего не записано.' : 'Готово. Уроки тригонометрии добавлены в курс (черновик; ученикам видны после публикации курса).'); diff --git a/plans/ct-math/BUILD_ON_QUESTIONS.md b/plans/ct-math/BUILD_ON_QUESTIONS.md index ded556a..c3ad7c5 100644 --- a/plans/ct-math/BUILD_ON_QUESTIONS.md +++ b/plans/ct-math/BUILD_ON_QUESTIONS.md @@ -23,7 +23,16 @@ **Сделано (скрипт `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. +**Сделано (скрипт `backend/scripts/seed_ctmath_lessons_trig.js`, применён 2026-06-15):** +- ✅ **Блок «Тригонометрия» — 3 урока** в секции 31 курса 13 (по [PILOT_TRIGONOMETRY.md](PILOT_TRIGONOMETRY.md)): + «Тригонометрический круг и значения» (`lessons.id=41`, 18 блоков, А3 🟢), «Тождества и формулы» + (`id=42`, 19 блоков, А8/В4 🟡), «Уравнения и отбор корней» (`id=43`, 15 блоков, В15 🔴). + Блоки в формате рендера lesson.html (heading/text/formula/callout/sim `trigcircle`/flashcard/quiz/ + matching/ordering/accordion/table); математика `$…$`/`$$…$$`; JSON валиден; идемпотентно. + +**Дальше (не сделано):** уроки остальных 8 блоков по пилотам (тиражирование, начать со стереометрии); +assignment-практика `mode='topic'`; колоды формул (`flashcard_decks`); публикация курса; выдача +диагностики классу (assignment `test_id=164`). См. §8. --- diff --git a/plans/ct-math/README.md b/plans/ct-math/README.md index f7e8504..edbb114 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 диагностика (`tests.id=164`, 15 вопросов) · ⬜4 уроки · ⬜5 пробники/практика · ⬜6 карточки/публикация. +Реализация (BUILD_ON_QUESTIONS §8): ✅1 темы · ✅2 каркас курса · ✅3 диагностика (`tests.id=164`, 15 вопросов) · 🟦4 уроки (блок «Тригонометрия» готов: `lessons.id=41–43`; остальные 8 блоков — нет) · ⬜5 пробники/практика · ⬜6 карточки/публикация. Следующий шаг на выбор: -- наполнить уроки первого блока (стерео/тригонометрия) по пилотам; +- тиражировать уроки на следующий блок (рекомендую **стереометрию** по пилоту); - настроить практику `mode='topic'` по темам (и выдать диагностику классу — assignment `test_id=164`); -- собрать пробный вариант (`test` single+fill-blank на 30 заданий). +- собрать колоду карточек формул для тригонометрии (`flashcard_decks`).