diff --git a/parcoursup/.gitignore b/parcoursup/.gitignore new file mode 100644 index 0000000..45a204e --- /dev/null +++ b/parcoursup/.gitignore @@ -0,0 +1,8 @@ +# Fichiers de présentation et documentation temporaire +fonctions_js_soutenance.md + +# Autres fichiers courants à ignorer +node_modules/ +*.log +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/parcoursup/README.md b/parcoursup/README.md index fd3c584..05955f7 100644 --- a/parcoursup/README.md +++ b/parcoursup/README.md @@ -88,5 +88,54 @@ L'application utilise un système de routage manuel natif basé sur l'API `windo * Affichage conditionnel des composants via les directives natives `if={ state.view === ... }`. *** +## Cycle de vie de l'application +### Initialisation de la carte dans map-view.riot +```javascript +onMounted() { + this.map = L.map(element).setView([46.8, 2.5], 6) // crée la carte Leaflet + L.tileLayer('https://...').addTo(this.map) // ajoute les tuiles OpenStreetMap +} +``` + +**Étape 9 — L'application est prête, elle attend l'utilisateur** + +À ce stade le navigateur affiche la page avec la barre de recherche, la carte vide centrée sur la France, et aucun résultat. Tout est en mémoire, prêt à réagir. + +**Étape 10 — L'utilisateur interagit** +``` +L'utilisateur tape "BUT informatique" et clique Rechercher + → le navigateur déclenche l'événement onclick sur le bouton + → Riot appelle submitSearch() dans search-bar.riot + → qui appelle props.onsearch(requete, filtres) + → qui appelle lancerRecherche() dans app.riot + → qui appelle chargerPage(1) + → qui appelle fetch() vers l'API + → l'API retourne le JSON + → app.riot fait this.update({ resultats: formations }) + → Riot détecte que le state a changé + → Riot re-rend le HTML automatiquement + → les cards de résultats apparaissent + → map-view reçoit les nouvelles props + → refreshMarkers() place les marqueurs +``` + +**Résumé de l'ordre d'exécution :** +``` +1. index.html chargé +2. CSS téléchargés (style.css, leaflet.css, charts.css) +3. JS externes téléchargés (riot, leaflet) +4. Fichiers .riot téléchargés (pas exécutés) +5. Modules JS importés (api.js, formation.js, firebase.js, estimation.js, selection.js) +6. Firebase se connecte +7. Tout exposé sur window +8. riot.compile() → compile les .riot en JS +9. riot.mount('app') → monte le composant principal +10. onMounted() → charge localStorage, écoute Firebase, écoute hashchange +11. gererRoute() → lit le hash → affiche la vue recherche +12. Composants enfants montés (search-bar, map-view, result-list) +13. Application prête → attend les interactions utilisateur +``` + +*** *Réalisé par Aylane Sehl, Jenson Val et Séri-Khane Yolou à l'IUT Sénart-Fontainebleau (UPEC).* diff --git a/parcoursup/api.js b/parcoursup/api.js index 72f1e5c..82ebb27 100644 --- a/parcoursup/api.js +++ b/parcoursup/api.js @@ -1,24 +1,59 @@ // Échapper les apostrophes dans les valeurs injectées dans la clause where +// --------------------------------------------------------------- +// Cette fonction est utilisée pour prévenir les problèmes de syntaxe +// quand on insère une valeur dans une requête Parcoursup qui nécessite +// des guillemets simples. function echapperValeur(valeur) { + // force en string (undefined/null -> "undefined"/"null"), puis remplace toutes + // les apostrophes par une version échappée (\'). return String(valeur).replace(/'/g, "\\'"); } // Construire l'URL de requête vers l'API Parcoursup +// -------------------------------------------------- +// - filtre par recherche texte + plusieurs paramètres de filtre +// - limit / offset pour pagination +// - where encodé (via encodeURIComponent) export function construireURL(requete, limite = 20, decalage = 0, filtres = {}) { var url = "https://data.enseignementsup-recherche.gouv.fr/api/explore/v2.1/catalog/datasets/fr-esr-parcoursup/records?"; + // pagination simple url += "limit=" + limite; url += "&offset=" + decalage; var conditions = []; + // recherche libre if (requete && requete.trim() !== "") { conditions.push("search(lib_for_voe_ins, '" + echapperValeur(requete.trim()) + "')"); } + // ===== FILTRE FILIÈRE ===== + // Ce bloc filtre les formations par type (BTS, BUT, Licence, CPGE, etc.) + // Il est OPTIONNEL : si l'user ne choisit pas de filière, on le saute. + + // CONDITION : "Si l'user a choisi une filière ET qu'elle n'est pas vide" if (filtres.filiere && filtres.filiere !== "") { + // if (filtres.filiere && ...) + // ↑ vérifie que filtres.filiere EXISTE (n'est pas undefined/null) + // + // if (... && filtres.filiere !== "") + // ↑ ET vérifie que la filière NE S'PAS VIDE (pas "") + // + // RÉSUMÉ : si les 2 conditions sont vraies, on rentre dans le bloc + + // ACTION : construire et ajouter le filtre filière conditions.push("fili='" + echapperValeur(filtres.filiere) + "'"); + // + // Exemple si l'user choisit "BTS" : + // - filtres.filiere = "BTS" + // - echapperValeur("BTS") = "BTS" (pas d'apostrophe, inchangé) + // - condition.push ajoute : "fili='BTS'" au tableau conditions + // - Résultat : conditions = [..., "fili='BTS'"] + // + // Cette condition sera fusionnée avec les autres via AND + // Exemple d'URL finale : ...where=search(...) AND fili='BTS' AND region='IDF' } if (filtres.selectivite && filtres.selectivite !== "") { @@ -37,6 +72,7 @@ export function construireURL(requete, limite = 20, decalage = 0, filtres = {}) conditions.push("taux_acces_ens<=" + filtres.tauxMax); } + // si on a des conditions, on les ajoute dans paramètre where encodé if (conditions.length > 0) { url += "&where=" + encodeURIComponent(conditions.join(" AND ")); } @@ -45,11 +81,14 @@ export function construireURL(requete, limite = 20, decalage = 0, filtres = {}) } // Charger les formations depuis l'API Parcoursup +// ---------------------------------------------- +// Appelle l'URL construite ci-dessus via fetch, parse le JSON. export async function chargerFormations(requete, limite = 20, decalage = 0, filtres = {}) { var url = construireURL(requete, limite, decalage, filtres); var reponse = await fetch(url); + // vérifie le code HTTP; si erreur, lève une exception pour le remonté à l'appelant if (!reponse.ok) { throw new Error("Erreur HTTP " + reponse.status); } @@ -58,6 +97,10 @@ export async function chargerFormations(requete, limite = 20, decalage = 0, filt } // Charger l'historique d'une formation sur plusieurs années +// --------------------------------------------------------- +// - appelle plusieurs versions du dataset (2020..2025) +// - récupère le premier résultat valide pour chaque année +// - calcule un taux d'accès (acc_tot / voe_tot) export async function chargerHistoriqueFormation(codUai, nomFormation) { var jeuDeDonnees = { @@ -70,6 +113,8 @@ export async function chargerHistoriqueFormation(codUai, nomFormation) { }; var historique = []; + + // on échappe aussi cod_uai et nom pour la requête (sécurité sintaxe) var nomCourt = echapperValeur((nomFormation || "").substring(0, 40)); var codeUai = echapperValeur(codUai); var annees = [2020, 2021, 2022, 2023, 2024, 2025]; @@ -105,6 +150,7 @@ export async function chargerHistoriqueFormation(codUai, nomFormation) { taux = Math.round((ligne.acc_tot / ligne.voe_tot) * 100); } + // ajoute un objet historique pour cette année historique.push({ annee: annee, tauxAcces: taux, @@ -123,6 +169,7 @@ export async function chargerHistoriqueFormation(codUai, nomFormation) { } } catch (e) { + // si un appel échoue, on affiche l'erreur et on continue la boucle console.warn("Erreur pour l'année " + annee + " :", e); } } diff --git a/parcoursup/fonctions_js_soutenance.md b/parcoursup/fonctions_js_soutenance.md new file mode 100644 index 0000000..99fc489 --- /dev/null +++ b/parcoursup/fonctions_js_soutenance.md @@ -0,0 +1,367 @@ +# 📚 Fiche de Révision – Fonctions JS — Parcoursup Explorer + +> Préparée pour la soutenance. Tous les fichiers `.js` et `.riot` (partie `