feat: migration 038 to fix thesis identifiers mismatched with their year

This commit is contained in:
Pontoporeia
2026-06-09 12:44:13 +02:00
parent 3df1456781
commit 34739d6ae5
4 changed files with 92 additions and 2 deletions

View File

@@ -0,0 +1,85 @@
<?php
/**
* Migration 038 — fix thesis identifiers that don't match their year.
*
* Some entries have identifiers like "2024-026" with year=2025 (or vice-versa),
* typically because the year was corrected after initial creation. When editing
* a thesis and changing its year, the identifier is now auto-regenerated via
* ThesisEditController::save(), but pre-existing wrong identifiers need a
* one-off fix.
*
* Logic: for each thesis whose identifier year-prefix does NOT match its `year`
* column, assign a new identifier <year>-<NNN> using the next available sequence
* number for that year. Entries are ordered by id within each year so the
* numbering is deterministic and roughly chronological.
*
* Safe to re-run: only updates identifiers that are already mismatched.
*/
defined('APP_ROOT') || define('APP_ROOT', dirname(__DIR__, 2));
defined('STORAGE_ROOT') || define('STORAGE_ROOT', APP_ROOT . '/storage');
$dbPath = APP_ROOT . '/storage/xamxam.db';
if (!file_exists($dbPath)) {
echo "ERROR: database not found at $dbPath\n";
exit(1);
}
$pdo = new PDO('sqlite:' . $dbPath);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$pdo->exec('PRAGMA foreign_keys = ON');
// ── 1. Identify mismatched rows ──────────────────────────────────────────
$stmt = $pdo->query(
"SELECT id, year, identifier
FROM theses
WHERE deleted_at IS NULL
AND CAST(SUBSTR(identifier, 1, 4) AS INTEGER) != year"
);
$mismatched = $stmt->fetchAll();
if (empty($mismatched)) {
echo "No mismatched identifiers found. Nothing to fix.\n";
exit(0);
}
echo "Found " . count($mismatched) . " thesis(es) with mismatched identifiers.\n";
// Group by year, ordered by id within each year (deterministic renumbering)
$byYear = [];
foreach ($mismatched as $row) {
$year = (int)$row['year'];
$byYear[$year][] = $row;
}
$fixed = 0;
foreach ($byYear as $year => $rows) {
// Sort by id for deterministic sequencing
usort($rows, fn($a, $b) => (int)$a['id'] <=> (int)$b['id']);
// Find the max existing sequence for CORRECT identifiers of this year
$stmt = $pdo->prepare(
"SELECT COALESCE(MAX(CAST(SUBSTR(identifier, 6) AS INTEGER)), 0)
FROM theses
WHERE year = ?
AND deleted_at IS NULL
AND CAST(SUBSTR(identifier, 1, 4) AS INTEGER) = ?"
);
$stmt->execute([$year, $year]);
$maxSeq = (int)$stmt->fetchColumn();
foreach ($rows as $row) {
$maxSeq++;
$newIdentifier = sprintf('%d-%03d', $year, $maxSeq);
echo " Thesis {$row['id']}: {$row['identifier']}{$newIdentifier} (year={$year})\n";
$pdo->prepare("UPDATE theses SET identifier = ? WHERE id = ?")
->execute([$newIdentifier, $row['id']]);
$fixed++;
}
}
echo "\nFixed $fixed identifier(s).\n";
echo "Migration 038 complete.\n";