diff --git a/docs/pedagogie/00-plan-preparation-soutenance.md b/docs/pedagogie/00-plan-preparation-soutenance.md index 2aee863..a3747cf 100644 --- a/docs/pedagogie/00-plan-preparation-soutenance.md +++ b/docs/pedagogie/00-plan-preparation-soutenance.md @@ -2,7 +2,7 @@ > **Méthode :** pour chaque lot, l’agent décrit ce qu’il compte faire → **tu valides ou ajustes** → on exécute → on coche. -**Dernière mise à jour :** lot 2 livré (README rendu + `.env.example`). +**Dernière mise à jour :** lot 3 livré ([fiche-soutenance-orale.md](fiche-soutenance-orale.md)). --- @@ -36,7 +36,7 @@ | A | 0 Cadrage | ✅ Validé | | B | 1 Dépôt + branche | ✅ [fiche-depot-soutenance.md](fiche-depot-soutenance.md) | | C | 2 README rendu autonome | ✅ fait | -| D | 3 Fiche soutenance orale | ☐ à faire | +| D | 3 Fiche soutenance orale | ✅ [fiche-soutenance-orale.md](fiche-soutenance-orale.md) | | E | 4 Commentaires `lib/` | ☐ à faire | --- @@ -47,22 +47,13 @@ - `.env.example` commenté (racine + `swifty_companion/.env.example`). - `swifty_companion/README.md` : pointeur court vers le README racine. -**Prochaine étape :** dis **« OK lot 3 »** pour la fiche orale détaillée. +**Prochaine étape :** dis **« OK lot 4 »** pour les commentaires FR dans `lib/` (+ décision bouton DEBUG). --- -## Lot 3 — Fiche orale (aperçu du déroulé prévu) +## Lot 3 — Livré -Structure envisagée pour `fiche-soutenance-orale.md` : - -1. Introduction (30 s) — sujet, objectif, stack -2. Sécurité & `.env` — encadré sujet, ce qu’est chaque clé OAuth -3. Architecture — 2 vues, services, flux token unique -4. Démo guidée — commandes `flutter doctor`, `test`, `run`, parcours écran par écran -5. Critères obligatoires — comment chaque critère est rempli (renvoi fichiers) -6. Erreurs & edge cases — quoi montrer -7. Bonus refresh — flux + option démo DEBUG -8. Questions fréquentes — réponses courtes +- [fiche-soutenance-orale.md](fiche-soutenance-orale.md) — déroulé oral, commandes commentées, démo, FAQ. --- @@ -79,5 +70,5 @@ Structure envisagée pour `fiche-soutenance-orale.md` : | 0 | ✅ | | 1 | ✅ | | 2 | ✅ | -| 3 | ☐ | +| 3 | ✅ | | 4 | ☐ | diff --git a/docs/pedagogie/README.md b/docs/pedagogie/README.md index 1e51cbf..49e2ba9 100644 --- a/docs/pedagogie/README.md +++ b/docs/pedagogie/README.md @@ -10,6 +10,8 @@ Parcours pour construire le projet **étape par étape**. → **[fiche-depot-soutenance.md](fiche-depot-soutenance.md)** — branche `soutenance`, `.env`, push PC école, bouton DEBUG. +→ **[fiche-soutenance-orale.md](fiche-soutenance-orale.md)** — déroulé soutenance, commandes, démo, FAQ. + → **[guide-soutenance-realisation.md](guide-soutenance-realisation.md)** — cartographie sujet ↔ code pour la correction. ## Prérequis diff --git a/docs/pedagogie/fiche-soutenance-orale.md b/docs/pedagogie/fiche-soutenance-orale.md new file mode 100644 index 0000000..8352c19 --- /dev/null +++ b/docs/pedagogie/fiche-soutenance-orale.md @@ -0,0 +1,257 @@ +# Fiche soutenance orale — Swifty Companion + +> **Lot 3** du [plan de préparation](00-plan-preparation-soutenance.md). +> **Usage :** répétition à voix haute, ordre de démo, réponses courtes. +> **Cartographie détaillée sujet ↔ code :** [guide-soutenance-realisation.md](guide-soutenance-realisation.md). +> **Dépôt / `.env` / PC école :** [fiche-depot-soutenance.md](fiche-depot-soutenance.md). + +**Durée type peer-eval :** 5 à 15 min selon l’évaluateur — viser **8 min** de démo + questions. + +--- + +## Avant d’entrer en soutenance (5 min) + +| Check | Action | +|-------|--------| +| Device | Téléphone chargé, USB débogage ou Wi‑Fi ADB OK | +| `.env` | Présent dans `swiftyCompanion/swifty_companion/.env`, **pas** affiché à l’écran | +| OAuth Intra | App avec scopes **public** + **projects**, redirect = `REDIRECT_URI` du `.env` | +| Terminal prêt | Dossier `swiftyCompanion/swifty_companion` | +| Git école | Pas de `.env` dans `git status` si on montre le repo | + +--- + +## 1. Introduction (~30 s) + +**À dire (modèle) :** + +> « Swifty Companion est une app Flutter qui se connecte une fois à l’API 42 en OAuth2, puis permet de chercher un login et d’afficher son profil : photo, coordonnées, niveau cursus, skills avec pourcentage, et projets validés ou en échec. Les secrets sont dans un `.env` local, ignoré par Git. J’ai deux écrans et un seul token réutilisé pour toutes les requêtes API. » + +| Point | Détail à mentionner si demandé | +|-------|--------------------------------| +| Sujet | Mobile Initiation v4 — API Intra **v2** | +| Stack | Flutter / Dart, `flutter_appauth`, `http`, `flutter_dotenv` | +| Entrée app | `lib/main.dart` → `SearchScreen` | + +--- + +## 2. Sécurité et fichier `.env` (~1 min) + +### Ce que le sujet impose (éliminatoire) + +- Identifiants **uniquement** dans `.env` **local**. +- `.env` **ignoré par Git** — exposition publique = **échec direct**. +- Le rendu Vogosphere contient **`.env.example`**, pas de vrais secrets. + +### Où est le fichier qui tourne + +```text +swiftyCompanion/swifty_companion/.env +``` + +Chargé au démarrage : `dotenv.load()` dans `main.dart`, déclaré en asset dans `pubspec.yaml`. + +### Les trois variables (à expliquer sans lire les valeurs à l’écran) + +| Variable | Rôle | Où la trouver sur l’Intra | +|----------|------|---------------------------| +| `CLIENT_ID` | Identifiant de l’app OAuth | Champ **UID** | +| `CLIENT_SECRET` | Échange code → tokens | Champ **SECRET** (privé) | +| `REDIRECT_URI` | Retour navigateur → app | **URI de redirection** (ex. `com.example.swiftycompanion://callback`) | + +**Scopes** (cochés sur l’Intra, pas dans `.env`) : **`public`** + **`projects`** — sans `projects`, les projets du profil renvoient **403**. + +**Phrase clé :** « Le correcteur copie `.env.example` vers `.env` et met ses propres clés ; mon dépôt Git ne contient jamais le SECRET. » + +--- + +## 3. Architecture (~1–2 min) + +### Les deux vues + +| Vue | Fichier | Rôle | +|-----|---------|------| +| **1 — Recherche** | `lib/screens/search_screen.dart` | Connexion OAuth, champ login, navigation vers profil | +| **2 — Profil** | `lib/screens/profile_screen.dart` | Données agrégées, erreur + Réessayer | + +### Couche services (flux données) + +```text +SearchScreen / ProfileScreen + ↓ +IntraUserService (recherche) IntraProfileService (profil) + ↓ ↓ + intra_http.dart (GET + gestion 401 / 429) + ↓ + IntraAuthService.instance (un seul access + refresh) +``` + +| Fichier | Responsabilité | +|---------|----------------| +| `config/intra_config.dart` | URLs API v2 + lecture `.env` | +| `services/intra_auth_service.dart` | OAuth, headers Bearer, refresh | +| `services/intra_user_service.dart` | `GET /users?filter[login]=` | +| `services/intra_profile_service.dart` | User + skills (cursus) + `projects_users` | +| `services/intra_api_exception.dart` | Erreurs métier → messages UI | +| `theme/swifty_ui.dart` | Scroll, largeurs max, AppBar commune | + +### Un seul token OAuth (critère sujet) + +1. **Une fois** : `authorizeAndExchangeCode` → access (+ refresh en bonus). +2. **Ensuite** : chaque appel API utilise `authHeaders()` — **pas** de nouvelle fenêtre OAuth par recherche. + +Singleton : `IntraAuthService.instance`. + +--- + +## 4. Démo guidée — commandes et parcours (~4–6 min) + +### 4.1 Terminal — ce que fait chaque commande + +Toujours depuis le package Flutter : + +```bash +cd ~/chemin/vers/swiftyCompanion/swifty_companion +``` + +| Commande | À dire à l’oral | +|----------|-----------------| +| `flutter doctor` | Vérifie SDK Flutter, Android toolchain, device branché — preuve que l’environnement est prêt. | +| `flutter pub get` | Télécharge les dépendances du `pubspec.yaml` (`flutter_appauth`, `http`, `dotenv`, …). | +| `flutter analyze` | Analyse statique Dart — pas d’erreurs bloquantes. | +| `flutter test` | Lance les tests unitaires / widget (`test/widget_test.dart`, `test/intra_oauth_tokens_test.dart` pour le bonus). | +| `flutter devices` | Liste émulateur ou téléphone — on note l’id pour `-d`. | +| `flutter run -d ` | Compile, installe et lance l’app en mode debug sur l’appareil. | + +**Ordre recommandé devant l’évaluateur :** `flutter test` (rapide, visible) → `flutter run` si l’app n’est pas déjà lancée. + +### 4.2 Parcours écran par écran + +| Étape | Action | Ce que tu commentes | +|-------|--------|---------------------| +| 1 | Lancer l’app | Si `.env` manquant : message explicite sur l’écran recherche (`IntraConfig.isConfigured`). | +| 2 | **Se connecter avec 42** | Flux OAuth : navigateur / Custom Tab → redirect vers l’app → token stocké en mémoire. | +| 3 | Login valide (ex. un camarade ou toi) | `GET` filtre login → si trouvé, `Navigator.push` vers profil. | +| 4 | **Profil** | Photo, ≥ 4 champs, carte skills (niveau + %), liste projets **finished** et **failed**. | +| 5 | **Retour** (flèche) | Retour vue 1 — **session conservée** (pas besoin de reconnecter). | +| 6 | Deuxième recherche | Prouve le token unique. | +| 7 | Login invalide `zzzzinvalid` | Message clair, pas de crash, pas d’ouverture profil vide. | +| 8 | Mode avion (recherche ou profil) | Erreur réseau ; sur profil, bouton **Réessayer**. | +| 9 | (Option) Rotation / grand écran | Profil en 2 colonnes si large (`LayoutBuilder`, breakpoint 720 px). | + +**Login de démo :** prévoir un login qui a des skills et au moins un projet failed visible. + +--- + +## 5. Critères obligatoires — réponses « où c’est » (~2 min si questions) + +Référence complète : [guide-soutenance-realisation.md §3](guide-soutenance-realisation.md#3-chapitre-v1--partie-obligatoire-9-critères). + +| # | Critère | Réponse courte + fichier | +|---|---------|---------------------------| +| 1 | 2 vues | `search_screen.dart` + `profile_screen.dart` | +| 2 | Toutes les erreurs | Exceptions dans `intra_api_exception.dart` ; catch dans services + UI (`search_screen`, `profile_screen` `_ErrorBody`) | +| 3 | Profil seulement si login existe | Pas de navigation si liste users vide — `IntraUserNotFoundException` | +| 4 | ≥ 4 infos + photo | `_ProfileHeader`, `_DetailsCard` — email, phone, location, wallet, niveau cursus, … | +| 5 | Skills niveau + % | Depuis `cursus_users[].skills` (pas `/experiences`) — `IntraProfileService._parseSkillsFromCursus` | +| 6 | Projets dont failed | `projects_users` — statuts `finished` et `failed` — scope `projects` | +| 7 | Retour vue 1 | `Navigator.pop` + AppBar retour | +| 8 | Layout moderne / flexible | `swifty_ui.dart` — scroll, `searchMaxWidth`, 2 colonnes profil | +| 9 | Un token, pas un par requête | `IntraAuthService.instance` + `intra_http.dart` | + +**Champs `—` sur le profil :** l’API masque certaines données selon la confidentialité — conforme au sujet. + +--- + +## 6. Erreurs et cas limites — quoi montrer + +| Cas | Démo | Message / comportement attendu | +|-----|------|--------------------------------| +| Login vide | Rechercher sans texte | « Saisis un login » | +| Login inconnu | `zzzzinvalid` | Pas de profil | +| Non connecté | (si évaluateur demande) tenter recherche sans OAuth | Invitation à se connecter | +| Réseau | Mode avion | Message réseau | +| 403 scopes | (expliquer) | Scopes `public` + `projects` sur l’app Intra | +| 429 rate limit | (souvent pas en démo) | Retry dans `intra_http.dart` + cache profil 2 min | +| 401 session | Après refresh impossible | « Session expirée… » — voir bonus | +| Erreur chargement profil | (si réseau flaky) | Écran erreur + **Réessayer** | + +Tu n’es pas obligé de tout rejouer : **2–3 cas** bien choisis suffisent souvent. + +--- + +## 7. Bonus refresh token (~1–2 min) + +### Flux à expliquer + +```text +Login → access + refresh en mémoire +GET API → 401 Unauthorized + → POST /oauth/token (grant_type=refresh_token) + → nouveau access + → même GET rejoué une fois +Si refresh échoue → IntraUnauthorizedException, l’utilisateur se reconnecte +``` + +| Fichier | Rôle | +|---------|------| +| `intra_auth_service.dart` | `refreshAccessToken()`, `_refreshInFlight` | +| `intra_http.dart` | `intraApiGet` avec retry après refresh | +| `intra_oauth_tokens.dart` | Parse JSON token — testé dans `intra_oauth_tokens_test.dart` | + +**Limite assumée :** tokens en **mémoire** — kill app = nouvelle connexion OAuth (acceptable si tu le dis). + +### Démo DEBUG (build debug uniquement) + +1. Connexion OK, une recherche OK. +2. Bouton **« DEBUG : simuler token expiré »** sur `SearchScreen` (`kDebugMode` — absent en release). +3. **Rechercher** à nouveau → refresh silencieux → profil sans repasser par le navigateur. + +**Si évaluateur en release :** montrer `flutter test` sur `intra_oauth_tokens_test.dart` + expliquer le flux §7. + +**Bouton DEBUG au rendu final :** voir [fiche-depot-soutenance.md §8](fiche-depot-soutenance.md) — garder (visible seulement en debug) ou retirer avant push ; décision à trancher au lot 4. + +--- + +## 8. Questions fréquentes — réponses courtes + +| Question | Réponse | +|----------|---------| +| Pourquoi pas `/experiences` pour les skills ? | Souvent **403** ; le sujet permet les données user / cursus — skills dans `cursus_users`. | +| Pourquoi `projects` dans les scopes ? | Endpoint `projects_users` exige ce scope. | +| Où est le redirect Android ? | `AndroidManifest.xml` — `RedirectUriReceiverActivity` ; `appAuthRedirectScheme` dans `build.gradle.kts`. | +| Pourquoi des requêtes séquentielles ? | Éviter **429** Too Many Requests sur l’API 42. | +| Cache profil ? | ~2 minutes en mémoire dans `IntraProfileService` — moins de hits API. | +| Différence dépôt perso / école ? | Perso : `docs/` + travail ; école : **racine** = contenu de `swiftyCompanion/` seul. | +| Où est la traduction du sujet ? | Dépôt perso `docs/sujets/` — pas dans Vogosphere. | +| Tests ? | `widget_test` démarre l’app ; bonus : parse token OAuth. | +| WSL vs téléphone ? | OAuth fiable sur **device/émulateur Android** ; voir stratégie VM école dans la piscine mobile. | +| Que faire si SECRET exposé ? | Régénérer sur l’Intra, mettre à jour `.env` local, vérifier l’historique Git. | + +--- + +## 9. Script condensé (une page — à réciter) + +1. **Intro** — Flutter, OAuth une fois, 2 vues, `.env` hors Git. +2. **`flutter test`** — vert. +3. **Connexion 42** — une fois. +4. **Recherche OK** → profil (photo, champs, skills %, failed). +5. **Retour** → 2ᵉ recherche sans reconnecter. +6. **Login faux** + **mode avion**. +7. **Bonus** — DEBUG token expiré → recherche OK. +8. **Rappel** — 9 critères, un token, `public`+`projects`, `.env.example` dans le repo. + +--- + +## 10. Liens utiles (perso) + +| Document | Usage | +|----------|--------| +| [guide-soutenance-realisation.md](guide-soutenance-realisation.md) | Tableau sujet ↔ fichiers | +| [etape-06-checklist-soutenance.md](etape-06-checklist-soutenance.md) | Checklist peer-eval | +| [etape-07-bonus-token.md](etape-07-bonus-token.md) | Détail refresh | +| [swiftyCompanion/README.md](../../swiftyCompanion/README.md) | README correcteurs (rendu) | + +--- + +*Aligné sujet Swifty Companion v4 — lot 3 livré.*