951c9c1fea
Check Deno code / Check Deno code (pull_request) Has been cancelled
Tests / Unit tests (pull_request) Has been cancelled
Tests / Integration tests (pull_request) Has been cancelled
Check Deno code / Check Deno code (push) Has been cancelled
Tests / Unit tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
// @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;
|
|
}
|