feat(ct-math): уроки блока «Тригонометрия» (3 урока в курсе ЦЭ/ЦТ)

- backend/scripts/seed_ctmath_lessons_trig.js — идемпотентный seed 3 уроков по
  PILOT_TRIGONOMETRY в секцию «Тригонометрия» курса 13:
  круг и значения (lessons.id=41, 18 блоков, А3), тождества и формулы (id=42,
  19 блоков, А8/В4), уравнения и отбор корней (id=43, 15 блоков, В15).
  Форматы блоков сверены с рендером frontend/lesson.html (heading/text/formula/
  callout/sim trigcircle/flashcard/quiz/matching/ordering/accordion/table;
  math $…$/$$…$$; data JSON валиден). Уроки — в DRAFT-курсе (ученикам не видны).
- BUILD_ON_QUESTIONS.md / README: статус (блок «Тригонометрия» готов).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-15 07:41:15 +03:00
parent 228bd885ed
commit 31719b2e79
3 changed files with 149 additions and 4 deletions
+136
View File
@@ -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: ничего не записано.' : 'Готово. Уроки тригонометрии добавлены в курс (черновик; ученикам видны после публикации курса).');
+10 -1
View File
@@ -23,7 +23,16 @@
**Сделано (скрипт `backend/scripts/seed_ctmath_diagnostic.js`, применён 2026-06-14):** **Сделано (скрипт `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`. -**Диагностический `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.
--- ---
+3 -3
View File
@@ -45,9 +45,9 @@
«ЦЭ/ЦТ — Математика» (`courses.id=13`, не опубликован) + 9 секций (id 27–35). Существующие данные «ЦЭ/ЦТ — Математика» (`courses.id=13`, не опубликован) + 9 секций (id 27–35). Существующие данные
не тронуты. Миграция 077 (exam-prep) в БД не применялась. не тронуты. Миграция 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=4143`; остальные 8 блоков — нет) · ⬜5 пробники/практика · ⬜6 карточки/публикация.
Следующий шаг на выбор: Следующий шаг на выбор:
- наполнить уроки первого блока (стерео/тригонометрия) по пилотам; - тиражировать уроки на следующий блок (рекомендую **стереометрию** по пилоту);
- настроить практику `mode='topic'` по темам (и выдать диагностику классу — assignment `test_id=164`); - настроить практику `mode='topic'` по темам (и выдать диагностику классу — assignment `test_id=164`);
- собрать пробный вариант (`test` single+fill-blank на 30 заданий). - собрать колоду карточек формул для тригонометрии (`flashcard_decks`).