Référentiel Établissements — Construction
A1Connect est la source de vérité du référentiel des établissements (lycées + supérieur), alimenté depuis ONISEP chaque mois et complété par le dataset MESR des campus connectés. Dema1n et Inspire gardent chacun une copie locale rafraîchie sur notification.
[ONISEP] →(mensuel)→ [A1C] →(webhook POST)→ [Dema1n] copie locale
[MESR] →(mensuel)→ →(webhook POST)→ [Inspire] copie locale
Pour consommer les données déjà construites (auth, endpoints, format), voir api.md.
Récapitulatif des syncs
| Donnée | Source | Déclenchement | Endpoint manuel |
|---|---|---|---|
| Structures (lycées + supérieur) | ONISEP Idéo | Cron mensuel | POST /admin/etablissements/sync |
| Campus connectés (sup., hors UAI) | MESR — dataset fr-esr-campus-connectes |
Cron mensuel (même cron) | POST /admin/etablissements/sync/campus-connecte |
| Ministère (tutelle) | ONISEP Idéo | Manuel | POST /admin/etablissements/sync/ministere |
| Téléphone | ONISEP Idéo | Manuel | POST /admin/etablissements/sync/telephone |
| Formations | ONISEP Idéo (2 datasets dédiés) | Auto après chaque sync mensuel + manuel | POST /admin/etablissements/sync/formations |
| Code INSEE commune | geo.api.gouv.fr | Auto (calcul des flags) + manuel | POST /admin/etablissements/sync/insee |
| ZRR | collectivites-locales.gouv.fr (Excel FRR) | Auto (calcul des flags) + manuel | POST /admin/etablissements/sync/zrr |
| QPV | SIGVille ANCT | Auto (calcul des flags) + manuel | POST /admin/etablissements/sync/qpv |
| IPS / IEL | data.education.gouv.fr | Auto (calcul des flags) + manuel | POST /admin/etablissements/sync/ips-iel |
| isPrioritaire | Calculé (QPV ou ZRR ou IEL ou IPS renseigné ou MVLS partenaire) | Manuel (bouton admin) | POST /admin/etablissements/sync/prioritaire |
| Sélectif | Liste statique SELECTIF_UAIS |
Manuel | POST /admin/etablissements/sync/selectif |
| Cordées de la Réussite | Scraping portail ONISEP | Manuel | POST /admin/etablissements/sync/cordees |
| isMVLSPartenaire | Aucune — édition admin uniquement | — | PATCH /admin/etablissements/:id |
Source de données — ONISEP Idéo
ONISEP est la seule source. Elle couvre lycées + supérieur sans primaire ni maternelle, déjà filtrés pour l'orientation scolaire.
Pourquoi ONISEP et pas RAMSESE : RAMSESE ne couvre pas le supérieur et inclut primaire/maternelle (~30K établissements inutiles). ONISEP couvre les deux en un seul outil.
| Dataset | ID | Volume |
|---|---|---|
| Structures secondaire | 5fa5816ac6a6e |
~15 283 |
| Structures supérieur | 5fa586da5c4b6 |
~9 024 |
Auth : Bearer token JWT 24h + Application-ID statique. OnisepAuthService gère le cache mémoire du token (TTL 23h, renouvellement automatique). Env vars : ONISEP_APP_ID, ONISEP_EMAIL, ONISEP_PASSWORD.
Deux champs ONISEP sont aussi resynchronisables seuls, sans repasser par l'upsert complet : ministere (POST /admin/etablissements/sync/ministere) et telephone (POST /admin/etablissements/sync/telephone).
Source de données — Campus connectés (MESR)
Deuxième source, indépendante d'ONISEP : dataset fr-esr-campus-connectes (MESR, via OpenDataSoft, CampusConnecteAdapter). Couvre des structures du supérieur sans code UAI — upsert par idPaysage (clé MESR) plutôt que par codeUai, avec estCampusConnecte: true posé à la création.
Auth : clé API en query string, publique (exposée côté front sur services.dgesip.fr, pas un secret) — CAMPUS_CONNECTE_API_KEY dans campus-connecte.adapter.ts.
Champs alimentés : nom, adresse, academie, region, universiteRattachement, telephone, latitude, longitude. Pas de codePostal fourni ⇒ pas de résolution INSEE ⇒ pas de ZRR (ni IPS/IEL, qui matchent aussi par codeUai, absent ici). QPV reste calculable via SIGVille (lat/long directement).
Synchronisation
Cron
Un seul cron NestJS : @Cron('0 3 1 * *') dans EtablissementSyncService (1er du mois à 3h), qui déclenche successivement le sync ONISEP puis le sync campus connectés. Pas de CronJob K8s nécessaire — le scheduling est géré par l'application.
Le sync ONISEP se termine dès que l'upsert est fini. Le calcul des flags tourne ensuite en arrière-plan (non-bloquant). État accessible en temps réel via GET /api/v1/etablissements/sync/status.
Post-sync (ONISEP uniquement) : email de rapport Brevo à tech@article-1.eu (succès ou échec). Pas de message RabbitMQ à ce jour.
Flow complet
1. Pull ONISEP — 2 datasets (~26 appels paginés)
2. Upsert ~24K records par codeUai (idempotent, relançable)
3. Résolution codes INSEE
→ si index absent : chargement automatique depuis geo.api.gouv.fr (~35K communes)
→ chaque établissement : codePostal + ville → codeInseeCommune
4. Calcul des flags (en parallèle)
├─ ZRR → Excel FRR (collectivites-locales.gouv.fr) + codeInseeCommune
├─ QPV → SIGVille API X,Y (latitude/longitude) — 50 requêtes concurrentes
└─ IPS + IEL → datasets data.education.gouv.fr (match par UAI)
5. Webhook POST vers Dema1n + Inspire (non actif — à décommenter dans notifyPlatforms()
quand les plateformes auront leur endpoint POST /etablissements/sync)
6. (cron uniquement) Sync campus connectés MESR — déclenché juste après, indépendant d'ONISEP
Tout est idempotent. En cas d'erreur mi-parcours, relancer POST /api/v1/etablissements/sync suffit.
Chaque flag est aussi relançable seul (utile pour ne pas refaire tout syncAll) : POST /admin/etablissements/sync/zrr, /sync/qpv, /sync/ips-iel, /sync/selectif, /sync/prioritaire.
Calcul des flags — ZRR / QPV / IPS / IEL
Quatre indicateurs de vulnérabilité territoriale et socio-économique, calculés chacun par un service dédié (EtablissementZrrService, EtablissementQpvService, EtablissementIpsIelService, EtablissementPrioritaireService) orchestrés par EtablissementFlagsService. Description des champs exposés : voir api.md.
Sources
| Flag | Source | Détail |
|---|---|---|
| ZRR | collectivites-locales.gouv.fr — fichier Excel FRR |
Colonne Code_insee, filtrée sur la colonne Classement... (!== 'Non classée') — le fichier liste toutes les communes de France, pas seulement celles classées. URL dans la constante FRR_EXCEL_URL de etablissement-flags.constants.ts — à mettre à jour si le fichier est republié après une révision législative (~3–5 ans). |
| QPV | SIGVille ANCT — wsa.sig.ville.gouv.fr/api/xy.json |
Géoréférencement inverse : POST {type_quartier: 'QP', x: longitude, y: latitude} → OUI/NON. Auth HTTP Basic : SIGVILLE_USERNAME / SIGVILLE_PASSWORD. |
| IPS | data.education.gouv.fr — dataset fr-en-ips_lycees |
Champ ips_ensemble_gt_pro. Mis à jour à chaque rentrée scolaire. Fournit aussi code_insee_de_la_commune, renseigné en bonus sur les ~4 300 lycées couverts. |
| IEL | dataeducation.opendatasoft.com — dataset fr-en-indice_eloignement_lycee_ap2020 |
Champ indice_eloignement. Mis à jour à chaque rentrée scolaire. |
ZRR — source et signification de null
Source : liste officielle des communes classées France Ruralités Revitalisation (FRR, ex-ZRR) publiée par collectivites-locales.gouv.fr — elle couvre la quasi-totalité des communes de France, classées ou non.
zrr vaut :
- true — la commune est classée FRR.
- false — la commune est dans la liste FRR mais "Non classée".
- null — la commune n'a pas été trouvée dans la liste FRR (pas encore rattachée à un code INSEE, ou absente du fichier).
isPrioritaire
Pas une source externe : flag calculé (QPV ou ZRR ou IEL ou IPS renseigné ou MVLS partenaire), donc dépendant des flags ci-dessus et de isMVLSPartenaire. Reset puis ré-application complète à chaque appel — pas de sync automatique, déclenchement manuel uniquement (bouton "Prioritaire" du menu Actions en admin) : POST /admin/etablissements/sync/prioritaire.
Résolution code INSEE
ZRR et QPV requièrent le codeInseeCommune (code INSEE 5 chars), différent du codePostal ONISEP.
Résolution via un index mémoire construit depuis geo.api.gouv.fr/communes (Map<codePostal, {code, nom}[]>, ~35K communes). syncAll le charge automatiquement en début de traitement s'il n'est pas déjà en mémoire. Aucun appel réseau par établissement — tout se fait en mémoire. Statut : GET /admin/etablissements/communes-index/status ; rechargement forcé : POST /admin/etablissements/communes-index/refresh.
Suivi de la résolution : GET /admin/etablissements/sync/insee/remaining (non résolus), GET /admin/etablissements/sync/insee/not-found (sentinel posé), POST /admin/etablissements/sync/insee (relance la résolution sur les non-résolus).
Si ambiguïté (plusieurs communes pour un code postal) : match sur le nom de ville (exact, puis préfixe pour les arrondissements Paris/Lyon/Marseille). Si non résolu, fallback sur la première entrée.
Sentinel NOT_FOUND
codeInseeCommune a trois états :
| Valeur | Signification |
|---|---|
null |
Pas encore résolu |
"01234" |
Résolu |
"NOT_FOUND" |
Résolution tentée, aucune commune trouvée |
Le sentinel distingue "pas encore traité" de "traité sans résultat" — évite de retenter à chaque sync. Arrive pour des codes postaux CEDEX ou des établissements à l'étranger. Si le code postal est corrigé dans ONISEP, relançable via POST /v1/admin/etablissements/sync/insee/not-found.
Formations
Source : deux datasets ONISEP dédiés, distincts des structures (OnisepAdapter.fetchFormationsIndex).
| Dataset | ID |
|---|---|
| Formations lycée | 605340ddc19a9 |
| Formations supérieur | 605344579a7d7 |
Deux champs sont stockés à partir de ces datasets :
- formations — intitulé détaillé (formation_for_libelle, ex: "BTS gestion de la PME")
- formationsTypes — catégorie brute (for_type, ex: "brevet de technicien supérieur")
Sync indépendante du sync principal, non-bloquante : POST /admin/etablissements/sync/formations (EtablissementSyncService.syncFormations), aussi déclenchée automatiquement en arrière-plan après chaque sync mensuel.
Cordée de la Réussite & établissements sélectifs
Cordées — seule donnée du référentiel obtenue par scraping (pas de dataset opendata dédié) : CordeePortailAdapter parcourt portail.onisep.fr/cordeesdelareussite.onisep.fr académie par académie. Déclenché via POST /admin/etablissements/sync/cordees.
Établissements sélectifs — liste statique SELECTIF_UAIS, pas de source externe — reset puis ré-application complète à chaque sync.
Notes et limites
Fallback établissement libre — ONISEP ne couvre pas les CFA hors contrat, privés hors contrat, établissements étrangers. Le champ texte libre etablissementNom dans inspire-v2 reste nécessaire.
codeAcademie et etatEtablissement — non fournis par ONISEP, restent null dans A1C.
sigle — champ sigle du dataset ONISEP (secondaire et supérieur), souvent vide côté secondaire. Réécrit à chaque sync mensuel comme les autres champs ONISEP ; correction manuelle possible en admin mais non protégée d'un futur resync.
isMVLSPartenaire — seul champ du référentiel sans aucune source de sync : purement éditable en admin (PATCH /admin/etablissements/:id), jamais touché par un resync.