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
@@ -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();