mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-08-10 15:21:22 +02:00
fix: unify CSV import/export columns, fix import JS error, fix createThesis VALUES
- Fix createThesis SQL: extra ? placeholder in VALUES (27 values for 26 columns) - Add createThesis integration tests to catch column/value mismatches - Add CSV_COLUMNS as single source of truth in ExportController, deriving csvHeaders() and positional fallback from it - Add missing columns to export query + CSV: duration_pages, duration_minutes, has_annexes, license_custom, contact_visible, objet - Add missing columns to import INSERT: license_id, license_custom, cc2r, exemplaire_baiu, exemplaire_erg, objet, contact_visible, duration_pages, duration_minutes, has_annexes - Resolve license name → license_id during import - Fix XamxamInitFilePonds: add file-upload-filepond.js to admin-entry.js so FilePond initialization code is available on the admin list page - Add FilePond vendor CSS to admin.min.css bundle (import dialog styling) - Generate .admin-file-hint from csvHeaders() instead of hardcoded stale list - Use positional fallback from CSV_COLUMNS for import cell parsing
This commit is contained in:
+102
-35
@@ -37,14 +37,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
// Build colIdx[name] → position map; fall back to positional if header not found.
|
||||
// Matching uses prefix + variant logic so "contact.visible" matches "contact",
|
||||
// "promoteur·ice(s)" matches "promoteur", "Licence" matches "license", etc.
|
||||
require_once APP_ROOT . '/src/Controllers/ExportController.php';
|
||||
$csvCols = ExportController::CSV_COLUMNS;
|
||||
$csvHeaders = ExportController::csvHeaders();
|
||||
$colCount = count($csvCols);
|
||||
|
||||
$colIdx = null;
|
||||
$headerRowNum = 0;
|
||||
$knownHeaders = [
|
||||
'identifiant', 'titre', 'sous-titre', 'auteur', 'contact',
|
||||
'promoteur', 'lecteur', 'ulb', 'externe', 'format', 'année', 'ap', 'orientation', 'finalité',
|
||||
'mots-clés', 'synopsis', 'contexte', 'remarques', 'langue',
|
||||
'autorisation', 'licence', 'license', 'points', 'lien baiu',
|
||||
];
|
||||
// Build knownHeaders from CSV_COLUMNS import_key column (index 2)
|
||||
$knownHeaders = array_unique(array_column($csvCols, 2));
|
||||
// Add licence/license cross-match aliases
|
||||
$knownHeaders[] = 'license';
|
||||
for ($scan = 0; $scan < 8; $scan++) {
|
||||
$hrow = fgetcsv($handle, 0, ',', '"', '');
|
||||
if ($hrow === false) break;
|
||||
@@ -101,10 +104,20 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// Build positional fallback from CSV_COLUMNS (keyed by import_key → position)
|
||||
$fallbackPositions = [];
|
||||
foreach ($csvCols as $pos => $col) {
|
||||
$key = $col[2];
|
||||
if (!isset($fallbackPositions[$key])) {
|
||||
$fallbackPositions[$key] = $pos;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: get cell value by column name.
|
||||
// When header was found: only use mapped column (returns '' if missing from header).
|
||||
// When no header found: use positional fallback index.
|
||||
$cell = function(array $row, string $name, int $fallbackPos) use ($colIdx): string {
|
||||
// When header was found: use mapped column.
|
||||
// When header was found but key is missing: use positional fallback.
|
||||
// When no header found: use positional fallback.
|
||||
$cell = function(array $row, string $name) use ($colIdx, $fallbackPositions): string {
|
||||
if ($colIdx !== null) {
|
||||
$pos = $colIdx[$name] ?? null;
|
||||
if ($pos === null) {
|
||||
@@ -112,13 +125,24 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
if ($name === 'license') $pos = $colIdx['licence'] ?? null;
|
||||
elseif ($name === 'licence') $pos = $colIdx['license'] ?? null;
|
||||
}
|
||||
// Fall back to positional if header didn't have this key
|
||||
if ($pos === null) {
|
||||
$pos = $fallbackPositions[$name] ?? null;
|
||||
}
|
||||
if ($pos === null) return '';
|
||||
} else {
|
||||
$pos = $fallbackPos;
|
||||
$pos = $fallbackPositions[$name] ?? null;
|
||||
if ($pos === null) return '';
|
||||
}
|
||||
return isset($row[$pos]) ? trim((string)$row[$pos]) : '';
|
||||
};
|
||||
|
||||
// Helper: parse "Oui"/"Non"/1/0 strings to int
|
||||
$parseBool = function(string $raw): int {
|
||||
$raw = strtolower(trim($raw));
|
||||
return ($raw === 'oui' || $raw === '1' || $raw === 'yes' || $raw === 'true') ? 1 : 0;
|
||||
};
|
||||
|
||||
// Code → canonical name (legacy short-code CSV format)
|
||||
$orientationCodeMap = [
|
||||
'SC'=>'Sculpture','VI'=>'Vidéographie','CA'=>"Cinéma d'animation",
|
||||
@@ -232,39 +256,55 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
try {
|
||||
$importDb->beginTransaction();
|
||||
|
||||
$identifier = $cell($row, 'identifiant', 0);
|
||||
$title = $cell($row, 'titre', 1);
|
||||
$subtitle = $cell($row, 'sous-titre', 2);
|
||||
$authorsRaw = $cell($row, 'auteur', 3);
|
||||
$contact = $cell($row, 'contact', 4);
|
||||
$identifier = $cell($row, 'identifiant');
|
||||
$title = $cell($row, 'titre');
|
||||
$subtitle = $cell($row, 'sous-titre');
|
||||
$authorsRaw = $cell($row, 'auteur');
|
||||
$contact = $cell($row, 'contact');
|
||||
// Normalise CSV artefacts: OUI/NON → empty (not a valid email)
|
||||
if ($contact !== '' && in_array(strtoupper(trim($contact)), ['NON', 'OUI'], true)) {
|
||||
$contact = '';
|
||||
}
|
||||
$supervisorsRaw = $cell($row, 'promoteur', 5);
|
||||
$lecteursInternesRaw = $cell($row, 'lecteur', 6); // first "lecteur" col = interne
|
||||
$lecteursExternesRaw = $cell($row, 'externe', 7); // contains "externe"
|
||||
$promoteursUlbRaw = $cell($row, 'ulb', 8); // contains "ulb"
|
||||
$formatsRaw = $cell($row, 'format', 9);
|
||||
$yearRaw = $cell($row, 'année', 10);
|
||||
$supervisorsRaw = $cell($row, 'promoteur');
|
||||
$lecteursInternesRaw = $cell($row, 'lecteur'); // first "lecteur" col = interne
|
||||
$lecteursExternesRaw = $cell($row, 'externe'); // contains "externe"
|
||||
$promoteursUlbRaw = $cell($row, 'ulb'); // contains "ulb"
|
||||
$formatsRaw = $cell($row, 'format');
|
||||
$yearRaw = $cell($row, 'année');
|
||||
$year = $yearRaw !== '' ? intval($yearRaw) : 0;
|
||||
// Fallback: derive year from identifier (e.g. "2024-003" → 2024)
|
||||
if ($year === 0 && $identifier !== '' && preg_match('/^(\d{4})-/', $identifier, $m)) {
|
||||
$year = (int)$m[1];
|
||||
}
|
||||
$apCode = $cell($row, 'ap', 11);
|
||||
$orientationCode = $cell($row, 'orientation', 12);
|
||||
$finalityName = $cell($row, 'finalité', 13);
|
||||
$keywordsRaw = $cell($row, 'mots-clés', 14);
|
||||
$synopsis = $cell($row, 'synopsis', 15);
|
||||
$context = $cell($row, 'contexte', 16);
|
||||
$remarks = $cell($row, 'remarques', 17);
|
||||
$languageRaw = $cell($row, 'langue', 18);
|
||||
$access = $cell($row, 'autorisation', 19);
|
||||
$license = $cell($row, 'license', 20);
|
||||
$juryPointsRaw = $cell($row, 'points', 21);
|
||||
$apCode = $cell($row, 'ap');
|
||||
$orientationCode = $cell($row, 'orientation');
|
||||
$finalityName = $cell($row, 'finalité');
|
||||
$keywordsRaw = $cell($row, 'mots-clés');
|
||||
$synopsis = $cell($row, 'synopsis');
|
||||
$context = $cell($row, 'contexte');
|
||||
$remarks = $cell($row, 'remarques');
|
||||
$languageRaw = $cell($row, 'langue');
|
||||
$access = $cell($row, 'autorisation');
|
||||
$license = $cell($row, 'licence');
|
||||
$juryPointsRaw = $cell($row, 'points');
|
||||
$juryPoints = $juryPointsRaw !== '' ? floatval($juryPointsRaw) : null;
|
||||
$baiuLink = $cell($row, 'lien baiu', 22);
|
||||
$baiuLink = $cell($row, 'lien baiu');
|
||||
$cc2rRaw = $cell($row, 'cc2r');
|
||||
$cc2r = $parseBool($cc2rRaw);
|
||||
$exempBaiuRaw = $cell($row, 'exemplaire'); // first "exemplaire" col = BAIU
|
||||
$exempBaiu = $parseBool($exempBaiuRaw);
|
||||
$exempErgRaw = $cell($row, 'exemplaire-erg'); // second "exemplaire", key won't match header → positional
|
||||
$exempErg = $parseBool($exempErgRaw);
|
||||
$durationPagesRaw = $cell($row, 'pages');
|
||||
$durationPages = $durationPagesRaw !== '' ? (int)$durationPagesRaw : null;
|
||||
$durationMinutesRaw = $cell($row, 'durée');
|
||||
$durationMinutes = $durationMinutesRaw !== '' ? (int)$durationMinutesRaw : null;
|
||||
$hasAnnexesRaw = $cell($row, 'annexes');
|
||||
$hasAnnexes = $parseBool($hasAnnexesRaw);
|
||||
$licenseCustom = $cell($row, 'licence-perso'); // second "licence", key won't match header → positional
|
||||
$contactVisible = $cell($row, 'contact-visible'); // second "contact", key won't match header → positional
|
||||
$objetRaw = $cell($row, 'objet');
|
||||
$objet = in_array($objetRaw, ['tfe', 'thèse', 'frart'], true) ? $objetRaw : 'tfe';
|
||||
|
||||
if ($title === '' || $year === 0) {
|
||||
$missing = [];
|
||||
@@ -303,14 +343,31 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve license name → license_id
|
||||
$licenseId = null;
|
||||
if (!empty($license)) {
|
||||
$ls = $importPdo->prepare("SELECT id FROM license_types WHERE LOWER(name) = LOWER(?)");
|
||||
$ls->execute([trim($license)]);
|
||||
$lr = $ls->fetch();
|
||||
$licenseId = $lr ? (int)$lr['id'] : null;
|
||||
}
|
||||
// If a custom license string is provided and no DB license matched, store as custom
|
||||
if ($licenseId === null && !empty($license) && empty($licenseCustom)) {
|
||||
$licenseCustom = $license;
|
||||
}
|
||||
|
||||
$s = $importPdo->prepare("
|
||||
INSERT INTO theses (
|
||||
identifier, title, subtitle, year,
|
||||
orientation_id, ap_program_id, finality_id,
|
||||
synopsis, context_note, remarks,
|
||||
jury_points, baiu_link,
|
||||
access_type_id, is_published, submitted_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,0,CURRENT_TIMESTAMP)
|
||||
access_type_id, license_id, license_custom,
|
||||
cc2r, exemplaire_baiu, exemplaire_erg,
|
||||
objet, contact_visible,
|
||||
duration_pages, duration_minutes, has_annexes,
|
||||
is_published, status, submitted_at
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,0,'active',CURRENT_TIMESTAMP)
|
||||
");
|
||||
$s->execute([
|
||||
!empty($identifier) ? $identifier : null, $title,
|
||||
@@ -322,6 +379,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['csv_file'])) {
|
||||
$juryPoints,
|
||||
!empty($baiuLink) ? $baiuLink : null,
|
||||
$accessTypeId,
|
||||
$licenseId,
|
||||
!empty($licenseCustom) ? $licenseCustom : null,
|
||||
$cc2r,
|
||||
$exempBaiu,
|
||||
$exempErg,
|
||||
$objet,
|
||||
!empty($contactVisible) ? $contactVisible : null,
|
||||
$durationPages,
|
||||
$durationMinutes,
|
||||
$hasAnnexes,
|
||||
]);
|
||||
$thesisId = $importPdo->lastInsertId();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user