Files
Learn_System/backend/scripts/seed_ctmath_lessons_trig.js
T
Maxim Dolgolyov 31719b2e79 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>
2026-06-15 07:41:15 +03:00

137 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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: ничего не записано.' : 'Готово. Уроки тригонометрии добавлены в курс (черновик; ученикам видны после публикации курса).');