'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\\lt 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 r0$, основание $>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\'\\gt 0\\Rightarrow\\text{возрастает};\\quad f\'\\lt 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: ничего не записано.' : 'Готово. Уроки остальных блоков добавлены (черновик курса).');