'use strict'; /* ─────────────────────────────────────────────────────────────────────────── seed_ctmath_flashcards_p3.js Ещё две колоды карточек для подготовки к ЦЭ/ЦТ (интервальное повторение): 1. «ЦТ · Прогрессии — формулы» — арифметическая + геометрическая 2. «ЦТ · Двойные неравенства» — оценка a±b, a·b, a/b почленно (+ловушки) Источники: • Прогрессии — канонический набор формул ЦТ (отдельной шпоры Кедр нет). • Двойные неравенства — Кедр «Операции_с_двойными_неравенствами.pdf» (сложение/умножение/вычитание/деление двойных неравенств; ловушки строгости и запрет почленного вычитания/деления). Математика — KaTeX inline $…$ (страница флешкарт рендерит \( \), \[ \], $ $; НЕ $$). Те же владелец/таблицы/стиль, что seed_ctmath_flashcards.js / _p2.js. Идемпотентность: колода ищется по (user_id, title). Если уже есть и наполнена — пропуск (не клобберит SR-прогресс). Пустую/новую — наполняет. Запуск: node backend/scripts/seed_ctmath_flashcards_p3.js # DRY-RUN (по умолчанию) node backend/scripts/seed_ctmath_flashcards_p3.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: '#06D6A0', descr: 'Арифметическая и геометрическая прогрессии: n-й член, суммы, характеристические свойства.', cards: [ // ── Арифметическая ─────────────────────────────────────────────────── ['Арифметическая прогрессия — $n$-й член', '$a_n=a_1+(n-1)d$'], ['Разность арифметической прогрессии', '$d=a_{n+1}-a_n$ (постоянная)'], ['Характеристическое свойство арифм. прогрессии', '$a_n=\\dfrac{a_{n-1}+a_{n+1}}{2}$ — каждый член есть среднее арифметическое соседних'], ['Сумма $n$ членов арифм. прогрессии', '$S_n=\\dfrac{a_1+a_n}{2}\\cdot n$'], ['Сумма арифм. прогрессии через $d$', '$S_n=\\dfrac{2a_1+(n-1)d}{2}\\cdot n$'], ['Связь двух членов арифм. прогрессии', '$a_n=a_k+(n-k)d$'], // ── Геометрическая ─────────────────────────────────────────────────── ['Геометрическая прогрессия — $n$-й член', '$b_n=b_1\\,q^{\\,n-1}$'], ['Знаменатель геометрической прогрессии', '$q=\\dfrac{b_{n+1}}{b_n}$ (постоянный, $q\\neq0$)'], ['Характеристическое свойство геом. прогрессии', '$b_n^2=b_{n-1}\\cdot b_{n+1}$'], ['Сумма $n$ членов геом. прогрессии ($q\\neq1$)', '$S_n=\\dfrac{b_1(q^{\\,n}-1)}{q-1}$'], ['Сумма бесконечной убывающей геом. прогрессии ($|q|<1$)', '$S=\\dfrac{b_1}{1-q}$'], ['Связь двух членов геом. прогрессии', '$b_n=b_k\\cdot q^{\\,n-k}$'], ]}, { title: 'ЦТ · Двойные неравенства', color: '#EF476F', descr: 'Оценка суммы, разности, произведения и частного через двойные неравенства. Ловушки вычитания и деления.', cards: [ ['Сложение неравенств', 'Складываем почленно ТОЛЬКО одинаково направленные. $5\\le a\\le6$, $7\\le b\\le9$ $\\Rightarrow$ $12\\le a+b\\le15$'], ['Умножение неравенств', 'Перемножаем почленно (все части положительны). $5\\le a\\le6$, $7\\le b\\le9$ $\\Rightarrow$ $35\\le ab\\le54$'], ['Ловушка строгости', 'Если хоть одно исходное неравенство строгое, результат строгий ($<$). Напр. $5\\le a<6$, $70$ берём обратные с переворотом: $7 { [['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_p3 (${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_p3.js --apply\n'); else console.log('Готово. Колоды добавлены (владелец — admin; раздать классу — через доступ к колоде).\n');