Documentation Technique : Jobs d’importation
Cette section décrit les traitements automatisés (Jobs) qui alimentent la base de données MongoDB de l’application Exposition, dans l’ordre d’exécution de la commande importAll.
1. Référentiels BCN
Vue d’ensemble
La BCN (Base Centrale des Nomenclatures) est le référentiel officiel de l’Éducation Nationale pour les formations et diplômes. Ce module importe et enrichit ces données pour permettre le suivi des formations à travers leurs évolutions (continuum).
Nomenclatures API utilisées
| Nomenclature | Description | Clé primaire | Usage |
|---|---|---|---|
V_FORMATION_DIPLOME | Diplômes voie apprentissage | formation_diplome | CFD apprentissage |
N_FORMATION_DIPLOME | Diplômes voie scolaire | formation_diplome | CFD scolaire + continuum |
N_FORMATION_DIPLOME_ENQUETE_51 | Diplômes apprentissage hors EN | formation_diplome | CFD apprentissage complémentaire |
N_MEF | Modules Élémentaires de Formation | mef_stat_11 | Années de formation |
N_DIPLOME_SISE | Diplômes supérieur | diplome_sise | Formations sup |
N_NIVEAU_FORMATION_DIPLOME | Niveaux de diplôme | niveau_formation_diplome | Enrichissement niveaux |
N_TYPE_DIPLOME_SISE | Types diplômes SISE | type_diplome_sise | Enrichissement SISE |
N_GROUPE_FORMATION | Familles de métiers | groupe_formation | Secondes communes |
N_LIEN_FORMATION_GROUPE | Liens formation-groupe | formation_diplome + groupe_formation | Association familles |
Formats des codes d’identification
CFD (Code Formation Diplôme) - 8 caractères alphanumériques
Exemple : 40025214
- Utilisé pour identifier un diplôme indépendamment de l’année de cursus
- Partagé entre voie scolaire et apprentissage
MEF STAT 11 - 11 caractères alphanumériques
Code hiérarchique : plus on lit de caractères, plus la description est fine.
Exemple : 24725214311
│││││││││││
││││││││└┴┴─ Groupe de spécialité (positions 9-11)
│││││└┴┴──── Domaine et catégorie de spécialité (positions 7-8)
││││└─────── Voie/Série (positions 5-6)
│││└──────── Niveau de classe (position 4)
││└───────── Dispositif de formation (position 3)
│└────────── Cycle d'enseignement (position 2)
└─────────── Degré d'enseignement (position 1)
| Position | Désignation | Signification |
|---|---|---|
| 1 | Degré | Primaire, secondaire, post-secondaire |
| 2 | Cycle | 1er cycle, 2ème cycle, cycle pro |
| 3 | Dispositif | CAP 1 an, CAP 2 ans, BAC PRO 3 ans, BTS 2 ans… |
| 4 | Niveau | Niveau de classe (1=1ère année, 2=2ème, 3=terminale) |
| 5-6 | Voie/Série | Générale, Technologique, Pro / S, L, ES, STI2D… |
| 7-9 | Spécialité | Catégorie, domaine et groupe de spécialité |
| 10-11 | Suffixe | Identification complémentaire |
- Position 4 (index 3) : Utilisée par le code pour identifier l’année du cursus
- Permet d’identifier une année précise d’une formation
- Utilisé uniquement pour la voie scolaire
SISE - 7 caractères numériques
Exemple : 2500128
- Utilisé pour l’enseignement supérieur
Collections MongoDB
| Collection | Clé unique | Description |
|---|---|---|
bcn | code_certification | CFD et MEF enrichis avec continuum |
bcnMef | mef_stat_11 | MEF détaillés (36 champs) |
bcnSise | diplome_sise | Diplômes supérieur |
importBCN
Description : Import des codes CFD (diplômes) et MEF (modules d’enseignement) depuis la Base Centrale des Nomenclatures.
- Source Externe : API BCN (V_FORMATION_DIPLOME, N_FORMATION_DIPLOME, N_FORMATION_DIPLOME_ENQUETE_51, N_MEF)
- Collections MongoDB Sources : -
- Collections MongoDB de Sortie :
bcn
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
code_certification | Calculé | Clé unique = code_formation_diplome (CFD) ou mef_stat_11 (MEF) |
code_formation_diplome | formation_diplome | Code CFD du diplôme parent |
type | Calculé | "cfd" ou "mef" selon la source |
Règles métier
- Deux types de certifications :
type: "cfd"pour les diplômes (sources : V_FORMATION_DIPLOME, N_FORMATION_DIPLOME, N_FORMATION_DIPLOME_ENQUETE_51)type: "mef"pour les modules d’enseignement (source : N_MEF)
-
Mapping du niveau de diplôme de l’ancienne nomenclature (0-5) vers la nouvelle (0-7) : | Ancien | Nouveau | Exemple | |——–|———|———| | 5 | 3 | CAP | | 4 | 4 | BAC PRO | | 3 | 5 | BTS, DUT | | 2 | 6 | Licence | | 1 | 7 | Master, Doctorat | | 0 | 0 | Mention complémentaire |
-
Extraction du niveau depuis le CFD :
CFD "40025214" → niveau_formation_diplome = "400" Lookup dans N_NIVEAU_FORMATION_DIPLOME → niveau_interministeriel = "4" Mapping → niveau = 4 (BAC) - Initialisation des tableaux
ancien_diplomeetnouveau_diplomevides (remplis par importBCNContinuum)
Traitements effectués
Pour chaque nomenclature (V_FORMATION_DIPLOME, N_FORMATION_DIPLOME, N_FORMATION_DIPLOME_ENQUETE_51):
1. Récupérer les données depuis l'API BCN
2. Pour chaque formation:
a. Extraire le code CFD (formation_diplome)
b. Calculer code_certification = formation_diplome
c. Définir type = "cfd"
d. Extraire niveau_formation_diplome (3 premiers caractères de formation_diplome si commence par chiffre)
e. Lookup niveau dans N_NIVEAU_FORMATION_DIPLOME
f. Mapper niveau ancien → nouveau
g. Parser dates (date_ouverture, date_fermeture) en UTC
h. Créer objet diplome: { code, libelle }
i. Upsert dans collection bcn (clé: code_certification)
Pour N_MEF:
1. Récupérer les données depuis l'API BCN
2. Pour chaque MEF:
a. Extraire mef_stat_11
b. Calculer code_certification = mef_stat_11
c. Définir type = "mef"
d. Récupérer code_formation_diplome depuis le champ formation_diplome
e. Parser dates en UTC
f. Upsert dans collection bcn (clé: code_certification)
- Upsert avec 10 workers parallèles
- Rate limiting : 5 requêtes/seconde vers l’API BCN
importBCNMEF
Description : Import détaillé des MEF (Modules d’Enseignement-Formation) pour l’enseignement secondaire.
- Source Externe : API BCN (N_MEF)
- Collections MongoDB Sources : -
- Collections MongoDB de Sortie :
bcnMef
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
mef_stat_11 | mef_stat_11 | Clé unique, 11 caractères |
mef | mef | Code MEF court |
formation_diplome | formation_diplome | CFD parent |
dispositif_formation | dispositif_formation | Type de voie (ex: 247 = 2nde pro) |
annee_dispositif | annee_dispositif | Année dans le cursus (1, 2 ou 3) |
duree_dispositif | duree_dispositif | Durée totale du cursus |
Champs importés (22 au total)
Catégories de champs :
- Identifiants :
mef,mef_stat_11,mef_stat_9,formation_diplome,dispositif_formation - Temporels :
annee_dispositif,duree_dispositif,duree_projet,duree_stage - Dates :
date_ouverture,date_fermeture,date_intervention - Libellés :
libelle_court,libelle_long,libelle_edition - Options pédagogiques :
nb_option_obligatoire,nb_option_facultatif,renforcement_langue,horaire - Inscription et statut :
mef_inscription_scolarite,statut_mef,commentaire
Traitements effectués
Pour chaque record N_MEF:
1. Mapper les 22 champs source vers le schéma cible
2. Convertir les dates au format UTC:
- date_ouverture: parseAsUTCDate(date_ouverture)
- date_fermeture: parseAsUTCDate(date_fermeture)
- date_intervention: parseAsUTCDate(date_intervention)
3. Ajouter métadonnées _meta: { created_on, updated_on }
4. Upsert dans bcnMef (clé: mef_stat_11)
importBCNSise
Description : Import des diplômes du supérieur (codes SISE) depuis la BCN.
- Source Externe : API BCN (N_DIPLOME_SISE) + 3 records fictifs créés par InserSup
- Collections MongoDB Sources : -
- Collections MongoDB de Sortie :
bcnSise
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
diplome_sise | diplome_sise | Clé unique, 7 caractères |
type_diplome_sise | type_diplome_sise | Type de diplôme supérieur |
Records fictifs BUT
3 codes SISE fictifs sont ajoutés pour certaines spécialités BUT non présentes dans la BCN :
{ diplome_sise: "0000001", libelle: "BUT spécialité 1" }
{ diplome_sise: "0000002", libelle: "BUT spécialité 2" }
{ diplome_sise: "0000003", libelle: "BUT spécialité 3" }
Traitements effectués
1. Récupérer N_DIPLOME_SISE depuis l'API BCN
2. Récupérer N_TYPE_DIPLOME_SISE pour enrichissement
3. Fusionner avec les 3 records fictifs BUT
4. Pour chaque diplôme SISE:
a. Extraire les 19 champs principaux
b. Enrichir avec type_diplome depuis N_TYPE_DIPLOME_SISE
c. Upsert dans bcnSise (clé: diplome_sise)
importBCNContinuum
Description : Construction du continuum diplôme en établissant les relations de succession entre anciens et nouveaux diplômes.
- Source Externe : API BCN (N_FORMATION_DIPLOME)
- Collections MongoDB Sources :
bcn - Collections MongoDB de Sortie :
bcn(mise à jour)
Structure du continuum dans N_FORMATION_DIPLOME
La table N_FORMATION_DIPLOME contient jusqu’à 7 champs pour les anciens diplômes et 7 pour les nouveaux :
ancien_diplome_1, ancien_diplome_2, ..., ancien_diplome_7
nouveau_diplome_1, nouveau_diplome_2, ..., nouveau_diplome_7
Règles métier
- Relation bidirectionnelle :
- Si diplôme A référence B dans
ancien_diplome_*, alors B obtient A dansnouveau_diplome - Si diplôme A référence C dans
nouveau_diplome_*, alors C obtient A dansancien_diplome
- Si diplôme A référence B dans
-
Auto-références filtrées : Un diplôme ne peut pas être son propre successeur/prédécesseur
- Relations many-to-many : Un diplôme peut avoir plusieurs prédécesseurs ET plusieurs successeurs
Traitements effectués
1. Récupérer N_FORMATION_DIPLOME depuis l'API BCN
2. Pour chaque formation:
a. Extraire dynamiquement tous les champs ancien_diplome_* non vides
b. Extraire dynamiquement tous les champs nouveau_diplome_* non vides
c. Filtrer les auto-références (cfd !== ancien_cfd && cfd !== nouveau_cfd)
3. Mise à jour directe (A → anciens/nouveaux):
Pour chaque CFD A avec anciens [B, C] et nouveaux [D]:
- Update bcn où code_certification = A:
$addToSet ancien_diplome: [B, C]
$addToSet nouveau_diplome: [D]
4. Mise à jour inverse (anciens/nouveaux → A):
Pour chaque CFD A avec anciens [B, C]:
- Update bcn où code_certification IN [B, C]:
$addToSet nouveau_diplome: A
Pour chaque CFD A avec nouveaux [D]:
- Update bcn où code_certification = D:
$addToSet ancien_diplome: A
5. Mise à jour des dates de session:
- $set date_premiere_session depuis la source BCN
- $set date_derniere_session depuis la source BCN
computeBCNMEFContinuum
Description : Projection du continuum CFD au niveau MEF pour suivre les trajectoires de modules d’enseignement.
- Source Externe : -
- Collections MongoDB Sources :
bcn,bcnMef - Collections MongoDB de Sortie :
bcn(mise à jour des records MEF)
Algorithme de résolution MEF
Pour chaque record MEF dans bcn (où type = "mef"):
1. Récupérer le CFD parent depuis code_formation_diplome
2. Récupérer le continuum CFD (ancien_diplome, nouveau_diplome)
Pour chaque ancien_cfd dans ancien_diplome:
3. Récupérer tous les MEF de l'ancien CFD depuis bcnMef
4. Appliquer l'algorithme de matching:
SI ancien_cfd a exactement 1 MEF ET cfd_courant a exactement 1 MEF:
→ Association directe (cas 1:1)
SINON:
a. Filtrer par dispositif_formation identique
b. Comparer les 4 premiers caractères de mef_stat_11
c. Si correspondance trouvée → association
d. Sinon → pas d'association (null)
5. Si MEF ancien trouvé:
$addToSet ancien_diplome: mef_ancien
Pour chaque nouveau_cfd dans nouveau_diplome:
6. Même algorithme pour trouver le MEF nouveau
7. Si MEF nouveau trouvé:
$addToSet nouveau_diplome: mef_nouveau
Exemple de matching
CFD actuel: 40025214
→ MEF parent: mef_stat_11 = "24725214311", dispositif_formation = "247"
CFD ancien: 40025213 avec 2 MEFs dans bcnMef:
- mef_stat_11 = "24725213311", dispositif_formation = "247"
→ MATCH : même dispositif_formation ET mef_stat_11.substr(0,4) = "2472" identique
- mef_stat_11 = "24125213311", dispositif_formation = "241"
→ PAS DE MATCH : dispositif_formation différent (241 ≠ 247)
importLibelle
Description : Enrichissement des certifications avec les libellés courants et historiques.
- Source Externe : -
- Collections MongoDB Sources :
bcn - Collections MongoDB de Sortie :
bcn(mise à jour)
Règles métier
-
Parcours récursif de la chaîne
ancien_diplomejusqu’à trouver un libellé fondamentalement différent -
Comparaison des libellés :
// Normalisation: suppression accents, minuscules normalize("CAP Boulangerie") → "cap boulangerie" // Comparaison par inclusion Si libelle_actuel est sous-chaîne de libelle_ancien: → considérés identiques, continuer le parcours Sinon: → libellés différents, stocker et arrêter -
Conditions d’arrêt :
- Branchement (plusieurs prédécesseurs)
- Fin de chaîne (plus d’ancien diplôme)
- Libellé fondamentalement différent trouvé
Traitements effectués
Pour chaque certification dans bcn:
SI type = "mef":
libelle = bcn[code_formation_diplome].libelle_long
SINON:
libelle = certification.libelle_long
libelle_ancien = null
cfd_courant = certification
TANT QUE cfd_courant.ancien_diplome.length == 1:
ancien = bcn[cfd_courant.ancien_diplome[0]]
ancien_libelle = ancien.libelle_long
SI NOT normalize(libelle).includes(normalize(ancien_libelle)):
libelle_ancien = ancien_libelle
BREAK
cfd_courant = ancien
Update certification:
libelle_long = libelle
libelle_long_ancien = libelle_ancien
importBCNFamilleMetier
Description : Association des diplômes aux familles de métiers pour le regroupement par secteur professionnel (secondes communes).
- Source Externe : API BCN (N_GROUPE_FORMATION, N_LIEN_FORMATION_GROUPE)
- Collections MongoDB Sources :
bcn,bcnMef - Collections MongoDB de Sortie :
bcn(mise à jour)
Structure des données source
N_GROUPE_FORMATION (familles de métiers) :
{
groupe_formation: "G0003",
libelle_long: "Métiers de la construction durable, du bâtiment et des travaux publics"
}
N_LIEN_FORMATION_GROUPE (liens CFD → famille) :
{
formation_diplome: "40025214", // CFD
groupe_formation: "G0003" // Code famille
}
Règles métier
- Propagation au continuum :
- Si CFD A appartient à la famille X, tous ses ancêtres et descendants dans la chaîne 1-to-1 héritent de X
- Pas de propagation si branchement (plusieurs anciens ou nouveaux)
- Flag
isAnneeCommune: ```javascript isAnneeCommune = true SI:- Tous les MEF du CFD ont annee_dispositif = “1”
- ET il n’existe pas de MEF avec annee_dispositif = “2” ou “3” ```
Traitements effectués
1. Récupérer N_GROUPE_FORMATION → Map<code, { code, libelle }>
2. Récupérer N_LIEN_FORMATION_GROUPE → Map<cfd, code_famille>
3. Pour chaque lien (cfd, famille):
a. Récupérer tous les CFD du continuum via cfdsParentAndChildren(cfd)
(uniquement relations 1:1)
b. Pour chaque cfd_continuum:
- Vérifier isAnneeCommune via bcnMef:
mefsAnnee1 = bcnMef.find({ formation_diplome: cfd, annee_dispositif: "1" })
mefsAutres = bcnMef.find({ formation_diplome: cfd, annee_dispositif: { $ne: "1" } })
isAnneeCommune = mefsAnnee1.length > 0 && mefsAutres.length == 0
- Update bcn où code_certification = cfd_continuum:
familleMetier: {
code: famille.code,
libelle: famille.libelle,
isAnneeCommune: isAnneeCommune
}
Fonction cfdsParentAndChildren(cfd)
Retourne tous les CFD liés par continuum 1:1:
function cfdsParentAndChildren(cfd):
result = [cfd]
// Remonter les ancêtres
current = cfd
TANT QUE bcn[current].ancien_diplome.length == 1:
ancien = bcn[current].ancien_diplome[0]
SI bcn[ancien].nouveau_diplome.length == 1:
result.push(ancien)
current = ancien
SINON:
BREAK // Branchement détecté
// Descendre les descendants
current = cfd
TANT QUE bcn[current].nouveau_diplome.length == 1:
nouveau = bcn[current].nouveau_diplome[0]
SI bcn[nouveau].ancien_diplome.length == 1:
result.push(nouveau)
current = nouveau
SINON:
BREAK // Branchement détecté
RETURN result
2. Établissements
Vue d’ensemble
Le fichier ACCE (Annuaire des Établissements de l’Éducation Nationale) contient l’ensemble des établissements d’enseignement français avec leurs caractéristiques administratives, géographiques et de contact.
Source de données
| Propriété | Valeur |
|---|---|
| Fichier | data/acce_etablissements.csv |
| Encodage | ISO-8859-1 |
| Délimiteur | Point-virgule (;) |
| Volume | ~154 000 établissements |
Format du code UAI
UAI (Unité Administrative Immatriculée) - 8 caractères
Exemple : 0010001W
Collection MongoDB
| Collection | Clé unique | Description |
|---|---|---|
acceEtablissements | numero_uai | Annuaire complet des établissements |
importEtablissements
Description : Import de l’annuaire des établissements (UAI) depuis le fichier ACCE.
- Source Externe : Fichier CSV ACCE (encodage ISO-8859-1, délimiteur point-virgule)
- Collections MongoDB Sources : -
- Collections MongoDB de Sortie :
acceEtablissements
Clés et identifiants
| Champ | Source CSV | Description |
|---|---|---|
numero_uai | numero_uai | Clé unique, 8 caractères (ex: 0010001W) |
Colonnes du fichier CSV (62 colonnes)
Identifiants et classification :
numero_uai, nature_uai, nature_uai_libe, type_uai, type_uai_libe,
etat_etablissement, etat_etablissement_libe
Tutelle et secteur :
ministere_tutelle, ministere_tutelle_libe, tutelle_2, tutelle_2_libe,
secteur_public_prive, secteur_public_prive_libe
Identités :
sigle_uai, denomination_principale, appellation_officielle, patronyme_uai
Juridique et financier :
categorie_juridique, categorie_juridique_libe, contrat_etablissement,
contrat_etablissement_libe, categorie_financiere, categorie_financiere_libe,
situation_comptable, situation_comptable_libe
Hiérarchie et localisation :
niveau_uai, niveau_uai_libe, commune, commune_libe, academie, academie_libe,
pays, pays_libe, departement_insee_3, departement_insee_3_libe
Adresse :
lieu_dit_uai, adresse_uai, boite_postale_uai, code_postal_uai,
etat_sirad_uai, localite_acheminement_uai, pays_etranger_acheminement
Contact :
numero_telephone_uai, numero_telecopieur_uai, mention_distribution,
mel_uai, site_web
Géolocalisation :
coordonnee_x, coordonnee_y, appariement, appariement_complement,
localisation, localisation_complement, date_geolocalisation, source
Dates :
date_ouverture, date_fermeture, date_derniere_mise_a_jour
Autres :
hebergement_etablissement, hebergement_etablissement_libe,
numero_siren_siret_uai, numero_finess_uai
Types d’établissements éligibles InserJeunes
La constante NATURE_UAI_ETABLISSEMENTS_INSERJEUNES définit les 40 codes nature_uai éligibles :
| Code | Libellé |
|---|---|
| Établissements spécialisés | |
| 240 | Institut médico-éducatif (IME) |
| 241 | ITEP / DITEP |
| 242 | Institut d’éducation motrice (IEM) |
| 261 | Maison d’enfants à caractère social |
| Lycées | |
| 300 | Lycée d’enseignement général et technologique |
| 301 | Lycée d’enseignement technologique |
| 302 | Lycée d’enseignement général |
| 306 | Lycée polyvalent |
| 307 | Lycée agricole |
| 310 | Lycée climatique |
| 315 | Lycée expérimental |
| 320 | Lycée professionnel |
| 332 | École professionnelle spécialisée handicap |
| 334 | Section d’enseignement professionnel |
| 370 | EREA / LEA |
| 380 | Maison familiale rurale |
| Enseignement supérieur court | |
| 400 | STS et/ou CPGE |
| 430 | École de formation sanitaire et sociale |
| 440 | École technico-professionnelle services |
| 445 | École de commerce, gestion, comptabilité |
| 450 | École de formation artistique |
| 480 | École technico-professionnelle production |
| Enseignement supérieur | |
| 506 | Centre régional CNAM |
| 523 | Université |
| 528 | Service formation continue |
| 540 | Composante université avec formation |
| 551 | Université de technologie |
| 580 | École d’ingénieurs |
| Apprentissage | |
| 600 | CFA (convention régionale) |
| 605 | Organisme de formation - CFA |
| 610 | CFA (convention nationale) |
| 625 | Annexe organisme de formation - CFA |
| 630 | Section d’apprentissage |
| Formation continue | |
| 700 | Établissement de formation continue |
| 710 | GRETA |
| 720 | Centre d’enseignement à distance |
| 730 | Établissement formation métiers du sport |
| 740 | Centre de formation professionnelle agricole |
| 830 | GIP formation continue et insertion |
Règles métier
-
Clé unique :
numero_uai(index unique MongoDB) -
Conversion des dates du format DD/MM/YYYY vers Date JS :
// Timezone Europe/Paris pour respecter les dates françaises moment.tz(dateStr, "DD/MM/YYYY", "Europe/Paris").toDate();Champs convertis :
date_ouverture,date_fermeture,date_derniere_mise_a_jour,date_geolocalisation -
Nettoyage des valeurs :
- Suppression des champs
nullouundefined - Suppression des chaînes vides
- Suppression des champs
Traitements effectués
1. Lecture du fichier CSV:
a. Ouvrir stream fichier: data/acce_etablissements.csv
b. Décoder ISO-8859-1 → UTF-8 (via iconv-lite)
c. Parser CSV avec délimiteur ";"
2. Pour chaque ligne:
a. Extraire les 62 colonnes
b. Convertir les dates:
- date_ouverture: DD/MM/YYYY → Date (Europe/Paris)
- date_fermeture: DD/MM/YYYY → Date (Europe/Paris)
- date_derniere_mise_a_jour: DD/MM/YYYY → Date (Europe/Paris)
- date_geolocalisation: DD/MM/YYYY → Date (Europe/Paris)
c. Nettoyer: supprimer champs null/undefined/vides (omitNil)
d. Préparer upsert:
- $setOnInsert: { _meta.created_on: now, _meta.date_import: now }
- $set: { ...data, _meta.updated_on: now }
e. Upsert dans acceEtablissements (clé: numero_uai)
3. Retourner stats:
{ total: N, created: X, updated: Y, failed: Z }
- Parallélisation : 10 documents traités simultanément
- Pattern : Stream processing (oleoduc)
Schéma MongoDB
{
// Clé unique
numero_uai: String, // REQUIRED, ex: "0010001W"
// Classification
nature_uai: String, // Code nature (ex: "320")
nature_uai_libe: String, // Libellé (ex: "Lycée professionnel")
type_uai: String,
type_uai_libe: String,
etat_etablissement: String, // "1" = ouvert, "2" = fermé
etat_etablissement_libe: String,
// Tutelle
ministere_tutelle: String,
ministere_tutelle_libe: String,
tutelle_2: String,
tutelle_2_libe: String,
secteur_public_prive: String, // "PU" ou "PR"
secteur_public_prive_libe: String,
// Identités
sigle_uai: String,
denomination_principale: String,
appellation_officielle: String, // Nom complet
patronyme_uai: String,
// Localisation
academie: String, // Code académie
academie_libe: String, // Libellé académie
departement_insee_3: String, // Code département (3 chars)
departement_insee_3_libe: String,
commune: String, // Code commune INSEE
commune_libe: String,
code_postal_uai: String,
adresse_uai: String,
// Coordonnées géographiques (Lambert 93)
coordonnee_x: String,
coordonnee_y: String,
// Identifiants externes
numero_siren_siret_uai: String, // SIRET
numero_finess_uai: String, // FINESS (sanitaire)
// Dates (converties en Date)
date_ouverture: Date,
date_fermeture: Date,
date_derniere_mise_a_jour: Date,
date_geolocalisation: Date,
// Contact
numero_telephone_uai: String,
mel_uai: String,
site_web: String,
// Métadonnées
_meta: {
created_on: Date,
updated_on: Date,
date_import: Date // REQUIRED
}
}
Utilisation dans le pipeline
Les données acceEtablissements sont utilisées par :
importFormationsStats: Filtrage des établissements éligibles vianature_uaiimportFormationsSupStats: Récupération des informations établissement par UAI- Routes API formations : Validation de l’existence d’un UAI
Requête type pour établissements éligibles :
AcceEtablissementRepository.find({
nature_uai: { $in: NATURE_UAI_ETABLISSEMENTS_INSERJEUNES },
pays_libe: "France",
});
3. Statistiques InserJeunes
Vue d’ensemble
InserJeunes est un dispositif de la DEPP (Direction de l’évaluation, de la prospective et de la performance) qui mesure l’insertion professionnelle des jeunes sortant de formations professionnelles et d’apprentissage. Les données sont collectées à différentes granularités : nationale (certifications), régionale, et par établissement (formations).
API InserJeunes
| Propriété | Valeur |
|---|---|
| URL de base | https://www.inserjeunes.education.gouv.fr/api/v1.0 |
| Documentation | https://www.inserjeunes.education.gouv.fr/api/docs/ |
| Authentification | Bearer Token (login/password via headers) |
| Rate limiting | 5 requêtes/seconde |
| Token timeout | 120 minutes |
| Retry | 5 tentatives avec backoff exponentiel |
Endpoints utilisés :
| Endpoint | Paramètres | Usage |
|---|---|---|
POST /login | headers: username, password | Authentification |
GET /france/millesime/{millesime}/filiere/{filiere} | millesime, filiere | Stats nationales |
GET /UAI/{uai}/millesime/{millesime} | uai, millesime | Stats établissement |
GET /region/{code_region_academique}/millesime/{millesime} | code_region, millesime | Stats régionales |
Format des millésimes
| Type | Format | Exemple | Usage |
|---|---|---|---|
| Simple | YYYY | "2024" | Certifications nationales |
| Double | YYYY_YYYY | "2023_2024" | Formations, Régionales |
Conversion :
// Année simple → Année double
millesime = "2024" → "2023_2024"
// Extraction depuis année double
"2023_2024" → année = "2024"
Filières
| Code API | Filière interne | Description |
|---|---|---|
apprentissage | apprentissage | Voie apprentissage |
voie_pro_sco_educ_nat | pro | Voie professionnelle scolaire |
voie_pro_sco_agri | agricole | Voie professionnelle agricole |
Collections MongoDB
| Collection | Clé unique | Granularité |
|---|---|---|
certificationsStats | millesime + code_certification + filiere | Nationale |
formationsStats | uai + code_certification + millesime + filiere | Établissement |
regionalesStats | region.code + code_certification + millesime + filiere | Régionale |
Structure des statistiques
Effectifs (nombres absolus) : | Champ | Description | |——-|————-| | nb_annee_term | Effectifs en année terminale | | nb_sortant | Sortants (calculé si absent) | | nb_poursuite_etudes | En poursuite d’études | | nb_en_emploi_6_mois | En emploi à 6 mois | | nb_en_emploi_12_mois | En emploi à 12 mois | | nb_en_emploi_18_mois | En emploi à 18 mois | | nb_en_emploi_24_mois | En emploi à 24 mois |
Taux (pourcentages) : | Champ | Description | |——-|————-| | taux_en_emploi_6_mois | Taux d’emploi à 6 mois | | taux_en_emploi_12_mois | Taux d’emploi à 12 mois (calculé) | | taux_en_emploi_18_mois | Taux d’emploi à 18 mois (calculé) | | taux_en_emploi_24_mois | Taux d’emploi à 24 mois (calculé) | | taux_en_formation | Taux en poursuite d’études | | taux_rupture_contrats | Taux de rupture (apprentissage) | | taux_autres_6_mois | Autres situations à 6 mois | | taux_autres_12_mois | Autres situations à 12 mois (calculé) | | taux_autres_18_mois | Autres situations à 18 mois (calculé) | | taux_autres_24_mois | Autres situations à 24 mois (calculé) |
Salaires (certifications uniquement) : | Champ | Description | |——-|————-| | salaire_12_mois_q1 | 1er quartile à 12 mois | | salaire_12_mois_q2 | Médiane à 12 mois | | salaire_12_mois_q3 | 3e quartile à 12 mois |
Système donnee_source
Le champ donnee_source identifie l’origine des données :
| Type | Description | Créé par |
|---|---|---|
"self" | Données directement importées d’InserJeunes | importStats |
"ancienne" | Données héritées d’un diplôme plus ancien | computeContinuumStats |
"nouvelle" | Données héritées d’un diplôme plus récent | computeContinuumStats |
donnee_source: {
code_certification: "40025214", // CFD source
type: "self" | "ancienne" | "nouvelle"
}
importStats
Description : Orchestrateur de l’import des statistiques d’insertion pour l’enseignement secondaire depuis l’API InserJeunes.
- Source Externe : API InserJeunes
- Collections MongoDB Sources :
acceEtablissements(pour les formations) - Collections MongoDB de Sortie :
certificationsStats,formationsStats,regionalesStats
Sous-jobs exécutés
L’orchestrateur appelle séquentiellement :
importCertificationsStats- Stats nationalesimportFormationsStats- Stats par établissementimportRegionalesStats- Stats régionales
Traitements effectués
1. Authentification API InserJeunes
- POST /login avec headers username/password
- Récupération Bearer Token
2. Import Certifications (niveau national):
Pour chaque millesime dans config.millesimes.default:
Pour chaque filiere dans ["apprentissage", "voie_pro_sco_educ_nat"]:
- GET /france/millesime/{millesime}/filiere/{filiere}
- Transformer et grouper par code_certification
- Upsert dans certificationsStats
3. Import Formations (niveau établissement):
Pour chaque etablissement dans acceEtablissements (filtré par nature_uai):
Pour chaque millesime dans config.millesimes.formations:
- GET /UAI/{uai}/millesime/{millesime}
- Enrichir avec région/académie
- Transformer et grouper par code_certification + filiere
- Upsert dans formationsStats
4. Import Régionales:
Pour chaque region (sauf codes 00 et 13):
Pour chaque millesime dans config.millesimes.formations:
- GET /region/{code_region_academique}/millesime/{millesime}
- Transformer et grouper par code_certification + filiere
- Upsert dans regionalesStats
5. Retourner les compteurs de chaque sous-job:
{
certifications: { created, updated, failed },
formations: { created, updated, failed, ignored },
regionales: { created, updated, failed }
}
importCertificationsStats
Description : Import des statistiques nationales par certification.
- Source Externe : API InserJeunes (endpoint
/france) - Collections MongoDB Sources :
bcn - Collections MongoDB de Sortie :
certificationsStats
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
millesime | Paramètre API | Année (ex: “2024”) |
code_certification | id_formation_apprentissage ou id_mefstat11 | CFD ou MEF11 |
filiere | Déduit de la réponse | apprentissage ou pro |
code_certification_type | Calculé | cfd ou mef11 |
Identification de la filière depuis l’API
// Dans la réponse API, chaque mesure a des "dimensions"
dimensions: [
{ id_formation_apprentissage: "40025214" } // → filiere = "apprentissage", type = "cfd"
// OU
{ id_mefstat11: "24725214311" } // → filiere = "pro", type = "mef11"
]
Règles métier
-
Suppression préalable : Avant upsert, suppression des données dérivées (
donnee_source.type !== "self") - Enrichissement BCN : Pour chaque certification, récupération depuis
bcn:code_formation_diplomelibelle,libelle_anciendiplome(code, libelle)date_fermeturefamilleMetier
-
Renommage des champs API :
DEVENIR_part_autre_situation_6_mois → taux_autres_6_mois DEVENIR_part_en_emploi_6_mois → taux_en_emploi_6_mois DEVENIR_part_poursuite_etudes → taux_en_formation salaire_TS_Q1_12_mois → salaire_12_mois_q1 salaire_TS_Q2_12_mois → salaire_12_mois_q2 salaire_TS_Q3_12_mois → salaire_12_mois_q3 -
Calcul des statistiques manquantes :
// Si nb_sortant absent nb_sortant = nb_annee_term - nb_poursuite_etudes // Si 100% en poursuite d'études SI nb_poursuite_etudes == nb_annee_term: nb_en_emploi_6_mois = 0 nb_en_emploi_12_mois = 0 nb_en_emploi_18_mois = 0 nb_en_emploi_24_mois = 0 // Taux calculés taux_en_emploi_12_mois = (nb_en_emploi_12_mois / nb_annee_term) * 100 taux_en_emploi_18_mois = (nb_en_emploi_18_mois / nb_annee_term) * 100 taux_en_emploi_24_mois = (nb_en_emploi_24_mois / nb_annee_term) * 100 taux_autres_12_mois = 100 - taux_en_emploi_12_mois - taux_en_formation taux_autres_18_mois = 100 - taux_en_emploi_18_mois - taux_en_formation taux_autres_24_mois = 100 - taux_en_emploi_24_mois - taux_en_formation - Champs ignorés stockés dans
_meta.inserjeunes:taux_poursuite_etudes, taux_emploi_*_mois, DEVENIR_part_*, salaire_TS_Q*_12_mois_prec
Traitements effectués
Pour chaque (millesime, filiere):
1. GET /france/millesime/{millesime}/filiere/{filiere}
2. Stream JSON → extraire tableau "data"
3. Pour chaque mesure:
a. Extraire code_certification depuis dimensions
b. Identifier filiere (apprentissage si id_formation_apprentissage, sinon pro)
c. Grouper les mesures par (code_certification, filiere)
4. Pour chaque groupe:
a. Aplatir les mesures en objet { stat_name: value }
b. Renommer les champs selon mapping
c. Calculer les stats manquantes
d. Séparer stats valides / ignorées
e. Enrichir avec données BCN
f. Supprimer données dérivées existantes
g. Upsert avec donnee_source.type = "self"
Parallélisation: 4 requêtes API, 10 écritures MongoDB
importFormationsStats
Description : Import des statistiques par établissement.
- Source Externe : API InserJeunes (endpoint
/UAI) - Collections MongoDB Sources :
acceEtablissements,bcn - Collections MongoDB de Sortie :
formationsStats
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
uai | Paramètre API | Code UAI établissement (ex: “0010001W”) |
millesime | Paramètre API | Année double (ex: “2023_2024”) |
code_certification | Réponse API | CFD ou MEF11 |
filiere | Déduit de la réponse | apprentissage ou pro |
Champs spécifiques formations
| Champ | Source | Description |
|---|---|---|
libelle_etablissement | acceEtablissements.appellation_officielle | Nom de l’établissement |
region.code | regions service | Code région INSEE |
region.nom | regions service | Nom de la région |
academie.code | acceEtablissements.academie | Code académie |
academie.nom | acceEtablissements.academie_libe | Nom académie |
Filtrage des établissements
Seuls les établissements avec nature_uai dans NATURE_UAI_ETABLISSEMENTS_INSERJEUNES sont requêtés (40 codes, voir section Établissements).
Règles métier
- Gestion des erreurs API :
- Code 400 “UAI incorrect ou agricole” → ignoré (pas d’échec)
- Autres erreurs → retry avec backoff
-
Conversion millésime :
// config.millesimes.formations = ["2019_2020", "2020_2021", ...] // Utilisé tel quel pour l'API - Enrichissement localisation :
{ region: { code: "84", // Code région INSEE nom: "Auvergne-Rhône-Alpes" }, academie: { code: "10", nom: "Lyon" } }
Traitements effectués
1. Récupérer tous les établissements éligibles:
acceEtablissements.find({
nature_uai: { $in: NATURE_UAI_ETABLISSEMENTS_INSERJEUNES },
pays_libe: "France"
})
2. Pour chaque etablissement:
a. Enrichir avec région/académie depuis services/regions.js
3. Pour chaque (etablissement, millesime):
a. GET /UAI/{uai}/millesime/{millesime}
b. Si erreur 400 "UAI incorrect" → ignorer
c. Grouper par (code_certification, filiere)
d. Pour chaque groupe:
- Enrichir avec données BCN
- Enrichir avec données établissement
- Supprimer données dérivées existantes
- Upsert avec donnee_source.type = "self"
Parallélisation: 10 requêtes API, 10 écritures MongoDB
importRegionalesStats
Description : Import des statistiques agrégées par région.
- Source Externe : API InserJeunes (endpoint
/region) - Collections MongoDB Sources :
bcn - Collections MongoDB de Sortie :
regionalesStats
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
region.code | regions service | Code région INSEE |
region.nom | regions service | Nom région |
region.code_region_academique | regions service | Code pour API InserJeunes |
millesime | Paramètre API | Année double |
code_certification | Réponse API | CFD ou MEF11 |
filiere | Déduit de la réponse | apprentissage ou pro |
Régions filtrées
Codes région académique ignorés (non gérés par InserJeunes) :
"00"- Collectivités d’outre-mer"13"- Mayotte
Traitements effectués
1. Récupérer toutes les régions (sauf codes 00 et 13)
2. Pour chaque (region, millesime):
a. GET /region/{code_region_academique}/millesime/{millesime}
b. Grouper par (code_certification, filiere)
c. Filtrer: supprimer filiere = "agricole"
d. Pour chaque groupe:
- Enrichir avec données BCN
- Enrichir avec données région
- Supprimer données dérivées existantes
- Upsert avec donnee_source.type = "self"
Parallélisation: 4 requêtes API, 10 écritures MongoDB
Schéma MongoDB certificationsStats
{
// Clés uniques
millesime: String, // "2024"
code_certification: String, // "40025214" ou "24725214311"
filiere: String, // "apprentissage" | "pro" | "agricole" | "superieur"
// Identification certification
code_certification_type: String, // "cfd" | "mef11" | "sise"
code_formation_diplome: String, // CFD parent (si MEF)
libelle: String,
libelle_ancien: String,
diplome: {
code: String,
libelle: String
},
date_fermeture: Date,
familleMetier: {
code: String,
libelle: String,
isAnneeCommune: Boolean
},
certificationsTerminales: [{
code_certification: String
}],
// Effectifs
nb_annee_term: Number,
nb_sortant: Number,
nb_poursuite_etudes: Number,
nb_en_emploi_6_mois: Number,
nb_en_emploi_12_mois: Number,
nb_en_emploi_18_mois: Number,
nb_en_emploi_24_mois: Number,
// Taux
taux_en_emploi_6_mois: Number,
taux_en_emploi_12_mois: Number,
taux_en_emploi_18_mois: Number,
taux_en_emploi_24_mois: Number,
taux_en_formation: Number,
taux_rupture_contrats: Number,
taux_autres_6_mois: Number,
taux_autres_12_mois: Number,
taux_autres_18_mois: Number,
taux_autres_24_mois: Number,
// Salaires (national uniquement)
salaire_12_mois_q1: Number,
salaire_12_mois_q2: Number,
salaire_12_mois_q3: Number,
// Source des données
donnee_source: {
code_certification: String,
type: String // "self" | "ancienne" | "nouvelle"
},
// Métadonnées
_meta: {
created_on: Date,
updated_on: Date,
date_import: Date,
inserjeunes: {
// Champs API ignorés stockés pour audit
taux_poursuite_etudes: Number,
taux_emploi_6_mois: Number,
// ...
}
}
}
Schéma MongoDB formationsStats
Hérite de tous les champs de certificationsStats plus :
{
// ... tous les champs certificationsStats ...
// Établissement
uai: String, // "0010001W"
libelle_etablissement: String,
// Classification UAI (ajouté par computeUAI)
uai_type: String, // "lieu_formation" | "formateur" | "gestionnaire" | "inconnu"
uai_donnee: String,
uai_donnee_type: String,
uai_lieu_formation: [String],
uai_formateur: [String],
uai_gestionnaire: [String],
// Localisation
region: {
code: String,
nom: String
},
academie: {
code: String,
nom: String
}
}
Schéma MongoDB regionalesStats
Hérite de tous les champs de certificationsStats plus :
{
// ... tous les champs certificationsStats ...
// Région
region: {
code: String, // Code INSEE région
nom: String,
code_region_academique: String
}
}
4. Statistiques InserSup
Vue d’ensemble
InserSup est le système d’information qui mesure l’insertion professionnelle des diplômés de l’enseignement supérieur. Les données proviennent du SIES (Sous-direction des systèmes d’information et des études statistiques) et sont exposées via deux plateformes techniques : une API OpenDataSoft pour les statistiques nationales et une API Omogen pour les statistiques par établissement.
Sources de données
| Source | Type | URL | Usage |
|---|---|---|---|
| DataEnseignementSup (ODS) | OpenDataSoft | data.enseignementsup-recherche.gouv.fr | Stats nationales |
| InserSup API (Omogen) | REST API | Configurable | Stats par établissement |
API DataEnseignementSup (Certifications nationales)
| Propriété | Valeur |
|---|---|
| Domain | https://data.enseignementsup-recherche.gouv.fr |
| Dataset | fr-esr-insersup |
| Rate limiting | 5 requêtes/seconde |
| Authentification | Aucune (API publique) |
API InserSup/Omogen (Formations par établissement)
| Propriété | Valeur |
|---|---|
| URL de base | Configurable via TRAJECTOIRES_PRO_INSERSUP_BASE_URL |
| Authentification | Header X-Omogen-Api-Key |
| Rate limiting | 5 requêtes/seconde |
| Format réponse | JSON avec tableau results |
Format des millésimes
| Type | Format | Exemple | Usage |
|---|---|---|---|
| Simple | YYYY | "2021" | Année unique (sous-seuil) |
| Double | YYYY_YYYY | "2020_2021" | Années cumulées |
Détection du format depuis les données :
// Valeur avec "**" (sous-seuil de confidentialité) → année simple
"12 **" → millesime = "2021"
// Valeur avec "*" (données cumulées) → année double
"130 *" → millesime = "2020_2021"
// Valeur sans marqueur → année simple
"54" → millesime = "2021"
Collections MongoDB
| Collection | Clé unique | Granularité |
|---|---|---|
certificationsStats | millesime + code_certification + filiere | Nationale (filiere = “superieur”) |
formationsStats | uai + code_certification + millesime + filiere | Établissement (filiere = “superieur”) |
Parsing des valeurs spéciales
Les données InserSup contiennent des marqueurs textuels :
| Marqueur | Signification | Transformation |
|---|---|---|
"nd" | Non disponible | null |
"na" | Non applicable | null |
"130 *" | Données cumulées sur 2 ans | 130 (entier) |
"12 **" | Sous seuil de confidentialité | 12 (entier) |
const formatInt = (str) =>
str.substr(0, 2) === "nd" || str.substr(0, 2) === "na" ? null : parseInt(str.replaceAll("*", ""));
Structure des statistiques InserSup
Effectifs : | Champ | Source Formations (Omogen) | Source Certifications (ODS) | Description | |——-|—————————-|—————————-|————-| | nb_annee_term | Calculé | Calculé | nb_sortant + nb_poursuite_etudes | | nb_sortant | nb_sortants | nb_sortants | Sortants du diplôme | | nb_poursuite_etudes | nb_poursuivants | nb_poursuivants | En poursuite d’études | | nb_diplome | nb_diplomes | - | Diplômés | | nb_en_emploi_6_mois | nb_in_dsn_6 | nb_sortants_en_emploi_sal_fr_6 | En emploi à 6 mois | | nb_en_emploi_12_mois | nb_in_dsn_12 | nb_sortants_en_emploi_sal_fr_12 | En emploi à 12 mois | | nb_en_emploi_18_mois | nb_in_dsn_18 | nb_sortants_en_emploi_sal_fr_18 | En emploi à 18 mois | | nb_en_emploi_24_mois | nb_in_dsn_24 | nb_sortants_en_emploi_sal_fr_24 | En emploi à 24 mois |
Salaires : | Champ | Source Formations (Omogen) | Source Certifications (ODS) | Description | |——-|—————————-|—————————-|————-| | salaire_12_mois_q1 | salaire_q1_national_diplome_12 | salaire_q1_12 | 1er quartile | | salaire_12_mois_q2 | salaire_q2_national_diplome_12 | salaire_q2_12 | Médiane | | salaire_12_mois_q3 | salaire_q3_national_diplome_12 | salaire_q3_12 | 3e quartile |
Taux calculés : | Champ | Formule | |——-|———| | taux_en_formation | (nb_poursuite_etudes / nb_annee_term) * 100 | | taux_en_emploi_6_mois | (nb_en_emploi_6_mois / nb_annee_term) * 100 | | taux_en_emploi_12_mois | (nb_en_emploi_12_mois / nb_annee_term) * 100 | | taux_en_emploi_18_mois | (nb_en_emploi_18_mois / nb_annee_term) * 100 | | taux_en_emploi_24_mois | (nb_en_emploi_24_mois / nb_annee_term) * 100 | | taux_autres_*_mois | 100 - taux_en_emploi_*_mois - taux_en_formation |
importSupStats
Description : Orchestrateur de l’import des statistiques d’insertion pour l’enseignement supérieur.
- Source Externe : API DataEnseignementSup (ODS) + API InserSup (Omogen)
- Collections MongoDB Sources :
acceEtablissements,bcnSise - Collections MongoDB de Sortie :
certificationsStats,formationsStats
Sous-jobs exécutés
L’orchestrateur appelle séquentiellement :
importCertificationsSupStats- Stats nationales par diplôme SISEimportFormationsSupStats- Stats par établissement
Traitements effectués
1. Import Certifications nationales:
Pour chaque millesime dans config.millesimes.defaultSup:
- GET DataEnseignementSup API (dataset fr-esr-insersup)
- Filtrer: nationalite/genre/obtention/regime = "ensemble"
- Transformer et enrichir avec BCN SISE
- Calculer taux dérivés
- Upsert dans certificationsStats (filiere = "superieur")
2. Import Formations par établissement:
- GET InserSup API (Omogen) - flux complet
- Pour chaque formation:
a. Parser millésime (détecter * et **)
b. Parser valeurs spéciales (nd, na)
c. Valider UAI via ACCE
d. Enrichir avec région/académie
e. Enrichir avec BCN SISE
f. Calculer taux dérivés
g. Upsert dans formationsStats (filiere = "superieur")
3. Retourner les compteurs de chaque sous-job:
{
certifications: { created, updated, failed },
formations: { created, updated, failed }
}
importCertificationsSupStats
Description : Import des statistiques nationales par diplôme SISE.
- Source Externe : API DataEnseignementSup (OpenDataSoft)
- Collections MongoDB Sources :
bcnSise - Collections MongoDB de Sortie :
certificationsStats
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
millesime | promo | Année(s) de promotion (ex: “2021” ou “2020_2021”) |
code_certification | diplome | Code SISE du diplôme |
filiere | Fixe | "superieur" |
code_certification_type | Fixe | "sise" |
Filtres appliqués sur l’API
Les données sont filtrées pour obtenir uniquement les agrégats nationaux :
{
nationalite: "ensemble",
genre: "ensemble",
obtention_diplome: "ensemble",
regime_inscription: "ensemble"
}
Règles métier
-
Construction du millésime depuis
promo:// promo = ["2021"] → millesime = "2021" // promo = ["2020", "2021"] → millesime = "2020_2021" millesime = promo.join("_"); -
Calcul de
nb_annee_term:nb_annee_term = nb_sortant + nb_poursuite_etudes; - Enrichissement BCN SISE : Pour chaque diplôme, récupération depuis
bcnSise:libelle(depuislibelle_intitule_1)diplome(code, libelle)date_fermeture
- Métadonnées stockées dans
_meta.insersup:{ type_diplome: "licence_pro", domaine_disciplinaire: "DEG", secteur_disciplinaire: "SCIENCES DE GESTION", discipline: "Sciences économiques" }
Traitements effectués
Pour chaque millesime dans config.millesimes.defaultSup:
1. GET DataEnseignementSup API:
- dataset: fr-esr-insersup
- filtre: promo contient millesime
- filtre: nationalite/genre/obtention/regime = "ensemble"
2. Pour chaque record:
a. Construire millesime depuis promo
b. Filtrer si diplome null
c. Mapper champs API → champs stats:
- nb_sortants_en_emploi_sal_fr_* → nb_en_emploi_*_mois
- nb_poursuivants → nb_poursuite_etudes
- salaire_q*_12 → salaire_12_mois_q*
d. Calculer nb_annee_term
e. Enrichir avec BCN SISE
f. Calculer taux dérivés
g. Supprimer données dérivées existantes
h. Upsert avec donnee_source.type = "self"
importFormationsSupStats
Description : Import des statistiques par établissement pour l’enseignement supérieur.
- Source Externe : API InserSup (Omogen)
- Collections MongoDB Sources :
acceEtablissements,bcnSise - Collections MongoDB de Sortie :
formationsStats
Clés et identifiants
| Champ | Source | Description |
|---|---|---|
uai | etablissement | Code UAI établissement |
millesime | Calculé depuis annee_universitaire | Format selon marqueurs |
code_certification | diplome ou diplome_consol | Code SISE |
filiere | Fixe | "superieur" |
Format de réponse API Omogen
{
"results": [
{
"diplome": "2400074",
"libelle_diplome": "MANAGEMENT ET GESTION DES ORGANISATIONS",
"type_diplome_long": "licence_pro",
"etablissement": "0062205P",
"denomination_principale": "UNIVERSITE COTE D'AZUR",
"domaine": "DEG",
"discipline": "Sciences économiques",
"secteur_disciplinaire": "SCIENCES DE GESTION",
"annee_universitaire": "2020-2021",
"nb_inscrits": "119",
"nb_diplomes": "101",
"nb_sortants": "54", // ou "130 *" ou "12 **"
"nb_poursuivants": "65",
"nb_in_dsn_6": "23",
"nb_in_dsn_12": "25",
"nb_in_dsn_18": "26",
"nb_in_dsn_24": "27",
"salaire_q1_national_diplome_12": "nd",
"salaire_q2_national_diplome_12": "nd",
"salaire_q3_national_diplome_12": "nd"
}
]
}
Règles métier
-
Détermination du millésime :
// annee_universitaire = "2020-2021" const cumulImpossible = /\*\*/.test(nb_sortants); // Sous-seuil const hasCumul = /\*/.test(nb_sortants); // Cumulé if (cumulImpossible || !hasCumul) { millesime = "2021"; // Année simple (2ème partie) } else { millesime = "2020_2021"; // Format cumulé } -
Mapping des champs :
{ uai: data.etablissement, code_certification: data.diplome || data.diplome_consol, nb_annee_term: formatInt(data.nb_inscrits), nb_diplome: formatInt(data.nb_diplomes), nb_sortant: formatInt(data.nb_sortants), nb_poursuite_etudes: formatInt(data.nb_poursuivants), nb_en_emploi_6_mois: formatInt(data.nb_in_dsn_6), nb_en_emploi_12_mois: formatInt(data.nb_in_dsn_12), nb_en_emploi_18_mois: formatInt(data.nb_in_dsn_18), nb_en_emploi_24_mois: formatInt(data.nb_in_dsn_24), salaire_12_mois_q1: formatInt(data.salaire_q1_national_diplome_12), salaire_12_mois_q2: formatInt(data.salaire_q2_national_diplome_12), salaire_12_mois_q3: formatInt(data.salaire_q3_national_diplome_12) } -
Validation UAI : L’établissement doit exister dans
acceEtablissements -
Enrichissement localisation :
{ libelle_etablissement: acce.appellation_officielle, region: { code, nom }, academie: { code, nom } } -
Métadonnées stockées dans
_meta.insersup:{ etablissement_libelle: data.denomination_principale, etablissement_actuel_libelle: data.denomination_actuel_principale, type_diplome: data.type_diplome_long, domaine_disciplinaire: data.domaine, secteur_disciplinaire: data.secteur_disciplinaire, discipline: data.discipline }
Traitements effectués
1. GET InserSup API (Omogen) - flux complet JSON
2. Stream JSON → extraire tableau "results"
3. Pour chaque formation:
a. Parser millésime depuis annee_universitaire et marqueurs
b. Parser valeurs avec formatInt (nd, na, *, **)
c. Filtrer si millesime non dans config.millesimes.formationsSup
d. Valider UAI existe dans ACCE
e. Récupérer région/académie
f. Enrichir avec BCN SISE (getCertificationSupInfo)
g. Calculer taux dérivés
h. Supprimer données dérivées existantes
i. Upsert avec donnee_source.type = "self"
Parallélisation: 10 écritures MongoDB
Schéma MongoDB pour InserSup
Les données InserSup utilisent les mêmes collections que InserJeunes (certificationsStats, formationsStats) avec filiere = "superieur".
Champs spécifiques au supérieur :
{
// Identification
code_certification_type: "sise", // Toujours "sise" pour le supérieur
filiere: "superieur",
// Effectif supplémentaire
nb_diplome: Number, // Nombre de diplômés (spécifique sup)
// Métadonnées InserSup
_meta: {
insersup: {
type_diplome: String, // "licence_pro", "master", etc.
domaine_disciplinaire: String, // "DEG", "SHS", etc.
secteur_disciplinaire: String,
discipline: String,
// Pour formations uniquement:
etablissement_libelle: String,
etablissement_actuel_libelle: String
}
}
}
Note : Le continuum diplôme (computeContinuumStats) n’est pas appliqué aux données du supérieur (filiere !== "superieur" est filtré).
5. Calcul du continuum stats
Vue d’ensemble
Le continuum diplôme représente les chaînes de succession entre diplômes (ancien → nouveau). Ce job propage les statistiques d’insertion le long de ces chaînes pour permettre l’affichage de données sur des diplômes qui n’ont pas encore leurs propres statistiques (nouveaux diplômes) ou qui n’en ont plus (anciens diplômes fermés).
Concept de donnee_source
Le champ donnee_source indique l’origine des statistiques :
| Type | Description | Priorité |
|---|---|---|
self | Données propres au diplôme (issues d’InserJeunes) | Haute - jamais écrasées |
ancienne | Données dérivées d’un ancien diplôme | Basse - créées si pas de self |
nouvelle | Données dérivées d’un nouveau diplôme | Basse - créées si pas de self |
Structure du champ :
donnee_source: {
type: "self" | "ancienne" | "nouvelle",
code_certification: "CFD_source" // CFD du diplôme source (si dérivé)
}
Règle de propagation 1-to-1
CRITIQUE : La propagation ne se fait que pour les relations 1-to-1 :
- Un diplôme avec plusieurs anciens diplômes → pas de propagation vers lui depuis les anciens
- Un diplôme avec plusieurs nouveaux diplômes → pas de propagation vers eux depuis lui
Ceci évite l’ambiguïté : si un diplôme a 3 prédécesseurs, lequel utiliser pour les stats ?
computeContinuumStats
Description : Propagation des statistiques à travers le continuum diplôme pour créer des records synthétiques sur les anciens/nouveaux diplômes.
- Source Externe : -
- Collections MongoDB Sources :
bcn,certificationsStats,formationsStats,regionalesStats - Collections MongoDB de Sortie : Mêmes collections stats (ajout de records synthétiques)
Clés et identifiants
| Champ | Description | Utilisation |
|---|---|---|
code_certification | CFD du diplôme | Clé de jointure avec BCN |
ancien_diplome[] | Liste des CFD prédécesseurs | Navigation vers ancêtres |
nouveau_diplome[] | Liste des CFD successeurs | Navigation vers descendants |
millesime | Année des stats | Propagé tel quel |
filiere | Type de formation | Exclure “superieur” |
Règles métier détaillées
- Exclusion du supérieur :
filiere !== "superieur"(InserSup a son propre système) - Données sources uniquement : Ne propage que les stats avec
donnee_source.type: "self" - Pas d’écrasement : Si le diplôme cible a déjà des données (
selfou dérivées), pas de création - Récursivité : Parcours complet de la chaîne (diplôme → parent → grand-parent → …)
Algorithme de propagation
FONCTION computeContinuumStats():
POUR CHAQUE collection DANS [certificationsStats, formationsStats, regionalesStats]:
// Phase 1: Propager vers les NOUVEAUX diplômes (ancienne → nouvelle)
statsOriginales = collection.find({ "donnee_source.type": "self", filiere: { $ne: "superieur" } })
POUR CHAQUE stat DANS statsOriginales:
cfd = stat.code_certification
diplomeBCN = bcn.findOne({ code_certification: cfd })
SI diplomeBCN.nouveau_diplome.length > 0:
// Parcours récursif des descendants
propagerVersDescendants(stat, diplomeBCN.nouveau_diplome, collection, "nouvelle")
// Phase 2: Propager vers les ANCIENS diplômes (nouvelle → ancienne)
POUR CHAQUE stat DANS statsOriginales:
cfd = stat.code_certification
diplomeBCN = bcn.findOne({ code_certification: cfd })
SI diplomeBCN.ancien_diplome.length > 0:
// Parcours récursif des ancêtres
propagerVersAncetres(stat, diplomeBCN.ancien_diplome, collection, "ancienne")
FONCTION propagerVersDescendants(statSource, nouveauxDiplomes, collection, type):
POUR CHAQUE nouveauCfd DANS nouveauxDiplomes:
nouveauDiplome = bcn.findOne({ code_certification: nouveauCfd })
// Condition 1-to-1 : le nouveau diplôme n'a qu'un seul ancien
SI nouveauDiplome.ancien_diplome.length != 1:
CONTINUER // Skip - relation N-to-1
// Vérifier si données existantes
existant = collection.findOne({
code_certification: nouveauCfd,
millesime: statSource.millesime,
...autresCritères
})
SI existant EST NULL:
// Créer record dérivé
nouveauRecord = copierStatsSansIdentifiants(statSource)
nouveauRecord.code_certification = nouveauCfd
nouveauRecord.donnee_source = { type: "nouvelle", code_certification: statSource.code_certification }
collection.insertOne(nouveauRecord)
// Récursion vers les descendants du descendant
SI nouveauDiplome.nouveau_diplome.length > 0:
propagerVersDescendants(statSource, nouveauDiplome.nouveau_diplome, collection, type)
FONCTION propagerVersAncetres(statSource, anciensDiplomes, collection, type):
POUR CHAQUE ancienCfd DANS anciensDiplomes:
ancienDiplome = bcn.findOne({ code_certification: ancienCfd })
// Condition 1-to-1 : l'ancien diplôme n'a qu'un seul nouveau
SI ancienDiplome.nouveau_diplome.length != 1:
CONTINUER // Skip - relation 1-to-N
// Vérifier si données existantes
existant = collection.findOne({
code_certification: ancienCfd,
millesime: statSource.millesime,
...autresCritères
})
SI existant EST NULL:
// Créer record dérivé
nouveauRecord = copierStatsSansIdentifiants(statSource)
nouveauRecord.code_certification = ancienCfd
nouveauRecord.donnee_source = { type: "ancienne", code_certification: statSource.code_certification }
collection.insertOne(nouveauRecord)
// Récursion vers les ancêtres de l'ancêtre
SI ancienDiplome.ancien_diplome.length > 0:
propagerVersAncetres(statSource, ancienDiplome.ancien_diplome, collection, type)
FONCTION copierStatsSansIdentifiants(stat):
// Copier toutes les valeurs stats mais pas les identifiants
EXCLURE: _id, code_certification, uai, region, ...
GARDER: nb_annee_term, nb_poursuite_etudes, nb_en_emploi_*,
taux_en_formation, taux_en_emploi_*, taux_autres_*,
part_en_emploi_*, salaire_*, ...
RETOURNER copie
Exemple de propagation
Chaîne de continuum BCN:
40025106 (fermé 2018) → 40025214 (actif) → 40025315 (nouveau 2024)
(BAC PRO ancien) (BAC PRO actuel) (BAC PRO rénové)
Données InserJeunes disponibles:
40025214: millesime 2023, taux_emploi_6_mois: 75%
Après computeContinuumStats:
40025106: millesime 2023, taux_emploi_6_mois: 75%, donnee_source: { type: "ancienne", code_certification: "40025214" }
40025214: millesime 2023, taux_emploi_6_mois: 75%, donnee_source: { type: "self" }
40025315: millesime 2023, taux_emploi_6_mois: 75%, donnee_source: { type: "nouvelle", code_certification: "40025214" }
6. Années non terminales
Vue d’ensemble
Les formations professionnelles et agricoles durent généralement 3 ans (seconde, première, terminale). InserJeunes ne fournit des statistiques que pour l’année terminale. Ce module crée des records “virtuels” pour les années 1 et 2, liés à la certification terminale correspondante.
Structure du code MEF STAT 11
Le code MEF STAT 11 encode l’année dans sa structure :
Position: 1 2 3 4 5 6 7 8 9 10 11
Exemple: 2 4 7 3 1 2 1 1 A 1 1
^
|
Position 4 (index 3) = année du dispositif (1, 2 ou 3)
Valeurs de l’année (position 4) :
1= Première année (seconde pro)2= Deuxième année (première pro)3= Troisième année (terminale pro) - seule année avec stats InserJeunes
importAnneesNonTerminales
Description : Création des stats pour les années non terminales (années 1-2 des parcours 3 ans) liées aux certifications terminales.
- Source Externe : -
- Collections MongoDB Sources :
bcnMef,certificationsStats,formationsStats,regionalesStats - Collections MongoDB de Sortie : Mêmes collections stats
Clés et identifiants
| Champ | Description | Utilisation |
|---|---|---|
code_certification | MEF STAT 11 | Identifiant unique avec année encodée |
code_formation_diplome | CFD du diplôme | Lien vers BCN |
annee_dispositif | Année (1, 2 ou 3) | Extraite de position 3 du MEF |
certificationsTerminales[] | Liste des MEF terminales | Référence vers stats sources |
Règles métier détaillées
- Filières cibles :
proetagricoleuniquement (apprentissage exclu) - Source des stats : Uniquement les records avec
donnee_source.type: "self"et année = 3 - Construction itérative : Pour une terminale (année 3), créer les records année 2 puis année 1
- Résolution de certification :
- Méthode 1 :
getCertificationInfo(previousMef)avec MEF reconstruit - Méthode 2 (fallback) : Lookup
bcnMefparcode_formation_diplome+annee_dispositif
- Méthode 1 :
Algorithme de construction des MEF
FONCTION importAnneesNonTerminales():
POUR CHAQUE collection DANS [certificationsStats, formationsStats, regionalesStats]:
// Récupérer toutes les stats terminales (année 3)
statsTerminales = collection.find({
"donnee_source.type": "self",
filiere: { $in: ["pro", "agricole"] }
})
POUR CHAQUE stat DANS statsTerminales:
mefTerminal = stat.code_certification // ex: "24731211A11"
anneeTerminale = extraireAnnee(mefTerminal) // Position 3 → "3"
SI anneeTerminale != "3":
CONTINUER // Pas une terminale
// Itérer de année-1 jusqu'à année 1
POUR annee DE (anneeTerminale - 1) JUSQU'À 1:
mefAnnee = construireMEFAnnee(mefTerminal, annee)
// Résolution de la certification pour cette année
certifInfo = getCertificationInfo(mefAnnee)
SI certifInfo EST NULL:
// Fallback : lookup BCN
certifInfo = bcnMef.findOne({
code_formation_diplome: stat.code_formation_diplome,
annee_dispositif: annee.toString()
})
SI certifInfo:
// Créer ou mettre à jour le record
collection.updateOne(
{ code_certification: mefAnnee, millesime: stat.millesime, ... },
{
$set: { ...donnéesCertification },
$addToSet: { certificationsTerminales: mefTerminal }
},
{ upsert: true }
)
FONCTION extraireAnnee(mef):
// Position 3 (index 2) du MEF STAT 11
RETOURNER mef[2]
FONCTION construireMEFAnnee(mefOriginal, nouvelleAnnee):
// Remplacer le caractère à la position 3
prefixe = mefOriginal.substring(0, 2) // "24"
suffixe = mefOriginal.substring(3) // "31211A11"
RETOURNER prefixe + nouvelleAnnee + suffixe // "24" + "2" + "31211A11" = "24231211A11"
Exemple de création
Stat terminale existante:
code_certification: "24731211A11" (année 3 - terminale)
code_formation_diplome: "40025214"
millesime: "2023"
taux_emploi_6_mois: 75%
Après importAnneesNonTerminales:
Record année 2 créé:
code_certification: "24231211A11" (année 2 - première)
code_formation_diplome: "40025214"
millesime: "2023"
certificationsTerminales: ["24731211A11"]
// Pas de taux - juste le lien vers la terminale
Record année 1 créé:
code_certification: "24131211A11" (année 1 - seconde)
code_formation_diplome: "40025214"
millesime: "2023"
certificationsTerminales: ["24731211A11"]
importSecondeCommune
Description : Agrégation des stats pour les secondes communes (première année partagée entre plusieurs spécialisations) en filières pro et agricole.
- Source Externe : API BCN (
getFamilleMetier) - Collections MongoDB Sources :
certificationsStats,formationsStats,regionalesStats - Collections MongoDB de Sortie : Mêmes collections stats
Concept de seconde commune
Depuis la réforme du BAC PRO, certaines secondes sont “communes” à plusieurs spécialités. Par exemple, la seconde “Métiers de la relation client” peut mener à :
- BAC PRO Commerce
- BAC PRO Vente
- BAC PRO Accueil
Ces secondes sont regroupées en familles de métiers.
Clés et identifiants
| Champ | Description | Utilisation |
|---|---|---|
familleMetier.code | Code de la famille | Identifiant unique de regroupement |
familleMetier.isAnneeCommune | Boolean | true si seconde commune |
certificationsTerminales[] | Liste des MEF terminales | Toutes les terminales de la famille |
Règles métier détaillées
- Filtre famille : Uniquement celles avec
isAnneeCommune: true - Filières :
proetagricole(pas apprentissage car CFA n’a pas de seconde commune) - Séparation : Une famille peut avoir des versions pro ET agricole séparées
- Agrégation : Le record de seconde commune référence TOUTES les terminales de la famille
Algorithme
FONCTION importSecondeCommune():
// Récupérer les familles de métiers avec année commune
familles = getFamilleMetier() // API BCN
famillesCommunes = familles.filter(f => f.isAnneeCommune === true)
POUR CHAQUE collection DANS [certificationsStats, formationsStats, regionalesStats]:
POUR CHAQUE famille DANS famillesCommunes:
// Séparer pro et agricole
POUR CHAQUE filiere DANS ["pro", "agricole"]:
// Trouver toutes les terminales de cette famille avec stats
terminales = collection.find({
"familleMetier.code": famille.code,
filiere: filiere,
"donnee_source.type": "self"
})
SI terminales.length == 0:
CONTINUER
// Extraire les codes MEF des terminales
mefTerminales = terminales.map(t => t.code_certification)
// Trouver ou créer le MEF de la seconde commune
mefSecondeCommune = getMEFSecondeCommune(famille, filiere)
// Créer le record de seconde commune
collection.updateOne(
{
code_certification: mefSecondeCommune,
filiere: filiere,
...autresCritères
},
{
$set: {
familleMetier: famille,
filiere: filiere
},
$addToSet: {
certificationsTerminales: { $each: mefTerminales }
}
},
{ upsert: true }
)
Exemple de seconde commune
Famille de métiers: "Métiers de la relation client"
code: "MRC"
isAnneeCommune: true
Terminales trouvées avec stats:
- "24731211A11" (BAC PRO Commerce, filiere: "pro")
- "24731212B22" (BAC PRO Vente, filiere: "pro")
- "24731213C33" (BAC PRO Accueil, filiere: "pro")
Record seconde commune créé:
code_certification: "24131210MRC" // MEF seconde commune
filiere: "pro"
familleMetier: { code: "MRC", isAnneeCommune: true, ... }
certificationsTerminales: ["24731211A11", "24731212B22", "24731213C33"]
7. Catalogue Apprentissage
Vue d’ensemble
Le Catalogue de l’Apprentissage est une base de données nationale référençant toutes les formations en apprentissage. Il fournit les informations cruciales pour la chaîne UAI : qui est le lieu de formation (où l’apprenti suit les cours), le formateur (CFA qui dispense), et le gestionnaire (organisme administratif responsable).
Configuration API
| Paramètre | Valeur |
|---|---|
| URL de base | https://catalogue.apprentissage.education.gouv.fr/api/v1 |
| Endpoint formations | /entity/formations |
| Authentification | Cookie session (login/password) |
| Rate limiting | 5 requêtes par seconde |
| Expiration token | 6 heures |
| Pagination | 100 records par page |
importCatalogueApprentissage
Description : Import des formations en apprentissage depuis le Catalogue de l’Apprentissage pour lier les UAI aux lieux physiques de formation.
- Source Externe : API Catalogue de l’Apprentissage (Ministère de l’Éducation)
- Collections MongoDB Sources : -
- Collections MongoDB de Sortie :
CAFormations
Clés et identifiants
| Champ | Type | Description |
|---|---|---|
id | ObjectId | Identifiant unique de la formation (clé primaire) |
cfd | String | Code Formation Diplôme - lien vers BCN |
uai_formation | String | UAI du lieu physique de formation |
etablissement_formateur_uai | String | UAI du CFA formateur |
etablissement_gestionnaire_uai | String | UAI de l’organisme gestionnaire |
Structure des UAI dans le Catalogue
┌─────────────────────────────────────────────────────────────────┐
│ GESTIONNAIRE │
│ (Organisme administratif) │
│ UAI: 0751234A │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ FORMATEUR 1 │ │ FORMATEUR 2 │ │
│ │ (CFA dispensant) │ │ (CFA dispensant) │ │
│ │ UAI: 0752345B │ │ UAI: 0753456C │ │
│ │ │ │ │ │
│ │ ┌────┐ ┌────┐ │ │ ┌────┐ │ │
│ │ │Lieu│ │Lieu│ │ │ │Lieu│ │ │
│ │ │ 1 │ │ 2 │ │ │ │ 3 │ │ │
│ │ │UAI:│ │UAI:│ │ │ │UAI:│ │ │
│ │ │075 │ │075 │ │ │ │075 │ │ │
│ │ │456D│ │567E│ │ │ │678F│ │ │
│ │ └────┘ └────┘ │ │ └────┘ │ │
│ └──────────────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Requête API
// Filtre de requête
{
"$or": [
{ "published": true },
{ "catalogue_published": true }
],
"cfd": { "$ne": null }
}
Champs importés (whitelist)
Identifiants :
id→ transformé enObjectIdetablissement_gestionnaire_id→ transformé enObjectIdetablissement_formateur_id→ transformé enObjectIdetablissement_reference
Gestionnaire (préfixe etablissement_gestionnaire_) :
siret,enseigne,uai,entreprise_raison_sociale,siren
Formateur (préfixe etablissement_formateur_) :
siret,enseigne,uai,entreprise_raison_sociale,siren
Qualification :
cfd,cfd_specialite,cfd_outdated,cfd_date_fermeture,niveau,diplome
Localisation :
nom_academie,num_academie,code_postal,code_commune_insee,num_departement,nom_departement,region,localite,uai_formation
Description :
nom,intitule_long,intitule_court,libelle_court
MEF :
bcn_mefs_10→ transformé pour ne garder quemef10,modalite.duree,modalite.annee,date_fermeture
Algorithme d’import
FONCTION importCatalogueApprentissage():
// Authentification par cookie session
SI token_expiré OU non_authentifié:
response = POST /auth/login { username, password }
cookie = response.headers["set-cookie"]
page = 1
BOUCLE:
// Requête paginée
params = {
page: page,
limit: 100,
query: JSON.stringify(FILTRE_REQUETE)
}
response = GET /entity/formations?{params}
headers: { cookie: cookie }
SI response.formations.length == 0:
SORTIR BOUCLE
// Transformation des formations
formations = response.formations.map(transformerFormation)
// Upsert batch
POUR CHAQUE formation DANS formations:
CAFormations.updateOne(
{ id: formation.id },
{ $set: formation },
{ upsert: true }
)
page++
FONCTION transformerFormation(formationAPI):
formation = {}
// Transformer les ObjectId
formation.id = ObjectId(formationAPI.id)
SI formationAPI.etablissement_gestionnaire_id:
formation.etablissement_gestionnaire_id = ObjectId(...)
SI formationAPI.etablissement_formateur_id:
formation.etablissement_formateur_id = ObjectId(...)
// Transformer les dates
SI formationAPI.cfd_date_fermeture:
formation.cfd_date_fermeture = new Date(formationAPI.cfd_date_fermeture)
// Extraire les champs de la whitelist
POUR CHAQUE champ DANS WHITELIST:
SI formationAPI[champ] EXISTE:
formation[champ] = formationAPI[champ]
// Transformer bcn_mefs_10 (extraction sélective)
SI formationAPI.bcn_mefs_10:
formation.bcn_mefs_10 = formationAPI.bcn_mefs_10.map(mef => ({
mef10: mef.mef10,
modalite: {
duree: mef.modalite?.duree,
annee: mef.modalite?.annee
}
}))
RETOURNER formation
Schéma MongoDB CAFormations
{
_id: ObjectId,
id: ObjectId, // ID source du Catalogue
// Gestionnaire
etablissement_gestionnaire_id: ObjectId,
etablissement_gestionnaire_siret: String,
etablissement_gestionnaire_enseigne: String,
etablissement_gestionnaire_uai: String, // Ex: "0751234A"
etablissement_gestionnaire_raison_sociale: String,
etablissement_gestionnaire_siren: String,
// Formateur
etablissement_formateur_id: ObjectId,
etablissement_formateur_siret: String,
etablissement_formateur_enseigne: String,
etablissement_formateur_uai: String, // Ex: "0752345B"
etablissement_formateur_raison_sociale: String,
etablissement_formateur_siren: String,
// Qualification
cfd: String, // Ex: "40025214"
cfd_specialite: String,
cfd_outdated: Boolean,
cfd_date_fermeture: Date,
niveau: String,
diplome: String,
// Localisation
nom_academie: String,
num_academie: String,
code_postal: String,
code_commune_insee: String,
region: String,
localite: String,
uai_formation: String, // Ex: "0754567D" - lieu physique
// Description
nom: String,
intitule_long: String,
intitule_court: String,
libelle_court: String,
// MEF
bcn_mefs_10: [{
mef10: String,
modalite: {
duree: String,
annee: String
}
}]
}
8. Calcul UAI
Vue d’ensemble
En apprentissage, une même formation peut avoir des statistiques rattachées à différents niveaux de la hiérarchie UAI. Ce module détermine à quel niveau (lieu, formateur, gestionnaire) appartiennent les données et propage les statistiques vers les niveaux inférieurs quand nécessaire.
Les 3 types d’UAI
| Type | Description | Exemple |
|---|---|---|
lieu_formation | Site physique où se déroulent les cours | Annexe d’un CFA à Versailles |
formateur | CFA qui dispense la formation | CFA des métiers de l’automobile IDF |
gestionnaire | Organisme administratif responsable | Chambre de Commerce et d’Industrie Paris |
Cas par filière
| Filière | Comportement |
|---|---|
superieur | uai_type: "inconnu" - classification non applicable |
pro, agricole (voie scolaire) | Les 3 rôles = même UAI (établissement unique) |
apprentissage | Lookup dans CAFormations pour déterminer le rôle réel |
computeUAI
Description : Classification du rôle de chaque UAI dans la chaîne de formation apprentissage : lieu de formation, formateur, ou gestionnaire.
- Source Externe : -
- Collections MongoDB Sources :
formationsStats,CAFormations,bcn - Collections MongoDB de Sortie :
formationsStats(mise à jour)
Clés et identifiants
| Champ | Description | Utilisation |
|---|---|---|
uai | Code UAI de la stat | Identifiant à classifier |
code_certification | CFD ou MEF | Jointure avec CAFormations via cfd |
uai_formation | UAI lieu (dans CA) | Lookup niveau 1 |
etablissement_formateur_uai | UAI formateur (dans CA) | Lookup niveau 2 |
etablissement_gestionnaire_uai | UAI gestionnaire (dans CA) | Lookup niveau 3 |
Champs mis à jour
| Champ | Type | Description |
|---|---|---|
uai_type | String | Type déterminé : lieu_formation, formateur, gestionnaire, inconnu |
uai_donnee_type | String | Type effectif des données source |
uai_formateur | Array | Liste des UAI formateurs liés |
uai_gestionnaire | Array | Liste des UAI gestionnaires liés |
uai_lieu_formation | Array | Liste des UAI lieux de formation liés |
Algorithme en 5 phases
Phase 1 : computeUAIBase - Classification initiale
Détermine le type de base de chaque UAI en cherchant dans le Catalogue.
FONCTION computeUAIBase():
POUR CHAQUE stat DANS formationsStats.find({ filiere: "apprentissage" }):
uai = stat.uai
cfd = extraireCFD(stat.code_certification)
// Recherche hiérarchique dans CAFormations
// 1. Est-ce un lieu de formation ?
caFormation = CAFormations.findOne({ uai_formation: uai, cfd: cfd })
SI caFormation:
stat.uai_type = "lieu_formation"
stat.uai_formateur = [caFormation.etablissement_formateur_uai]
stat.uai_gestionnaire = [caFormation.etablissement_gestionnaire_uai]
CONTINUER
// 2. Est-ce un formateur ?
caFormations = CAFormations.find({ etablissement_formateur_uai: uai, cfd: cfd })
SI caFormations.length > 0:
stat.uai_type = "formateur"
stat.uai_lieu_formation = caFormations.map(f => f.uai_formation).unique()
stat.uai_gestionnaire = caFormations.map(f => f.etablissement_gestionnaire_uai).unique()
CONTINUER
// 3. Est-ce un gestionnaire ?
caFormations = CAFormations.find({ etablissement_gestionnaire_uai: uai, cfd: cfd })
SI caFormations.length > 0:
stat.uai_type = "gestionnaire"
stat.uai_formateur = caFormations.map(f => f.etablissement_formateur_uai).unique()
stat.uai_lieu_formation = caFormations.map(f => f.uai_formation).unique()
CONTINUER
// 4. Non trouvé
stat.uai_type = "inconnu"
formationsStats.updateOne({ _id: stat._id }, { $set: stat })
Phase 2 : computeUaiFormateur - Upgrade lieu → formateur
Si un lieu_formation est aussi son propre formateur et que ce formateur a d’autres lieux sans données, upgrader en formateur.
FONCTION computeUaiFormateur():
POUR CHAQUE stat DANS formationsStats.find({ uai_type: "lieu_formation" }):
// Condition 1 : L'UAI est son propre formateur unique
SI stat.uai_formateur.length != 1 OU stat.uai_formateur[0] != stat.uai:
CONTINUER
// Condition 2 : Ce formateur a d'autres lieux de formation
autresLieux = CAFormations.find({
etablissement_formateur_uai: stat.uai,
uai_formation: { $ne: stat.uai }
})
SI autresLieux.length == 0:
CONTINUER
// Condition 3 : Ces autres lieux n'ont pas de données propres
lieuxAvecDonnees = formationsStats.find({
uai: { $in: autresLieux.map(l => l.uai_formation) },
code_certification: stat.code_certification,
millesime: stat.millesime
})
SI lieuxAvecDonnees.length == 0:
// Upgrader en formateur
stat.uai_type = "formateur"
stat.uai_donnee_type = "formateur" // Les données sont au niveau formateur
stat.uai_lieu_formation = [stat.uai, ...autresLieux.map(l => l.uai_formation)]
formationsStats.updateOne({ _id: stat._id }, { $set: stat })
Phase 3 : computeUaiGestionnaire - Upgrade → gestionnaire
Si un formateur/lieu est aussi son propre gestionnaire et que ce gestionnaire a d’autres formateurs sans données (mais dont les lieux ont des données), upgrader en gestionnaire.
FONCTION computeUaiGestionnaire():
POUR CHAQUE stat DANS formationsStats.find({ uai_type: { $in: ["lieu_formation", "formateur"] } }):
// Condition 1 : L'UAI est son propre gestionnaire unique
SI stat.uai_gestionnaire.length != 1 OU stat.uai_gestionnaire[0] != stat.uai:
CONTINUER
// Condition 2 : Ce gestionnaire a d'autres formateurs
autresFormateurs = CAFormations.find({
etablissement_gestionnaire_uai: stat.uai,
etablissement_formateur_uai: { $ne: stat.uai }
})
SI autresFormateurs.length == 0:
CONTINUER
// Condition 3 : Ces autres formateurs n'ont pas de données propres
// ET tous leurs lieux ont des données
formateursSansDonnees = []
POUR CHAQUE formateur DANS autresFormateurs:
statFormateur = formationsStats.findOne({
uai: formateur.etablissement_formateur_uai,
code_certification: stat.code_certification,
millesime: stat.millesime
})
SI statFormateur EST NULL:
// Vérifier si tous les lieux de ce formateur ont des données
lieuxFormateur = CAFormations.find({ etablissement_formateur_uai: formateur.etablissement_formateur_uai })
tousLieuxOntDonnees = lieuxFormateur.every(lieu =>
formationsStats.exists({
uai: lieu.uai_formation,
code_certification: stat.code_certification,
millesime: stat.millesime
})
)
// Si tous les lieux ont des données, le formateur n'est pas "sans données"
SI NON tousLieuxOntDonnees:
formateursSansDonnees.push(formateur)
SI formateursSansDonnees.length > 0:
// Upgrader en gestionnaire
stat.uai_type = "gestionnaire"
stat.uai_donnee_type = "gestionnaire" // Les données sont au niveau gestionnaire
formationsStats.updateOne({ _id: stat._id }, { $set: stat })
Phase 4 : computeUAILieuFormationForFormateur - Créer lieux manquants depuis formateur
Pour chaque formateur, créer des records pour ses lieux de formation non attestés (si formateur unique du lieu).
FONCTION computeUAILieuFormationForFormateur():
POUR CHAQUE stat DANS formationsStats.find({ uai_type: "formateur" }):
POUR CHAQUE lieuUai DANS stat.uai_lieu_formation:
// Skip si le lieu a déjà des données
SI formationsStats.exists({ uai: lieuUai, code_certification: stat.code_certification, millesime: stat.millesime }):
CONTINUER
// Vérifier que le formateur est le seul formateur de ce lieu
formateursLieu = CAFormations.find({ uai_formation: lieuUai }).map(f => f.etablissement_formateur_uai).unique()
SI formateursLieu.length != 1 OU formateursLieu[0] != stat.uai:
CONTINUER
// Créer le record pour le lieu
nouveauRecord = copierStats(stat)
nouveauRecord.uai = lieuUai
nouveauRecord.uai_type = "lieu_formation"
nouveauRecord.uai_donnee_type = "formateur" // Données issues du formateur
nouveauRecord.uai_formateur = [stat.uai]
formationsStats.insertOne(nouveauRecord)
Phase 5 : computeUAILieuFormationForGestionnaire - Créer lieux manquants depuis gestionnaire
Pour chaque gestionnaire, créer des records pour les lieux de ses formateurs.
FONCTION computeUAILieuFormationForGestionnaire():
POUR CHAQUE stat DANS formationsStats.find({ uai_type: "gestionnaire" }):
// Récupérer tous les formateurs du gestionnaire
formateurs = CAFormations.find({ etablissement_gestionnaire_uai: stat.uai })
.map(f => f.etablissement_formateur_uai).unique()
POUR CHAQUE formateurUai DANS formateurs:
// Récupérer les lieux de ce formateur
lieuxFormateur = CAFormations.find({ etablissement_formateur_uai: formateurUai })
.map(f => f.uai_formation).unique()
POUR CHAQUE lieuUai DANS lieuxFormateur:
// Skip si le lieu a déjà des données
SI formationsStats.exists({ uai: lieuUai, code_certification: stat.code_certification, millesime: stat.millesime }):
CONTINUER
// Créer le record pour le lieu
nouveauRecord = copierStats(stat)
nouveauRecord.uai = lieuUai
nouveauRecord.uai_type = "lieu_formation"
nouveauRecord.uai_donnee_type = "gestionnaire" // Données issues du gestionnaire
nouveauRecord.uai_formateur = [formateurUai]
nouveauRecord.uai_gestionnaire = [stat.uai]
formationsStats.insertOne(nouveauRecord)
Exemple de classification
Données du Catalogue Apprentissage:
Formation CAP Boulanger (CFD: 50022137)
- Gestionnaire: CCI Paris (UAI: 0751234A)
- Formateur: CFA Boulangerie (UAI: 0752345B)
- Lieux: Annexe Versailles (UAI: 0782456C), Annexe Meaux (UAI: 0772567D)
Stats InserJeunes reçues:
- UAI: 0751234A (gestionnaire), taux_emploi: 80%
Après computeUAI:
Phase 1 (computeUAIBase):
0751234A: uai_type="gestionnaire", uai_formateur=["0752345B"], uai_lieu_formation=["0782456C", "0772567D"]
Phase 5 (computeUAILieuFormationForGestionnaire):
Créé: 0782456C: uai_type="lieu_formation", uai_donnee_type="gestionnaire", taux_emploi=80%
Créé: 0772567D: uai_type="lieu_formation", uai_donnee_type="gestionnaire", taux_emploi=80%
Résultat final:
- 0751234A: données originales (gestionnaire)
- 0782456C: données propagées depuis gestionnaire (lieu)
- 0772567D: données propagées depuis gestionnaire (lieu)
Ordre d’exécution importAll
1. importBCN (7 sous-jobs)
├── importBCN
├── importBCNMEF
├── importBCNSise
├── importBCNContinuum
├── computeBCNMEFContinuum
├── importLibelle
└── importBCNFamilleMetier
2. importEtablissements
3. importStats
4. importSupStats
5. computeContinuumStats
6. importAnneesNonTerminales
├── importAnneesNonTerminales
└── importSecondeCommune
7. importCatalogueApprentissage
8. computeUAI