chore(textbooks): убрать сторонних авторов — все учебники наши (author=LearnSpace)

Политика «все учебники наши»: нигде не упоминаются сторонние авторы.
- Миграции (15 файлов): колонка author → 'LearnSpace'; из описаний убран оборот
  «по учебнику <автор>:»; авторские фамилии вычищены из комментариев. Покрыты
  Арефьева/Пирютко, Казаков, Латотин/Чеботаревский/Горбунова/Цыбулько, Исаченкова,
  Жилко/Маркович/Сокольский, Герасимов/Лобанов.
- HTML: physics_9_ch5 («по канве учебника Исаченковой» → «по учебной программе»),
  physics_11_hub (hdr-sub с авторами → описание курса), mocks-redesign (карточки-авторы → LearnSpace).
- Генераторы gen_phys9_ch.js/gen_phys11_stubs.js — шаблоны без авторов.
- НОВОЕ: update_textbook_authors.js — идемпотентный апдейтер ЖИВОЙ БД (миграции уже
  применены): author→'LearnSpace' у всех 107 учебников + чистка описаний. DRY-RUN по умолч.

⚠️ Живую БД правит ПОЛЬЗОВАТЕЛЬ: node backend/scripts/update_textbook_authors.js --apply
(в БД сейчас author пуст у всех, видимые упоминания были в описаниях «по учебнику …»).
review_geom10/11.js не тронуты — там фамилии как поисковые шаблоны детектора, не атрибуция.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maxim Dolgolyov
2026-06-20 11:52:06 +03:00
parent fec638135f
commit c0af5502bf
21 changed files with 122 additions and 52 deletions
+1 -1
View File
@@ -362,7 +362,7 @@ main{max-width:1180px;margin:0 auto;padding:32px 24px 60px}
</div>
<div>
<h1>Физика — 11 класс</h1>
<div class="hdr-sub">Жилко · Маркович · Сокольский (2021) · 8 глав · 45 параграфов</div>
<div class="hdr-sub">Полный курс физики 11 класса · 8 глав · 45 параграфов</div>
</div>
</div>
</header>
+2 -2
View File
@@ -366,14 +366,14 @@ function buildCh(chKey) {
const bodyHtml = isLR
? `<p><b>${name}</b> — лабораторная работа в разработке (Phase 5+).</p>
<p>Здесь появятся: <b>Цель · Оборудование · Проверьте себя · Вывод расчётных формул · Ход работы · Таблица измерений · Контрольные вопросы · Суперзадание</b> — по канве учебника Исаченковой 2019.</p>
<p>Здесь появятся: <b>Цель · Оборудование · Проверьте себя · Вывод расчётных формул · Ход работы · Таблица измерений · Контрольные вопросы · Суперзадание</b> — по учебной программе.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
<b>Phase 0:</b> создан скелет. <b>Phase 5:</b> наполнение ЛР пошаговой работой с интерактивной таблицей измерений.
</p>`
: `<p><b>${name}</b> — этот параграф в разработке (Phase ${C.chNum}+).</p>
<p>Здесь появятся: теория, формулы, разобранные примеры и 3–4 интерактива в стиле «физики 10» — векторные диаграммы, графики движения, ползунки и автопроверяемые тренажёры.</p>
<p style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem">
<b>Phase 0:</b> создан скелет. <b>Phase 5:</b> наполнение по учебнику «Физика 9» (Исаченкова, Сокольский, Захаревич, 2019).
<b>Phase 0:</b> создан скелет. <b>Phase 5:</b> наполнение по учебной программе «Физика 9» (2019).
</p>`;
return `function build_${pid}(){
@@ -0,0 +1,70 @@
'use strict';
/* ───────────────────────────────────────────────────────────────────────────
update_textbook_authors.js
Приводит метаданные учебников к политике «все учебники наши»:
• колонка textbooks.author → 'LearnSpace' (у всех учебников и их глав);
• в textbooks.description убирается оборот «по учебнику <автор>:» → «:».
Миграции 004/008/017027/031/038/049/050 уже применены к БД с фамилиями сторонних
авторов — их исходники почищены, но ЖИВУЮ БД правит этот идемпотентный скрипт.
Запуск:
node backend/scripts/update_textbook_authors.js # DRY-RUN (по умолчанию)
node backend/scripts/update_textbook_authors.js --apply # запись в БД
⚠️ Массовую запись в БД запускает ПОЛЬЗОВАТЕЛЬ вручную. Без --apply ничего не пишется.
─────────────────────────────────────────────────────────────────────────── */
const { DatabaseSync } = require('node:sqlite');
const path = require('path');
const APPLY = process.argv.includes('--apply');
const AUTHOR = 'LearnSpace';
const DB = path.join(__dirname, '..', 'data', 'learnspace.db');
const stripDesc = d => (d ? d.replace(/ по учебнику [^:]*:/g, ':') : d);
const db = new DatabaseSync(DB);
// есть ли таблица/колонки
const cols = db.prepare(`PRAGMA table_info(textbooks)`).all().map(c => c.name);
if (!cols.includes('author')) { console.error('✗ В таблице textbooks нет колонки author. Прерывание.'); db.close(); process.exit(1); }
const rows = db.prepare(`SELECT id, slug, author, description FROM textbooks`).all();
const changes = [];
for (const r of rows) {
const newDesc = stripDesc(r.description);
const authorChange = (r.author || '') !== AUTHOR;
const descChange = newDesc !== r.description;
if (authorChange || descChange) changes.push({ id: r.id, slug: r.slug, oldAuthor: r.author, authorChange, descChange, newDesc });
}
console.log(`\n=== update_textbook_authors (учебников: ${rows.length}) ===`);
console.log(`Режим: ${APPLY ? 'APPLY (запись)' : 'DRY-RUN (только показ)'}\n`);
console.log(`Под изменение: ${changes.length}`);
for (const c of changes) {
const tags = [c.authorChange ? `author: ${c.oldAuthor || '∅'}${AUTHOR}` : null, c.descChange ? 'описание: убран «по учебнику …»' : null].filter(Boolean).join('; ');
console.log(` ${String(c.slug).padEnd(20)} ${tags}`);
}
if (!changes.length) { console.log('\nНечего менять — БД уже чистая.'); db.close(); process.exit(0); }
if (!APPLY) {
console.log('\nDRY-RUN: ничего не записано. Для записи: node backend/scripts/update_textbook_authors.js --apply\n');
db.close();
process.exit(0);
}
const upd = db.prepare(`UPDATE textbooks SET author = ?, description = ? WHERE id = ?`);
let n = 0;
db.exec('BEGIN');
try {
for (const c of changes) { upd.run(AUTHOR, c.newDesc, c.id); n++; }
db.exec('COMMIT');
console.log(`\n✓ Обновлено строк: ${n}. Все учебники → author='${AUTHOR}', обороты «по учебнику …» убраны.\n`);
} catch (e) {
db.exec('ROLLBACK');
console.error('\n✗ Ошибка записи, откат:', e.message);
process.exitCode = 1;
}
db.close();