# Synchronisation Escada (pull) La synchronisation depuis Escada télécharge les PDFs (absences, BN, notes, fiches) et les importe en base. C'est l'opération la plus complexe de l'application. ## Page : `/escada` ### Sélection des classes La liste des classes vient d'un cache disque (`data/esacada_classes.json`) rempli via le bouton **Actualiser**. Le rafraîchissement lance une session Selenium qui se connecte à Escadaweb et récupère la liste complète des classes. > Note : MP, MI et "Formation" sont **filtrées de l'affichage UI** mais conservées dans le cache (elles servent au matching Matu pro). ### Options de synchronisation | Option | Effet | |---------------------|----------------------------------------------------------------------| | Absences | Télécharge les PDFs d'absences + parse + import | | BN | Bulletins de notes + moyennes Matu (semestres complets) | | Notes | Notes d'examens finales | | Données apprentis | Fiches personnelles : adresse, entreprise, formateur | ### Force réimportation complète La case "Forcer la réimportation complète" (signalée en jaune) **écrase tous les statuts d'absences** côté local par les valeurs du PDF, et **vide les pendings** des absences concernées. À utiliser uniquement quand on veut **reprendre l'état complet d'Escada** (par exemple après un test ou une corruption locale). Sans ce flag : - Les absences modifiées localement et **pas encore poussées** (= en pending) sont **préservées**. - Les nouvelles absences du PDF sont importées normalement. - Les absences orphelines (présentes en DB mais absentes du PDF) sont supprimées. ## Phases d'exécution ### Phase 1 : Scraping (Selenium) `scripts/sync_esacada.py --sync-all CLASSE1 CLASSE2 ...` 1. Selenium ouvre Escadaweb avec un profil persistant (`data/browser_profile/`) 2. Pour chaque classe sélectionnée : - Télécharge le PDF d'absences - Télécharge le PDF de bulletin - Télécharge le PDF de notes - Pour les apprentis Matu : télécharge le PDF Matu de la classe MP correspondante - Scrape les fiches personnelles (vue ViewLernende) 3. À la fin, écrit `ALL_DONE` dans la sortie standard et `data/sync_all_done.json` (timestamp). ### Phase 2 : Import en DB `scripts/run_imports.py` est lancé par le wrapper après réception du signal `ALL_DONE` : 1. Parse chaque PDF d'absences → upsert des `Absence` (avec déduplication sur (apprenti, date, période)) 2. Parse les BN → insère `NotesBulletin` 3. Parse les notes → insère `NotesExamen` 4. Parse les fiches → upsert `ApprentiFiche` 5. Détecte les **orphelines** (absences en DB mais absentes du PDF dans la fenêtre temporelle) et les supprime (sauf si elles ont un pending, sans force). 6. Écrit `data/sync_last_result.json` avec les compteurs détaillés. ## Pendings : modifications locales en attente Quand un utilisateur modifie une absence dans l'application (page Apprenti), une entrée est créée dans la table `EscadaPending` avec une action : - **`E`** : marquer comme excusée sur Escada - **`N`** : marquer comme non excusée sur Escada - **`clear`** : retirer l'absence sur Escada (= remettre l'apprenti présent) Ces pendings sont visibles sur la page `/escada` dans la section "Modifications en attente". ## Cas particuliers gérés | Situation | Sans force | Avec force | |----------------------------------------------------|---------------------------|---------------------------| | Absence dans PDF + pending sur la même | Pending préservé | Pending écrasé | | Absence dans PDF + DB sans pending | Mise à jour si différent | Mise à jour systématique | | Absence dans DB, absente du PDF, sans pending | Supprimée (orpheline) | Supprimée | | Absence dans DB, absente du PDF, avec pending=clear | Conservée (le clear gagne)| Supprimée | ## Diagnostic - **Timeout > 15 min** : vérifier les logs `data/logs/operations.log`. Souvent un problème Selenium (captcha, session expirée). - **Aucune classe récupérée** : token de session Escada expiré → relancer le rafraîchissement. - **Logs détaillés** : page `/logs` affiche `operations.log` en temps réel.