From 70ec09382e5bde904f7b1d266754cacc997304e5 Mon Sep 17 00:00:00 2001 From: Maxim Dolgolyov Date: Fri, 19 Jun 2026 09:27:25 +0300 Subject: [PATCH] =?UTF-8?q?feat(ctmath):=20seed-=D1=81=D0=BA=D1=80=D0=B8?= =?UTF-8?q?=D0=BF=D1=82=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=B4=20=D1=84=D0=BB?= =?UTF-8?q?=D0=B5=D1=88=D0=BA=D0=B0=D1=80=D1=82=20=C2=AB=D0=9A=D0=B2=D0=B0?= =?UTF-8?q?=D0=B4=D1=80=D0=B0=D1=82=D0=BD=D1=8B=D0=B5=20=D1=83=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=C2=BB=20=D0=B8=20=C2=AB?= =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D1=83=D0=BB=D1=8C=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Квадратные уравнения (12 карт) — дискриминант, формула корней, теорема Виета (вкл. приведённое), неполные уравнения, разложение на множители, знаки корней. Модуль (12 карт) — определение, геометрический смысл, уравнения |x|=a / |f|=|g| / |f|=g, неравенства |x|a, свойства, раскрытие по промежуткам. Канонический материал ЦТ. KaTeX inline $…$ (кириллица только вне math), идемпотентно, запись с --apply. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/scripts/seed_ctmath_flashcards_p5.js | 114 +++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 backend/scripts/seed_ctmath_flashcards_p5.js diff --git a/backend/scripts/seed_ctmath_flashcards_p5.js b/backend/scripts/seed_ctmath_flashcards_p5.js new file mode 100644 index 0000000..a122e52 --- /dev/null +++ b/backend/scripts/seed_ctmath_flashcards_p5.js @@ -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|0$)', 'Равносильно $-aa$ (при $a>0$)', 'Равносильно $x<-a$ или $x>a$'], + ['Неположительная правая часть', '$|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');