// @deno-types="https://cdn.sheetjs.com/xlsx-0.20.3/package/types/index.d.ts" import * as XLSX from "https://cdn.sheetjs.com/xlsx-0.20.3/package/xlsx.mjs"; export type ParsedUE = { code: string | null; name: string; ects: number | null; modules: ParsedModule[]; }; export type ParsedModule = { code: string; name: string; coeff: number; }; export type ParsedYear = { label: string; ues: ParsedUE[]; }; /** * Analyse un classeur Excel pour en extraire la maquette pédagogique. */ export function parseMaquette(workbook: XLSX.WorkBook): ParsedYear[] { const sheet = workbook.Sheets[workbook.SheetNames[0]]; const rows = XLSX.utils.sheet_to_json<(string | number | null)[]>(sheet, { header: 1, }); const years: ParsedYear[] = []; let currentYear: ParsedYear | null = null; let currentUE: ParsedUE | null = null; let moduleIndex = 0; for (const row of rows) { if (!row || row.length === 0) continue; const col0 = row[0] != null ? String(row[0]).trim() : ""; // Detect year row: INFO3A, INFO4A, INFO5A, INFOAPP3A, INFOAPP4A, etc. if (/^(INFO|INFOAPP)\s*\d+A$/i.test(col0)) { currentYear = { label: col0, ues: [] }; years.push(currentYear); currentUE = null; continue; } // Detect UE row: col[0] === "UE" or starts with "UE" (e.g., "UE51") if (col0 === "UE" || (col0.startsWith("UE") && /^UE\d/.test(col0))) { const ueCode = row[1] != null ? String(row[1]).trim() : null; const ueName = row[2] != null ? String(row[2]).trim() : "UE sans nom"; const ects = typeof row[4] === "number" ? row[4] : null; currentUE = { code: ueCode, name: ueName, ects, modules: [] }; if (currentYear) { currentYear.ues.push(currentUE); } else { // No year detected yet — create a default one currentYear = { label: "Maquette", ues: [currentUE] }; years.push(currentYear); } moduleIndex = 0; continue; } // Detect semester header rows — just skip, don't reset UE if (/^SEM\s*\d/i.test(col0)) { currentUE = null; continue; } // Detect module row: inside a UE, col[3] has a name, col[5] is a number (coeff) if (currentUE && row[3] != null && typeof row[5] === "number") { const modName = String(row[3]).trim(); if (!modName) continue; let modCode = row[1] != null ? String(row[1]).trim() : ""; if (!modCode) { const uePrefix = (currentUE.code || "MOD").replace(/[^A-Z0-9]/gi, ""); modCode = `${uePrefix}_${String(moduleIndex).padStart(2, "0")}`; } currentUE.modules.push({ code: modCode, name: modName, coeff: row[5] }); moduleIndex++; } } return years; }