feat(ctmath): seed-скрипт колод флешкарт «Квадратные уравнения» и «Модуль»
Квадратные уравнения (12 карт) — дискриминант, формула корней, теорема Виета (вкл. приведённое), неполные уравнения, разложение на множители, знаки корней. Модуль (12 карт) — определение, геометрический смысл, уравнения |x|=a / |f|=|g| / |f|=g, неравенства |x|<a / |x|>a, свойства, раскрытие по промежуткам. Канонический материал ЦТ. KaTeX inline $…$ (кириллица только вне math), идемпотентно, запись с --apply. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,114 @@
|
|||||||
|
'use strict';
|
||||||
|
/* ───────────────────────────────────────────────────────────────────────────
|
||||||
|
seed_ctmath_flashcards_p5.js
|
||||||
|
Ещё две колоды карточек для подготовки к ЦЭ/ЦТ (интервальное повторение):
|
||||||
|
|
||||||
|
1. «ЦТ · Квадратные уравнения» — дискриминант, формула корней, Виета, неполные
|
||||||
|
2. «ЦТ · Модуль» — определение, уравнения/неравенства с модулем
|
||||||
|
|
||||||
|
Источник: канонический набор ЦТ (фундаментальные высокочастотные темы; отдельных
|
||||||
|
шпор Кедр нет, материал стандартный).
|
||||||
|
|
||||||
|
Математика — KaTeX inline $…$ (страница флешкарт рендерит \( \), \[ \], $ $; НЕ $$).
|
||||||
|
⚠️ Кириллица ТОЛЬКО вне $…$ — в math-режиме KaTeX нет кириллических глифов.
|
||||||
|
Те же владелец/таблицы/стиль, что seed_ctmath_flashcards.js / _p2 / _p3 / _p4.
|
||||||
|
|
||||||
|
Идемпотентность: колода ищется по (user_id, title). Если уже есть и наполнена —
|
||||||
|
пропуск (не клобберит SR-прогресс). Пустую/новую — наполняет.
|
||||||
|
|
||||||
|
Запуск:
|
||||||
|
node backend/scripts/seed_ctmath_flashcards_p5.js # DRY-RUN (по умолчанию)
|
||||||
|
node backend/scripts/seed_ctmath_flashcards_p5.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: '#8338EC',
|
||||||
|
descr: 'Дискриминант, формула корней, теорема Виета, неполные уравнения, разложение на множители.',
|
||||||
|
cards: [
|
||||||
|
['Квадратное уравнение — общий вид', '$ax^2+bx+c=0$, где $a\\neq0$'],
|
||||||
|
['Дискриминант', '$D=b^2-4ac$'],
|
||||||
|
['Число корней по дискриминанту', '$D>0$ — два корня; $D=0$ — один (кратный); $D<0$ — действительных корней нет'],
|
||||||
|
['Формула корней', '$x=\\dfrac{-b\\pm\\sqrt{D}}{2a}$'],
|
||||||
|
['Теорема Виета', '$x_1+x_2=-\\dfrac{b}{a}$, $x_1 x_2=\\dfrac{c}{a}$'],
|
||||||
|
['Виета для приведённого $x^2+px+q=0$', '$x_1+x_2=-p$, $x_1 x_2=q$'],
|
||||||
|
['Разложение трёхчлена на множители', '$ax^2+bx+c=a(x-x_1)(x-x_2)$, где $x_1,x_2$ — корни'],
|
||||||
|
['Неполное уравнение $ax^2+c=0$', '$x^2=-\\dfrac{c}{a}$; корни $x=\\pm\\sqrt{-\\dfrac{c}{a}}$ при $-\\dfrac{c}{a}\\ge0$, иначе их нет'],
|
||||||
|
['Неполное уравнение $ax^2+bx=0$', 'Вынести $x$: $x(ax+b)=0$, откуда $x=0$ или $x=-\\dfrac{b}{a}$'],
|
||||||
|
['Сокращённая формула при чётном $b=2k$', '$x=\\dfrac{-k\\pm\\sqrt{k^2-ac}}{a}$'],
|
||||||
|
['Сумма квадратов корней через Виета', '$x_1^2+x_2^2=(x_1+x_2)^2-2x_1 x_2$'],
|
||||||
|
['Знаки корней по Виета', 'Оба положительны при $x_1 x_2>0$ и $x_1+x_2>0$; корни разных знаков при $x_1 x_2<0$'],
|
||||||
|
]},
|
||||||
|
|
||||||
|
{ title: 'ЦТ · Модуль', color: '#E63946',
|
||||||
|
descr: 'Определение и свойства модуля, уравнения и неравенства с модулем, раскрытие по промежуткам.',
|
||||||
|
cards: [
|
||||||
|
['Определение модуля', '$|x|=x$ при $x\\ge0$ и $|x|=-x$ при $x<0$'],
|
||||||
|
['Геометрический смысл модуля', '$|x|$ — расстояние от точки $x$ до нуля; $|x-a|$ — расстояние между $x$ и $a$'],
|
||||||
|
['Уравнение $|x|=a$', 'При $a>0$: $x=\\pm a$; при $a=0$: $x=0$; при $a<0$ решений нет'],
|
||||||
|
['Уравнение $|f|=|g|$', 'Равносильно совокупности $f=g$ или $f=-g$'],
|
||||||
|
['Уравнение $|f|=g$', 'Нужно $g\\ge0$, и тогда $f=g$ или $f=-g$ (правую часть проверяем на знак)'],
|
||||||
|
['Неравенство $|x|<a$ (при $a>0$)', 'Равносильно $-a<x<a$'],
|
||||||
|
['Неравенство $|x|>a$ (при $a>0$)', 'Равносильно $x<-a$ или $x>a$'],
|
||||||
|
['Неположительная правая часть', '$|x|<a$ при $a\\le0$ — решений нет; $|x|>a$ при $a<0$ — любое $x$'],
|
||||||
|
['Свойства модуля произведения и дроби', '$|ab|=|a|\\cdot|b|$; $\\left|\\dfrac{a}{b}\\right|=\\dfrac{|a|}{|b|}$'],
|
||||||
|
['Корень из квадрата', '$\\sqrt{x^2}=|x|$ — это модуль, а не просто $x$'],
|
||||||
|
['Базовые свойства модуля', '$|a|\\ge0$ всегда; $|a|=|-a|$; $|a|\\ge 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_p5 (${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_p5.js --apply\n');
|
||||||
|
else console.log('Готово. Колоды добавлены (владелец — admin; раздать классу — через доступ к колоде).\n');
|
||||||
Reference in New Issue
Block a user