Files
Learn_System/backend/scripts/seed_ctmath_flashcards_p2.js
T
Maxim Dolgolyov b36f708b82 feat(ctmath): seed-скрипт ещё двух колод флешкарт (Планиметрия, Свойства функций)
Источники — бесплатные материалы Кедр: «Свойства четырёхугольников»,
«Уравнение окружности», «Шпора по свойствам функций» + базовый набор
формул треугольника. 50 карт (31 + 19), KaTeX inline $…$. Идемпотентно,
DRY-RUN по умолчанию, запись только с --apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:57:52 +03:00

148 lines
14 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';
/* ───────────────────────────────────────────────────────────────────────────
seed_ctmath_flashcards_p2.js
Ещё две колоды карточек для подготовки к ЦЭ/ЦТ (интервальное повторение):
1. «ЦТ · Планиметрия — формулы» — треугольники, четырёхугольники, окружность
2. «ЦТ · Свойства функций» — чтение графика: D(y), E(y), нули,
монотонность, экстремумы, f'(x), чётность, сдвиги
Источники (бесплатные материалы Кедр от Егора):
• Свойства четырехугольников.pdf (параллелограмм/прямоугольник/ромб/квадрат)
• Уравнение окружности _ Материал.pdf (уравнение, радиус, расстояние, прямая)
• Шпора_по_свойствам_функций_ct_matem.pdf (графический разбор свойств)
Формулы треугольника и площадей — базовый набор ЦТ (в шпорах Кедр их нет).
Математика — KaTeX inline $…$ (страница флешкарт рендерит \( \), \[ \], $ $; НЕ $$).
Те же владелец/таблицы/стиль, что seed_ctmath_flashcards.js (Тригонометрия и т.д.).
Идемпотентность: колода ищется по (user_id, title). Если уже есть и наполнена —
пропуск (не клобберит SR-прогресс). Пустую/новую — наполняет.
Запуск:
node backend/scripts/seed_ctmath_flashcards_p2.js # DRY-RUN (по умолчанию)
node backend/scripts/seed_ctmath_flashcards_p2.js --apply # запись в БД
⚠️ Массовую запись в БД запускает ПОЛЬЗОВАТЕЛЬ вручную (авто-режим Claude Code
блокирует продакшн-записи). Без --apply ничего не пишется — только сводка.
─────────────────────────────────────────────────────────────────────────── */
const db = require('../src/db/db');
const APPLY = process.argv.includes('--apply');
const owner = (db.prepare("SELECT id FROM users WHERE role='admin' ORDER BY id LIMIT 1").get()
|| db.prepare('SELECT id FROM users ORDER BY id LIMIT 1').get()).id;
const DECKS = [
{ title: 'ЦТ · Планиметрия — формулы', color: '#FB5607',
descr: 'Треугольники, четырёхугольники, окружность. Формулы для планиметрических задач ЦЭ/ЦТ.',
cards: [
// ── Треугольник ──────────────────────────────────────────────────────
['Сумма углов треугольника', '$\\angle A+\\angle B+\\angle C=180^\\circ$'],
['Площадь треугольника через основание и высоту', '$S=\\dfrac12\\,a\\,h_a$'],
['Площадь треугольника через две стороны и угол', '$S=\\dfrac12\\,ab\\sin C$'],
['Теорема Пифагора (прямоугольный $\\triangle$)', '$c^2=a^2+b^2$, где $c$ — гипотенуза'],
['Теорема косинусов', '$c^2=a^2+b^2-2ab\\cos C$'],
['Теорема синусов', '$\\dfrac{a}{\\sin A}=\\dfrac{b}{\\sin B}=\\dfrac{c}{\\sin C}=2R$'],
['Формула Герона', '$S=\\sqrt{p(p-a)(p-b)(p-c)}$, где $p=\\dfrac{a+b+c}{2}$'],
['Радиус вписанной окружности ($\\triangle$)', '$r=\\dfrac{S}{p}$ ($p$ — полупериметр)'],
['Радиус описанной окружности ($\\triangle$)', '$R=\\dfrac{abc}{4S}$'],
['Площадь равностороннего треугольника', '$S=\\dfrac{a^2\\sqrt3}{4}$'],
['Средняя линия треугольника', 'Параллельна стороне и равна её половине: $m=\\dfrac{a}{2}$'],
['Медиана к гипотенузе (прямоугольный $\\triangle$)', 'Равна половине гипотенузы: $m_c=\\dfrac{c}{2}$'],
// ── Четырёхугольники ─────────────────────────────────────────────────
['Параллелограмм — определение', 'Четырёхугольник, у которого противоположные стороны попарно параллельны'],
['Свойства параллелограмма', 'Противолежащие стороны и углы равны; сумма углов при одной стороне $180^\\circ$; диагонали точкой пересечения делятся пополам'],
['Сумма квадратов диагоналей параллелограмма', '$d_1^2+d_2^2=2(a^2+b^2)$'],
['Площадь параллелограмма', '$S=a\\,h_a=ab\\sin\\alpha$'],
['Прямоугольник — отличие и диагонали', 'Параллелограмм со всеми прямыми углами; диагонали равны: $AC=BD$'],
['Площадь прямоугольника', '$S=ab$'],
['Ромб — отличие и диагонали', 'Параллелограмм со всеми равными сторонами; диагонали $\\perp$ и являются биссектрисами углов'],
['Площадь ромба', '$S=\\dfrac12\\,d_1 d_2=a^2\\sin\\alpha$'],
['Квадрат — диагональ и площадь', '$d=a\\sqrt2$; $S=a^2=\\dfrac12\\,d^2$'],
['Средняя линия трапеции', '$m=\\dfrac{a+b}{2}$ — полусумма оснований'],
['Площадь трапеции', '$S=\\dfrac{a+b}{2}\\cdot h$'],
// ── Окружность ───────────────────────────────────────────────────────
['Длина окружности', '$C=2\\pi R=\\pi D$'],
['Площадь круга', '$S=\\pi R^2$'],
['Длина дуги в $n^\\circ$', '$l=\\dfrac{\\pi R n}{180}$'],
['Площадь сектора в $n^\\circ$', '$S=\\dfrac{\\pi R^2 n}{360}$'],
['Вписанный угол', 'Равен половине центрального, опирающегося на ту же дугу'],
['Угол, опирающийся на диаметр', 'Прямой: $90^\\circ$'],
['Уравнение окружности', '$(x-x_0)^2+(y-y_0)^2=R^2$ — центр $(x_0;y_0)$, радиус $R$'],
['Расстояние между точками', '$d=\\sqrt{(x_2-x_1)^2+(y_2-y_1)^2}$'],
]},
{ title: 'ЦТ · Свойства функций', color: '#3A86FF',
descr: 'Чтение графика функции: область определения и значений, нули, монотонность, экстремумы, производная, чётность, сдвиги.',
cards: [
["Область определения $D(y)$", "Множество всех значений $x$, при которых функция существует (проекция графика на ось $Ox$)"],
["Множество значений $E(y)$", "Множество всех значений $y$, которые принимает функция (проекция графика на ось $Oy$)"],
["Нули функции", "Значения $x$, при которых $f(x)=0$ — точки пересечения графика с осью $Ox$"],
["Пересечение графика с осью $Oy$", "Точка $(0;\\,f(0))$ — подставляем $x=0$"],
["Наибольшее и наименьшее значения функции", "Ордината $y$ самой высокой и самой низкой точек графика"],
["Функция возрастает на промежутке", "Большему $x$ соответствует большее $y$ (график идёт вверх); признак: $f'(x)>0$"],
["Функция убывает на промежутке", "Большему $x$ соответствует меньшее $y$ (график идёт вниз); признак: $f'(x)<0$"],
["Промежутки знакопостоянства", "$y>0$ — где график выше оси $Ox$; $y<0$ — где ниже оси $Ox$"],
["Точка максимума $x_{\\max}$", "Точка, в которой функция меняет возрастание на убывание ($f'$ меняет знак с $+$ на $-$)"],
["Точка минимума $x_{\\min}$", "Точка, в которой функция меняет убывание на возрастание ($f'$ меняет знак с $-$ на $+$)"],
["Экстремум функции", "Значение $y=f(x)$ в точке максимума или минимума (сама точка $x$ — точка экстремума)"],
["Знак $f'(x)$ и монотонность", "$f'(x)>0$ → функция возрастает; $f'(x)<0$ → функция убывает"],
["Условие $f'(x)=0$", "Точка, подозрительная на экстремум: экстремум есть, если $f'$ при переходе меняет знак"],
["Чётная функция", "$f(-x)=f(x)$; график симметричен относительно оси $Oy$"],
["Нечётная функция", "$f(-x)=-f(x)$; график симметричен относительно начала координат"],
["Сдвиг $y=f(x)+a$ (при $a>0$)", "График сдвигается вверх на $a$"],
["Сдвиг $y=f(x)-a$ (при $a>0$)", "График сдвигается вниз на $a$"],
["Сдвиг $y=f(x+a)$ (при $a>0$)", "График сдвигается влево на $a$"],
["Сдвиг $y=f(x-a)$ (при $a>0$)", "График сдвигается вправо на $a$"],
]},
];
/* ── self-check: чаще всего KaTeX ломают непарные $ или {} ─────────────────── */
let bad = 0;
for (const d of DECKS) {
d.cards.forEach(([f, b], i) => {
[['front', f], ['back', b]].forEach(([side, s]) => {
const dollars = (s.match(/\$/g) || []).length;
const braces = (s.match(/\{/g) || []).length - (s.match(/\}/g) || []).length;
if (dollars % 2 !== 0) { console.error(`✗ непарный $ — «${d.title}» #${i + 1} (${side}): ${s}`); bad++; }
if (braces !== 0) { console.error(`✗ непарные {} — «${d.title}» #${i + 1} (${side}): ${s}`); bad++; }
});
});
}
if (bad) { console.error(`\nСамопроверка: ${bad} проблем — исправь до записи.\n`); process.exit(1); }
const findDeck = db.prepare('SELECT id FROM flashcard_decks WHERE user_id=? AND title=?');
const countCard = db.prepare('SELECT COUNT(*) c FROM flashcard_cards WHERE deck_id=?');
const insDeck = db.prepare('INSERT INTO flashcard_decks (user_id,title,description,color) VALUES (?,?,?,?)');
const insCard = db.prepare('INSERT INTO flashcard_cards (deck_id,front,back,order_idx) VALUES (?,?,?,?)');
console.log(`\n=== seed_ctmath_flashcards_p2 (${APPLY ? 'APPLY' : 'DRY-RUN'}) ===`);
console.log('владелец user_id =', owner, '\n');
let plannedDecks = 0, plannedCards = 0;
for (const d of DECKS) {
const ex = findDeck.get(owner, d.title);
if (ex) {
const have = countCard.get(ex.id).c;
if (have > 0) { console.log(`• «${d.title}» (id ${ex.id}) — уже наполнена (${have} карт), пропуск`); continue; }
// колода есть, но пустая → дольём карты
plannedDecks++; plannedCards += d.cards.length;
console.log(`+ «${d.title}» (id ${ex.id}) — пустая, долить ${d.cards.length} карт`);
if (APPLY) d.cards.forEach(([f, b], i) => insCard.run(ex.id, f, b, i));
continue;
}
plannedDecks++; plannedCards += d.cards.length;
console.log(`+ «${d.title}» — новая колода, ${d.cards.length} карт`);
if (APPLY) {
const did = insDeck.run(owner, d.title, d.descr, d.color).lastInsertRowid;
d.cards.forEach(([f, b], i) => insCard.run(did, f, b, i));
console.log(` создана id ${did}`);
}
}
console.log(`\nИтого к ${APPLY ? 'записи' : 'добавлению'}: колод ${plannedDecks}, карт ${plannedCards}.`);
if (!APPLY) console.log('DRY-RUN: ничего не записано. Для записи: node backend/scripts/seed_ctmath_flashcards_p2.js --apply\n');
else console.log('Готово. Колоды добавлены (владелец — admin; раздать классу — через доступ к колоде).\n');