maj api+formation+app+main et maj index

This commit is contained in:
sehl
2026-03-31 17:54:23 +02:00
parent 20bdddd729
commit 1e95bad09d
5 changed files with 118 additions and 872 deletions
+15 -125
View File
@@ -1,131 +1,21 @@
// Échapper les apostrophes dans les valeurs injectées dans la clause where export function buildURL(query) {
function echapperValeur(valeur) { let url =
return String(valeur).replace(/'/g, "\\'"); "https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/fr-esr-parcoursup/records?limit=10"
if (query && query.trim() !== "") {
url += "&where=search(lib_for_voe_ins, '" + query + "')"
}
return url
} }
// Construire l'URL de requête vers l'API Parcoursup export async function fetchFormations(query) {
export function construireURL(requete, limite = 20, decalage = 0, filtres = {}) { const url = buildURL(query)
const response = await fetch(url)
var url = "https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/fr-esr-parcoursup/records?"; if (!response.ok) {
throw new Error("Erreur HTTP")
url += "limit=" + limite;
url += "&offset=" + decalage;
var conditions = [];
if (requete && requete.trim() !== "") {
conditions.push("search(lib_for_voe_ins, '" + echapperValeur(requete.trim()) + "')");
} }
if (filtres.filiere && filtres.filiere !== "") { return await response.json()
conditions.push("fili='" + echapperValeur(filtres.filiere) + "'");
}
if (filtres.selectivite && filtres.selectivite !== "") {
conditions.push("select_form='" + echapperValeur(filtres.selectivite) + "'");
}
if (filtres.region && filtres.region !== "") {
conditions.push("region_etab_aff='" + echapperValeur(filtres.region) + "'");
}
if (filtres.tauxMin && filtres.tauxMin > 0) {
conditions.push("taux_acces_ens>=" + filtres.tauxMin);
}
if (filtres.tauxMax && filtres.tauxMax < 100) {
conditions.push("taux_acces_ens<=" + filtres.tauxMax);
}
if (conditions.length > 0) {
url += "&where=" + encodeURIComponent(conditions.join(" AND "));
}
return url;
}
// Charger les formations depuis l'API Parcoursup
export async function chargerFormations(requete, limite = 20, decalage = 0, filtres = {}) {
var url = construireURL(requete, limite, decalage, filtres);
var reponse = await fetch(url);
if (!reponse.ok) {
throw new Error("Erreur HTTP " + reponse.status);
}
return await reponse.json();
}
// Charger l'historique d'une formation sur plusieurs années
export async function chargerHistoriqueFormation(codUai, nomFormation) {
var jeuDeDonnees = {
2020: "fr-esr-parcoursup_2020",
2021: "fr-esr-parcoursup_2021",
2022: "fr-esr-parcoursup_2022",
2023: "fr-esr-parcoursup_2023",
2024: "fr-esr-parcoursup_2024",
2025: "fr-esr-parcoursup"
};
var historique = [];
var nomCourt = echapperValeur((nomFormation || "").substring(0, 40));
var codeUai = echapperValeur(codUai);
var annees = [2020, 2021, 2022, 2023, 2024, 2025];
for (var i = 0; i < annees.length; i++) {
var annee = annees[i];
var dataset = jeuDeDonnees[annee];
try {
var where =
"cod_uai='" + codeUai + "' AND search(lib_for_voe_ins, '" + nomCourt + "')";
var url = "https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/"
+ dataset + "/records?"
+ "limit=5"
+ "&where=" + encodeURIComponent(where)
+ "&select=" + encodeURIComponent("cod_uai,lib_for_voe_ins,voe_tot,acc_tot,pct_sansmention,pct_ab,pct_b,pct_tb,pct_tbf,pct_bg,pct_bt,pct_bp");
var reponse = await fetch(url);
if (reponse.ok) {
var donnees = await reponse.json();
if (donnees.results && donnees.results.length > 0) {
var ligne = donnees.results[0];
var taux = 0;
if (ligne.voe_tot && ligne.voe_tot > 0) {
taux = Math.round((ligne.acc_tot / ligne.voe_tot) * 100);
}
historique.push({
annee: annee,
tauxAcces: taux,
candidats: ligne.voe_tot || 0,
admis: ligne.acc_tot || 0,
pctSansMention: ligne.pct_sansmention || 0,
pctAB: ligne.pct_ab || 0,
pctB: ligne.pct_b || 0,
pctTB: ligne.pct_tb || 0,
pctTBF: ligne.pct_tbf || 0,
pctGeneral: ligne.pct_bg || 0,
pctTechno: ligne.pct_bt || 0,
pctPro: ligne.pct_bp || 0
});
}
}
} catch (e) {
console.warn("Erreur pour l'année " + annee + " :", e);
}
}
return historique;
} }
+56 -525
View File
File diff suppressed because it is too large Load Diff
+17 -91
View File
@@ -1,97 +1,23 @@
// Créer un objet formation à partir des données brutes de l'API export function createFormation(raw) {
export function creerFormation(brut) { let taux = 0
var taux = 0; if (raw.voe_tot && raw.voe_tot > 0) {
var latitude = null; taux = Math.round((raw.acc_tot / raw.voe_tot) * 100)
var longitude = null;
if (brut.voe_tot && brut.voe_tot > 0) {
taux = Math.round((brut.acc_tot / brut.voe_tot) * 100);
}
if (brut.g_olocalisation_des_formations) {
latitude = brut.g_olocalisation_des_formations.lat;
longitude = brut.g_olocalisation_des_formations.lon;
} }
return { return {
id: brut.cod_uai + "-" + brut.lib_for_voe_ins, id: raw.cod_uai + "-" + raw.lib_for_voe_ins,
nom: raw.lib_for_voe_ins,
nom: brut.lib_for_voe_ins, etablissement: raw.g_ea_lib_vx,
etablissement: brut.g_ea_lib_vx, ville: raw.ville_etab,
ville: brut.ville_etab, departement: raw.dep,
departement: brut.dep, filiere: raw.fili,
departementLib: brut.dep_lib, selectivite: raw.select_form,
region: brut.region_etab_aff, capacite: raw.capa_fin,
academie: brut.acad_mies, candidats: raw.voe_tot,
contrat: brut.contrat_etab, admis: raw.acc_tot,
filiere: brut.fili,
selectivite: brut.select_form,
capacite: brut.capa_fin,
candidats: brut.voe_tot,
admis: brut.acc_tot,
tauxAcces: taux, tauxAcces: taux,
latitude: raw.g_olocalisation_des_formations ? raw.g_olocalisation_des_formations.lat : null,
latitude: latitude, longitude: raw.g_olocalisation_des_formations ? raw.g_olocalisation_des_formations.lon : null
longitude: longitude, }
pctFemmes: brut.pct_f,
pctBoursiers: brut.pct_bours,
pctNeoBac: brut.pct_neobac,
pctGeneral: brut.pct_bg,
pctTechno: brut.pct_bt,
pctPro: brut.pct_bp,
pctSansMention: brut.pct_sansmention,
pctAB: brut.pct_ab,
pctB: brut.pct_b,
pctTB: brut.pct_tb,
pctTBF: brut.pct_tbf,
pctDebutPhase: brut.pct_acc_debutpp,
pctDateBac: brut.pct_acc_datebac,
pctFinPhase: brut.pct_acc_finpp,
admisDebutPhase: brut.acc_debutpp,
admisDateBac: brut.acc_datebac,
admisFinPhase: brut.acc_finpp,
// Phase principale
voePPGeneral: brut.nb_voe_pp_bg,
voePPTechno: brut.nb_voe_pp_bt,
voePPPro: brut.nb_voe_pp_bp,
voePPAutres: brut.nb_voe_pp_at,
voePPTotal: brut.nb_voe_pp,
classesPPGeneral: brut.nb_cla_pp_bg,
classesPPTechno: brut.nb_cla_pp_bt,
classesPPPro: brut.nb_cla_pp_bp,
classesPPAutres: brut.nb_cla_pp_at,
classesPPTotal: brut.nb_cla_pp,
propositionsPPGeneral: brut.prop_tot_bg,
propositionsPPTechno: brut.prop_tot_bt,
propositionsPPPro: brut.prop_tot_bp,
propositionsPPAutres: brut.prop_tot_at,
propositionsPPTotal: brut.prop_tot,
acceptesPPGeneral: brut.acc_bg,
acceptesPPTechno: brut.acc_bt,
acceptesPPPro: brut.acc_bp,
acceptesPPAutres: brut.acc_at,
acceptesPPTotal: brut.acc_pp,
// Phase complémentaire
voePCGeneral: brut.nb_voe_pc_bg,
voePCTechno: brut.nb_voe_pc_bt,
voePCPro: brut.nb_voe_pc_bp,
voePCAutres: brut.nb_voe_pc_at,
voePCTotal: brut.nb_voe_pc,
classesPCTotal: brut.nb_cla_pc,
acceptesPCTotal: brut.acc_pc
};
} }
+12 -45
View File
@@ -1,58 +1,25 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Parcoursup Explorer</title> <title>Parcoursup Riot</title>
<link rel="stylesheet" href="./style.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/charts.css/dist/charts.min.css" />
<script src="https://cdn.jsdelivr.net/npm/riot@9/riot+compiler.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/riot@9/riot+compiler.min.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script type="module">
</head> import "./api.js"
<body> import "./formation.js"
</script>
</head>
<body>
<app></app> <app></app>
<script src="./components/search-bar.riot" type="riot"></script>
<script src="./components/result-list.riot" type="riot"></script>
<script src="./components/detail-view.riot" type="riot"></script>
<script src="./components/map-view.riot" type="riot"></script>
<script src="./components/auth-panel.riot" type="riot"></script>
<script src="./app.riot" type="riot"></script> <script src="./app.riot" type="riot"></script>
<script type="module"> <script>
import { chargerFormations, chargerHistoriqueFormation } from './api.js' riot.compile().then(() => {
import { creerFormation } from './formation.js'
import {
auth,
db,
createAccount,
login,
logout,
onUserChanged,
saveUserData,
loadUserData
} from './firebase.js'
window.chargerFormations = chargerFormations
window.creerFormation = creerFormation
window.chargerHistoriqueFormation = chargerHistoriqueFormation
window.firebaseServices = {
auth,
db,
createAccount,
login,
logout,
onUserChanged,
saveUserData,
loadUserData
}
await riot.compile()
riot.mount('app') riot.mount('app')
})
</script> </script>
</body> </body>
</html> </html>
-68
View File
@@ -1,68 +0,0 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Calculatrice simple</title>
</head>
<body>
<h1>Calculatrice</h1>
<?php
$resultat = "";
$op1 = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$op1 = $_POST["op1"];
$op2 = $_POST["op2"];
$operation = $_POST["operation"];
// Vérification que ce sont bien des nombres
if (is_numeric($op1) && is_numeric($op2)) {
switch ($operation) {
case "+":
$resultat = $op1 + $op2;
break;
case "-":
$resultat = $op1 - $op2;
break;
case "*":
$resultat = $op1 * $op2;
break;
case "/":
if ($op2 == 0) {
$resultat = "Erreur : division par 0 !";
} else {
$resultat = $op1 / $op2;
}
break;
}
// Pour utiliser le résultat comme prochaine valeur d'opérande 1
if (is_numeric($resultat)) {
$op1 = $resultat;
}
} else {
$resultat = "Veuillez entrer deux nombres valides.";
}
}
?>
<form method="post">
<input type="text" name="op1" value="<?= htmlspecialchars($op1) ?>" required>
<select name="operation">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="text" name="op2" required>
<input type="submit" value="Calculer">
</form>
<?php if ($resultat !== ""): ?>
<h2>Résultat : <?= $resultat ?></h2>
<?php endif; ?>
</body>
</html>