Files
Learn_System/backend/scripts/seed_ctmath_lessons_rest.js
T
Maxim Dolgolyov a982628d04 feat(ct-math): уроки всех остальных блоков (48-55) + 4 колоды флешкарт формул
- seed_ctmath_lessons_rest.js — 8 уроков по PLAN: числа, преобразования,
  уравнения (квадратные/рацион/модуль + показ/лог/иррац+рационализация),
  функции+производная, прогрессии/текстовые, планиметрия, параметры.
  Курс 13 теперь покрывает все 9 секций (15 уроков, lessons.id=41-55).
- seed_ctmath_flashcards.js — 4 колоды формул (тригонометрия/стереометрия/
  логарифмы-степени/производная, 49 карт, flashcard_decks.id=11-14, владелец admin).
- Форматы блоков/карт сверены с рендером (lesson.html $…$/$$; flashcards $…$/\(\)/\[\]).
  Применены seed-скриптами; JSON валиден (0 битых).
- README: статус контента.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 11:48:39 +03:00

177 lines
17 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';
/*
* Уроки остальных блоков курса «ЦЭ/ЦТ — Математика» (по PLAN.md, шаблон пилотов).
* Числа · Преобразования · Уравнения(×2) · Функции · Прогрессии/текстовые ·
* Планиметрия · Продвинутое. Форматы блоков — под рендер lesson.html
* (text/heading/callout esc-only; математика $…$/$$…$$; callout.style). Идемпотентно.
* node backend/scripts/seed_ctmath_lessons_rest.js [--dry]
*/
const db = require('../src/db/db');
const DRY = process.argv.includes('--dry');
const COURSE_TITLE = 'ЦЭ/ЦТ — Математика';
const course = db.prepare("SELECT id FROM courses WHERE subject_slug='math' AND title=?").get(COURSE_TITLE);
if (!course) { console.error('Нет курса. Сначала seed_ctmath_course.js'); process.exit(1); }
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 = (simId, caption) => ['sim', { simId, caption }];
const FC = (front, back) => ['flashcard', { front, back }];
const QZ = (question, options, correctIndex) => ['quiz', { question, options, correctIndex }];
const PR = () => CI('Тренажёр по теме — в модуле /exam-prep/ctmath (реальные задания ЦТ прошлых лет) и в практике по теме.');
const LESSONS = [
{ section: 'Числа и вычисления', title: 'Числа, делимость и проценты', read: 8, blocks: [
H('Числа, делимость и проценты'),
P('Действительные числа на координатной прямой нужно уметь оценивать и сравнивать. Деление с остатком записывается формулой ниже.'),
F('n = d\\cdot q + r,\\qquad 0\\le r<d', 'Деление с остатком'),
P('Проценты: $p\\%$ числа $a$ равно $\\dfrac{p}{100}\\cdot a$. Увеличение на $p\\%$ — умножение на $\\left(1+\\dfrac{p}{100}\\right)$, уменьшение — на $\\left(1-\\dfrac{p}{100}\\right)$.'),
F('\\text{НОД}(a,b)\\cdot\\text{НОК}(a,b)=a\\cdot b', 'Связь НОД и НОК'),
H('Разбор А4', 3),
P('Делитель $15$, неполное частное $k$, остаток $7$. Тогда делимое $n=15k+7$.'),
CS('Ответ: $n=15k+7$.'),
H('Разбор (проценты)', 3),
P('$15\\%$ числа равны $33$. Число $=33:0{,}15=220$, а $20\\%$ от него $=44$.'),
CS('Ответ: $44$.'),
FC('Деление с остатком', '$n=dq+r,\\ 0\\le r<d$'),
FC('$\\text{НОД}\\cdot\\text{НОК}$', '$a\\cdot b$'),
FC('$p\\%$ от $a$', '$\\dfrac{p}{100}\\,a$'),
QZ('20% некоторого числа равны 40. Само число равно:', ['200', '80', '160', '20'], 0),
PR(),
]},
{ section: 'Алгебраические преобразования', title: 'Степени, корни, дроби', read: 9, blocks: [
H('Преобразования выражений: степени, корни, дроби'),
F('a^m\\cdot a^n=a^{m+n},\\quad (a^m)^n=a^{mn},\\quad a^{-n}=\\dfrac{1}{a^n},\\quad a^{m/n}=\\sqrt[n]{a^m}', 'Степени'),
F('\\sqrt[n]{ab}=\\sqrt[n]{a}\\,\\sqrt[n]{b},\\qquad \\sqrt[n]{a^n}=|a|\\ (n\\text{ — чётное})', 'Корни'),
F('(a\\pm b)^2=a^2\\pm2ab+b^2,\\qquad a^2-b^2=(a-b)(a+b)', 'Формулы сокращённого умножения'),
P('ОДЗ выражения: под корнем чётной степени — неотрицательное число; знаменатель не равен нулю; аргумент логарифма положителен.'),
CW('В задании А10 проверяют именно ОДЗ: при каком значении выражение имеет смысл.'),
H('Разбор А10', 3),
P('При $a=-4$ из $\\sqrt{a}$, $\\sqrt[3]{a}$, $\\dfrac{1}{a+4}$ смысл имеет только $\\sqrt[3]{a}$: корень нечётной степени из отрицательного определён; $\\sqrt{-4}$ — нет; $\\dfrac{1}{0}$ — деление на ноль.'),
CS('Ответ: $\\sqrt[3]{a}$.'),
FC('$a^m\\cdot a^n$', '$a^{m+n}$'),
FC('$a^{m/n}$', '$\\sqrt[n]{a^m}$'),
FC('$a^2-b^2$', '$(a-b)(a+b)$'),
QZ('Значение $a^{1/2}$ при $a=9$:', ['3', '4,5', '81', '18'], 0),
PR(),
]},
{ section: 'Уравнения и неравенства', title: 'Квадратные, рациональные, модуль', read: 11, blocks: [
H('Квадратные и рациональные уравнения и неравенства. Модуль'),
F('x_{1,2}=\\dfrac{-b\\pm\\sqrt{D}}{2a},\\ \\ D=b^2-4ac;\\qquad x_1x_2=\\dfrac{c}{a},\\ \\ x_1+x_2=-\\dfrac{b}{a}', 'Квадратное уравнение и теорема Виета'),
SIM('quadratic', 'Корни квадратного уравнения и дискриминант'),
P('Метод интервалов: разложить на множители, отметить нули, расставить знаки по промежуткам. Учитывать кратность корня (при чётной кратности знак не меняется).'),
F('|x|=a\\Rightarrow x=\\pm a\\ (a\\ge0);\\qquad |f(x)|<a\\Leftrightarrow -a<f(x)<a', 'Модуль'),
CI('Двойное неравенство $a\\le f(x)<b$ решают как систему; целые решения отбирают на полученном промежутке.'),
H('Разбор А5', 3),
P('Произведение действительных корней уравнения $x^2-5x+6=0$ по теореме Виета равно $6$ (корни $2$ и $3$).'),
CS('Ответ: $6$.'),
H('Разбор (целые решения)', 3),
P('Сколько целых решений у неравенства $-4<2x-1\\le5$? Имеем $-1{,}5<x\\le3$, то есть $x\\in\\{-1,0,1,2,3\\}$ — пять решений.'),
CS('Ответ: $5$.'),
FC('Дискриминант', '$D=b^2-4ac$'),
FC('Виет: сумма и произведение корней', '$x_1+x_2=-\\dfrac{b}{a},\\ x_1x_2=\\dfrac{c}{a}$'),
FC('$|f(x)|<a$', '$-a<f(x)<a$'),
QZ('Сумма корней уравнения $x^2-7x+12=0$:', ['7', '12', '-7', '3'], 0),
PR(),
]},
{ section: 'Уравнения и неравенства', title: 'Показательные, логарифмические, иррациональные', read: 12, blocks: [
H('Показательные, логарифмические, иррациональные уравнения и неравенства'),
F('a^{f}=a^{g}\\Leftrightarrow f=g;\\qquad \\log_a f=\\log_a g\\Leftrightarrow f=g>0', 'Равносильные переходы'),
F('\\sqrt{f}=g\\ \\Leftrightarrow\\ \\begin{cases}g\\ge0\\\\ f=g^2\\end{cases}', 'Иррациональное уравнение'),
CI('Метод рационализации (для неравенств): знак $\\log_a f-\\log_a g$ совпадает со знаком $(a-1)(f-g)$; знак $a^{f}-a^{g}$ — со знаком $(a-1)(f-g)$. Экономит время на сложных неравенствах.'),
CW('В логарифмических всегда выписывайте ОДЗ: аргумент $>0$, основание $>0$ и $\\ne1$.'),
H('Разбор В11', 3),
P('$\\log_2^2 x-3\\log_2 x+2=0$. Замена $t=\\log_2 x$: $t^2-3t+2=0$, $t=1$ или $t=2$, откуда $x=2$ или $x=4$; их произведение $8$.'),
CS('Ответ: $8$.'),
H('Разбор В14', 3),
P('Наименьшее целое решение неравенства $3^{x}>9$: так как основание $>1$, получаем $x>2$, наименьшее целое $x=3$.'),
CS('Ответ: $3$.'),
FC('$a^{f}=a^{g}$', '$f=g$'),
FC('$\\log_a f=\\log_a g$', '$f=g>0$'),
FC('Знак $\\log_a f-\\log_a g$ (рационализация)', 'как у $(a-1)(f-g)$'),
QZ('$\\log_3 81$ равно:', ['4', '3', '27', '9'], 0),
PR(),
]},
{ section: 'Функции и производная', title: 'Функции, графики, производная', read: 11, blocks: [
H('Функции: свойства, графики, производная'),
P('Ключевые свойства: ОДЗ, чётность (если $f(-x)=f(x)$ — чётная, график симметричен относительно $Oy$; если $f(-x)=-f(x)$ — нечётная), монотонность, нули.'),
SIM('graphtransform', 'Преобразования графиков: сдвиги и растяжения'),
F('f\'>0\\Rightarrow\\text{возрастает};\\quad f\'<0\\Rightarrow\\text{убывает};\\quad f\'=0\\ \\text{со сменой знака}\\Rightarrow\\text{экстремум}', 'Производная и поведение функции'),
H('Разбор В2 (квадратичная)', 3),
P('$f(x)=x^2-6x+5$: нули $1$ и $5$ (их сумма $6$); $f(0)=5$; вершина при $x=3$, наименьшее значение $f(3)=-4$.'),
CS('Сумма нулей $=6$; наименьшее значение $=-4$.'),
H('Разбор В19 (производная)', 3),
P('$f(x)=x^3-3x^2+5$: $f\'(x)=3x^2-6x=3x(x-2)$; функция возрастает на $(-\\infty;0]$ и $[2;+\\infty)$, убывает на $[0;2]$.'),
CS('Промежутки возрастания: $(-\\infty;0]$ и $[2;+\\infty)$.'),
FC('Чётная функция', '$f(-x)=f(x)$, симметрия относительно $Oy$'),
FC('$(x^n)\'$', '$n x^{n-1}$'),
FC('Признак возрастания', '$f\'(x)>0$'),
QZ('Функция $y=x^2$ является:', ['чётной', 'нечётной', 'ни чётной, ни нечётной', 'периодической'], 0),
PR(),
]},
{ section: 'Прогрессии и текстовые задачи', title: 'Прогрессии и текстовые задачи', read: 10, blocks: [
H('Прогрессии и текстовые задачи'),
F('a_n=a_1+(n-1)d,\\qquad S_n=\\dfrac{a_1+a_n}{2}\\,n', 'Арифметическая прогрессия'),
F('b_n=b_1 q^{\\,n-1},\\qquad S_n=\\dfrac{b_1(q^{n}-1)}{q-1}\\ (q\\ne1)', 'Геометрическая прогрессия'),
P('Текстовые задачи: проценты; движение ($s=vt$); работа (производительность $=\\dfrac{1}{t}$); смеси и сплавы (масса вещества $=$ доля $\\times$ масса смеси).'),
H('Разбор В6', 3),
P('$b_3=12$, $b_5=48$ (знаменатель положителен): $q^2=\\dfrac{48}{12}=4$, $q=2$, $b_1=\\dfrac{12}{4}=3$. Сумма первых четырёх членов $3+6+12+24=45$.'),
CS('Ответ: $45$.'),
H('Разбор (сплавы)', 3),
P('Сплав массой $200$ г содержит $30\\%$ меди. Масса меди $=0{,}3\\cdot200=60$ г. На таких долях строятся уравнения смесей.'),
FC('$n$-й член арифм. прогрессии', '$a_n=a_1+(n-1)d$'),
FC('Сумма геом. прогрессии', '$S_n=\\dfrac{b_1(q^n-1)}{q-1}$'),
FC('Путь', '$s=v\\cdot t$'),
QZ('В арифметической прогрессии $a_1=2$, $d=3$. Тогда $a_4$ равно:', ['11', '14', '8', '9'], 0),
PR(),
]},
{ section: 'Планиметрия', title: 'Треугольники, четырёхугольники, окружность', read: 11, blocks: [
H('Планиметрия: треугольники, четырёхугольники, окружность'),
F('S_\\triangle=\\tfrac12 a h_a=\\tfrac12 ab\\sin C;\\qquad \\dfrac{a}{\\sin A}=2R;\\qquad c^2=a^2+b^2-2ab\\cos C', 'Треугольник'),
SIM('triangle', 'Геометрия треугольника'),
P('Прямоугольный треугольник: гипотенуза $=2R$ (радиус описанной окружности). Правильный $n$-угольник связывает сторону, радиус описанной $R$ и вписанной $r$ окружностей.'),
CI('Вписанный угол равен половине центрального, опирающегося на ту же дугу.'),
H('Разбор В5', 3),
P('В прямоугольном треугольнике радиус описанной окружности $R=13$, один катет $10$. Гипотенуза $=2R=26$, второй катет $=\\sqrt{26^2-10^2}=\\sqrt{576}=24$.'),
CS('Ответ: $24$.'),
H('Разбор В10 (правильный шестиугольник)', 3),
P('У правильного шестиугольника со стороной $a$: $R=a$, $r=\\dfrac{\\sqrt3}{2}a$, площадь $S=\\dfrac{3\\sqrt3}{2}a^2$.'),
FC('Площадь треугольника', '$\\tfrac12 ab\\sin C$'),
FC('Теорема синусов', '$\\dfrac{a}{\\sin A}=2R$'),
FC('Вписанный угол', 'половина центрального на ту же дугу'),
QZ('Гипотенуза прямоугольного треугольника, вписанного в окружность радиуса 5, равна:', ['10', '5', '2,5', '25'], 0),
PR(),
]},
{ section: 'Продвинутое и комбинированное', title: 'Параметры и комбинированные задачи', read: 10, blocks: [
H('Задачи с параметрами и комбинированные задачи'),
P('Параметр — буква, от которой зависит ответ. Два подхода: аналитический (исследовать решение по параметру) и графический (семейство графиков и их пересечения).'),
CI('Частый приём: выразить параметр $a=\\varphi(x)$ и смотреть, сколько решений даёт горизонтальная прямая $y=a$ (число пересечений с графиком $\\varphi$).'),
P('Комбинированные задачи смешивают темы (алгебра и геометрия, прогрессии и проценты). Стратегия: разбить на подзадачи, аккуратно следя за ОДЗ и единицами.'),
CI('Продвинутый уровень подробно — в плане курса (Сканави, Высоцкий «Параметры», Прасолов). Здесь — общая стратегия и ориентиры.'),
FC('Графический метод для параметра', '$a=\\varphi(x)$; число решений = число пересечений $y=a$ с графиком'),
FC('Уравнение $x^2=a$: число решений', '$a>0$ — два, $a=0$ — одно, $a<0$ — нет'),
QZ('При каком $a$ уравнение $x^2=a$ имеет ровно одно решение?', ['a=0', 'a>0', 'a<0', 'при любом'], 0),
PR(),
]},
];
console.log(DRY ? '[DRY-RUN]' : '[APPLY]', 'курс id=', course.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 (?,?,?,?)');
const secOrder = {};
for (const L of LESSONS) {
const sec = db.prepare('SELECT id FROM course_sections WHERE course_id=? AND title=?').get(course.id, L.section);
if (!sec) { console.log(` [skip] нет секции «${L.section}»`); continue; }
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}) — пропуск`); continue; }
secOrder[sec.id] = (secOrder[sec.id] || 0) + 1;
if (DRY) { console.log(` + [${L.section}] «${L.title}» (${L.blocks.length} блоков)`); continue; }
const lid = insLesson.run(course.id, L.title, secOrder[sec.id], sec.id, L.read).lastInsertRowid;
L.blocks.forEach(([type, data], bi) => insBlock.run(lid, type, bi, JSON.stringify(data)));
console.log(` + [${L.section}] «${L.title}» (id ${lid}, ${L.blocks.length} блоков)`);
}
console.log(DRY ? 'DRY-RUN: ничего не записано.' : 'Готово. Уроки остальных блоков добавлены (черновик курса).');