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

@@ -7,5 +7,7 @@
- [x] Fix #5: "Contact public : non" partout, non modifiable, sans impact
- [x] Fix #6: Investiguer "libre → interne" impossible — aucune restriction trouvée dans le code admin
- [x] Hotfix: contact_visible manquant dans le SQL de updateThesis (l'edit matchait createThesis à la place)
- [x] Fix #7: Options de licence non persistées en edit — HTMX load trigger perdait les valeurs (pas de hidden inputs pour license_id/license_custom/cc2r/want_license dans fieldset-licence-explanation.php)
- [x] Commit + jj new
- [x] Fix #7: Options de licence non persistées en edit — HTMX load trigger perdait les valeurs
- [x] Fix #3 (v2): findOrCreateAuthor avec cascade ID → nom → email, setThesisAuthors passe les IDs existants
- [x] Migration 038: corriger les identifiers theses qui ne matchent pas leur année
- [ ] Commit + jj new

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";

View File

@@ -5,3 +5,4 @@
{"timestamp":"2026-06-09T10:34:25+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}}
{"timestamp":"2026-06-09T10:34:45+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}}
{"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}}
{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","resource":"thesis","action":"edit","status":"success","context":{"thesis_id":26,"title":"DepNum"}}

View File

@@ -14,3 +14,5 @@
{"timestamp":"2026-06-09T10:34:45+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":6,"old_data":{"id":6,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:25","sort_order":1,"display_label":null,"file_hash":null}}
{"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 10:34:45","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":null}}
{"timestamp":"2026-06-09T10:34:52+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":7,"old_data":{"id":7,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:45","sort_order":1,"display_label":null,"file_hash":null}}
{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"UPDATE","table":"theses","record_id":26,"old_data":{"id":26,"identifier":"2024-026","title":"DepNum","subtitle":null,"year":2024,"is_doctoral":0,"objet":"tfe","orientation_id":1,"ap_program_id":3,"finality_id":3,"synopsis":"Mon mémoire de Master à l'ERG est un blog autobiographique sur ma dépendance numérique. Chaque post mêle vécu personnel, questions et recherche. J'explore les dynamiques complexes de notre dépendance collective aux technologies numériques, en croisant expérience individuelle et réflexion systémique. JLKJLKJLKJ","context_note":"blposqujdfmlkqshjd mfglkqjhzmdslkf qsdmlkfj mlqskjdf mqskdjf mlqksdjf mlqksdjf mlqksjd fmlkjqsd","remarks":null,"access_type_id":2,"license_id":3,"jury_points":17.5,"jury_note_added":0,"submitted_at":"2026-06-08 08:33:14","defense_date":null,"published_at":null,"is_published":1,"baiu_link":"https://ils.bib.uclouvain.be/global/documents/3830452","created_at":"2026-06-08 08:33:14","updated_at":"2026-06-09 10:34:52","exemplaire_baiu":0,"exemplaire_erg":0,"cc2r":1,"license_custom":null,"deleted_at":null,"contact_visible":null}}
{"timestamp":"2026-06-09T10:42:57+00:00","ip":"127.0.0.1","user_agent":"Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0","actor":"127.0.0.1","action":"DELETE","table":"thesis_files","record_id":8,"old_data":{"id":8,"thesis_id":26,"file_type":"website","file_path":"https://depnum.happyngreen.fr/","file_name":"depnum.happyngreen.fr","file_size":0,"mime_type":"text/html","description":null,"uploaded_at":"2026-06-09 10:34:52","sort_order":1,"display_label":null,"file_hash":null}}