phase 8
This commit is contained in:
@@ -4,9 +4,9 @@ Ce fichier sert de memoire de travail persistante. Il doit etre mis a jour a cha
|
||||
|
||||
## Phase courante
|
||||
|
||||
- Phase : `6A - SVG exercices CM1 mathematiques / 01B_fractions`
|
||||
- Statut : `termine - a relire`
|
||||
- Derniere mise a jour : `2026-05-05`
|
||||
- Phase : `8D - Utilisation du profil adaptatif / 01B_fractions`
|
||||
- Statut : `integre - a valider`
|
||||
- Derniere mise a jour : `2026-05-06`
|
||||
|
||||
## Phase 0 - Index et classification
|
||||
|
||||
@@ -230,9 +230,143 @@ Etat d'avancement :
|
||||
|
||||
## Prochaine etape
|
||||
|
||||
- Phase suivante : `6B - Validation SVG exercices 01B_fractions`
|
||||
- Statut : `a valider apres Phase 6A`
|
||||
- Objectif : relire les 8 SVG d'exercices avant de lancer le kit adaptatif.
|
||||
- Phase suivante : `7B/8B - Validation SVG exercices et kit adaptatif 01B_fractions`
|
||||
- Statut : `a valider dans l'interface`
|
||||
- Objectif : relire les SVG d'exercices, le diagnostic, les profils, les parcours et les SVG du kit depuis le dashboard admin.
|
||||
|
||||
## Phase 6B - Validation SVG exercices CM1 mathematiques / 01B_fractions
|
||||
|
||||
Objectif : relire les 8 SVG d'exercices avant de lancer le kit adaptatif.
|
||||
|
||||
Statut : validation utilisateur reportee dans l'interface admin.
|
||||
|
||||
Note :
|
||||
|
||||
- L'utilisateur a demande le `2026-05-06` de poursuivre la production sans bloquer sur cette validation manuelle.
|
||||
- Les SVG d'exercices restent donc `a relire` dans l'interface.
|
||||
- La Phase 7A a ete produite en avance pour preparer la validation du lendemain matin.
|
||||
|
||||
## Phase 7A - Kit adaptatif CM1 mathematiques / 01B_fractions
|
||||
|
||||
Objectif : produire un kit adaptatif pour le sous-theme fractions afin d'orienter Professeur TOP apres quelques reponses courtes de diagnostic.
|
||||
|
||||
Livrables attendus :
|
||||
|
||||
- `kit_adaptatif/README.md`
|
||||
- `kit_adaptatif/00_principe_adaptatif.md`
|
||||
- `kit_adaptatif/routage.md`
|
||||
- `kit_adaptatif/test_diagnostic/README.md`
|
||||
- `kit_adaptatif/test_diagnostic/01_lecture_vocabulaire.md`
|
||||
- `kit_adaptatif/test_diagnostic/02_partage_unite.md`
|
||||
- `kit_adaptatif/test_diagnostic/03_demi_droite.md`
|
||||
- `kit_adaptatif/test_diagnostic/04_comparer_expliquer.md`
|
||||
- `kit_adaptatif/parcours_profils/README.md`
|
||||
- `kit_adaptatif/parcours_profils/01_visuel_partage.md`
|
||||
- `kit_adaptatif/parcours_profils/02_verbal_vocabulaire.md`
|
||||
- `kit_adaptatif/parcours_profils/03_manipulation_concret.md`
|
||||
- `kit_adaptatif/parcours_profils/04_reperes_demi_droite.md`
|
||||
- `kit_adaptatif/parcours_profils/05_progressif_charge_reduite.md`
|
||||
- `kit_adaptatif/svg/README.md`
|
||||
- `kit_adaptatif/svg/test_diagnostic_fraction.svg`
|
||||
- `kit_adaptatif/svg/parcours_fraction_support.svg`
|
||||
|
||||
Etat d'avancement :
|
||||
|
||||
- Kit adaptatif : genere.
|
||||
- Tests diagnostics : `4` fichiers.
|
||||
- Parcours par profils : `5` fichiers.
|
||||
- SVG de support : `2` fichiers, XML valide.
|
||||
- Synchronisation backend : effectuee dans `backend/contenus_pedagogiques/.../01B_fractions/kit_adaptatif`.
|
||||
- Integration applicative fine : non lancee.
|
||||
|
||||
Statut : genere - a valider dans l'interface.
|
||||
|
||||
## Phase 8A - Exposition admin du kit adaptatif / 01B_fractions
|
||||
|
||||
Objectif : rendre le kit adaptatif visible et validable depuis l'interface admin, sans attendre une integration fine dans le deroulement eleve.
|
||||
|
||||
Livrables produits :
|
||||
|
||||
- `backend/app/program_content.py` : ajout des compteurs `adaptive_tests`, `adaptive_profiles`, `adaptive_svg`.
|
||||
- `backend/app/program_content.py` : ajout des noeuds `kit`, `diagnostic`, `parcours` et `support` dans l'arbre admin.
|
||||
- `frontend/src/App.jsx` : la file `Validation contenus` inclut maintenant les elements du kit adaptatif.
|
||||
- `frontend/src/App.jsx` : les panneaux de statut affichent les tests, parcours et SVG du kit.
|
||||
- `frontend/src/App.jsx` : les noeuds Markdown de diagnostic et de parcours affichent un apercu texte validable.
|
||||
|
||||
Etat d'avancement :
|
||||
|
||||
- Exposition dans l'arbre admin : effectuee.
|
||||
- Exposition dans la file de validation : effectuee.
|
||||
- Revue plein ecran des SVG du kit : disponible via les noeuds `support`.
|
||||
- Revue des textes Markdown du kit : disponible dans le panneau de validation.
|
||||
- Integration du choix adaptatif dans une session eleve : non lancee.
|
||||
|
||||
Statut : integre - a valider dans l'interface.
|
||||
|
||||
## Phase 8B - Contexte adaptatif dans Professeur TOP / 01B_fractions
|
||||
|
||||
Objectif : rendre le kit adaptatif exploitable par le moteur de dialogue sans encore construire une UI eleve dediee au diagnostic.
|
||||
|
||||
Livrables produits :
|
||||
|
||||
- `backend/app/program_content.py` : ajout de `adaptive_kit` dans les objets lecon.
|
||||
- `backend/app/program_content.py` : ajout de `format_adaptive_kit_context`.
|
||||
- `backend/app/schemas.py` : ajout de `adaptive_kit` a `ProgramLesson`.
|
||||
- `backend/app/schemas.py` : ajout de `lesson_id` dans `ChatRequest`.
|
||||
- `backend/app/main.py` : ajout du contexte adaptatif au demarrage de seance quand la lecon a un kit.
|
||||
- `backend/app/main.py` : ajout du contexte adaptatif aux appels `/chat` quand `lesson_id` est fourni.
|
||||
- `frontend/src/App.jsx` : envoi de `lesson_id` dans le contexte de lecon.
|
||||
- `frontend/src/App.jsx` : affichage du kit adaptatif dans la fiche eleve admin.
|
||||
|
||||
Etat d'avancement :
|
||||
|
||||
- Kit accessible via les reponses programme : effectue.
|
||||
- Contexte adaptatif transmis a Professeur TOP : effectue.
|
||||
- Interface eleve dediee au diagnostic : non lancee.
|
||||
- Selection automatique persistante d'un parcours adaptatif par eleve : non lancee.
|
||||
|
||||
Statut : integre - a valider dans l'interface.
|
||||
|
||||
## Phase 8C - Suivi adaptatif par eleve / 01B_fractions
|
||||
|
||||
Objectif : permettre au professeur de conserver un parcours adaptatif recommande pour un eleve et une lecon.
|
||||
|
||||
Livrables produits :
|
||||
|
||||
- `backend/app/models.py` : nouvelle table `student_adaptive_profiles`.
|
||||
- `backend/app/schemas.py` : schemas `StudentAdaptiveProfileRequest` et `StudentAdaptiveProfileRead`.
|
||||
- `backend/app/main.py` : endpoint `GET /admin/students/{student_id}/adaptive-profile`.
|
||||
- `backend/app/main.py` : endpoint `PUT /admin/students/{student_id}/adaptive-profile`.
|
||||
- `frontend/src/App.jsx` : chargement du profil adaptatif de l'eleve pour la lecon active.
|
||||
- `frontend/src/App.jsx` : formulaire admin pour choisir le test diagnostic de reference, le parcours recommande et une note de suivi.
|
||||
- `frontend/src/styles.css` : styles du panneau de suivi adaptatif.
|
||||
|
||||
Etat d'avancement :
|
||||
|
||||
- Stockage du profil adaptatif par eleve/lecon : effectue.
|
||||
- Affichage dans la fiche eleve admin : effectue.
|
||||
- Sauvegarde du parcours recommande : effectuee.
|
||||
- Utilisation automatique de ce profil dans le deroulement eleve : non lancee.
|
||||
|
||||
Statut : integre - a valider dans l'interface.
|
||||
|
||||
## Phase 8D - Utilisation du profil adaptatif / 01B_fractions
|
||||
|
||||
Objectif : faire en sorte qu'un profil adaptatif enregistre par le professeur influence effectivement les reponses de Professeur TOP.
|
||||
|
||||
Livrables produits :
|
||||
|
||||
- `backend/app/main.py` : ajout de `format_student_adaptive_profile_context`.
|
||||
- `backend/app/main.py` : injection du profil adaptatif au demarrage de seance.
|
||||
- `backend/app/main.py` : injection du profil adaptatif dans les appels `/chat`.
|
||||
|
||||
Etat d'avancement :
|
||||
|
||||
- Si un profil est enregistre pour l'eleve et la lecon active, il est transmis comme aide prioritaire au LLM.
|
||||
- Le kit adaptatif generique reste transmis meme sans profil enregistre.
|
||||
- Le frontend eleve ne montre pas encore explicitement le diagnostic adaptatif.
|
||||
|
||||
Statut : integre - a valider dans l'interface.
|
||||
|
||||
## Phases prevues
|
||||
|
||||
|
||||
@@ -129,3 +129,17 @@ Organisation :
|
||||
- `exercices_svg/08_fraction_d_une_quantite.svg`
|
||||
|
||||
Statut : SVG exercices initiaux prets pour relecture Phase 6B.
|
||||
|
||||
## Kit adaptatif genere
|
||||
|
||||
Phase 7A : un kit adaptatif de sous-theme est genere dans `kit_adaptatif/`.
|
||||
|
||||
Organisation :
|
||||
|
||||
- `kit_adaptatif/00_principe_adaptatif.md` : profils observables et erreurs a reperer.
|
||||
- `kit_adaptatif/routage.md` : choix du parcours apres diagnostic.
|
||||
- `kit_adaptatif/test_diagnostic/` : 4 tests courts.
|
||||
- `kit_adaptatif/parcours_profils/` : 5 parcours de reprise.
|
||||
- `kit_adaptatif/svg/` : 2 supports visuels XML valides.
|
||||
|
||||
Statut : kit adaptatif initial pret pour relecture Phase 7B.
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Principe adaptatif - fractions CM1
|
||||
|
||||
## Compétence cible
|
||||
|
||||
Comprendre une fraction simple comme un nombre qui exprime des parts égales d'une unité, puis utiliser cette compréhension pour lire, représenter, comparer et calculer avec des fractions simples.
|
||||
|
||||
## Faiblesses à détecter
|
||||
|
||||
- l'élève inverse numérateur et dénominateur ;
|
||||
- l'élève oublie que les parts doivent être égales ;
|
||||
- l'élève lit une fraction chiffre par chiffre sans utiliser les mots `demi`, `tiers`, `quart`, `cinquième` ;
|
||||
- l'élève ne sait pas relier fraction et dessin ;
|
||||
- l'élève place une fraction sur une demi-droite comme un entier isolé ;
|
||||
- l'élève compare seulement les nombres du haut ou seulement les nombres du bas ;
|
||||
- l'élève ne distingue pas fraction inférieure, égale ou supérieure à `1` ;
|
||||
- l'élève réussit un exercice visuel mais se bloque dès que la consigne devient verbale.
|
||||
|
||||
## Profils pédagogiques observables
|
||||
|
||||
### 1. Visuel et partage
|
||||
|
||||
L'élève comprend mieux avec une unité dessinée, des parts colorées, des bandes ou des disques fractionnés.
|
||||
|
||||
### 2. Verbal et vocabulaire
|
||||
|
||||
L'élève a besoin de dire les fractions, d'entendre les mots et de stabiliser le vocabulaire : numérateur, dénominateur, unité, parts égales.
|
||||
|
||||
### 3. Manipulation et concret
|
||||
|
||||
L'élève progresse avec des situations de partage : gâteau, bande, tablette, collection d'objets, recette.
|
||||
|
||||
### 4. Repères et demi-droite
|
||||
|
||||
L'élève doit travailler le passage de la fraction dessinée à la fraction placée sur une ligne graduée.
|
||||
|
||||
### 5. Progressif et charge réduite
|
||||
|
||||
L'élève sait parfois faire, mais se trompe quand il y a trop d'informations à traiter en même temps.
|
||||
|
||||
## Règle générale
|
||||
|
||||
Le diagnostic ne doit pas coller une étiquette à l'élève. Il sert seulement à choisir la meilleure première aide.
|
||||
|
||||
Professeur TOP doit pouvoir combiner deux profils si les réponses le justifient, par exemple `visuel et partage` puis `verbal et vocabulaire`.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Kit adaptatif - 01B fractions
|
||||
|
||||
## Rôle du dossier
|
||||
|
||||
Ce kit transforme le sous-thème `01B_fractions` en parcours adaptatif pour Professeur TOP.
|
||||
|
||||
Il sert à choisir rapidement une entrée de travail adaptée à l'élève après quelques réponses courtes :
|
||||
|
||||
- lecture et écriture des fractions simples ;
|
||||
- représentation d'un partage ;
|
||||
- placement sur demi-droite graduée ;
|
||||
- comparaison et encadrement ;
|
||||
- petites opérations sur fractions de même dénominateur ;
|
||||
- fraction d'une quantité.
|
||||
|
||||
## Structure
|
||||
|
||||
- `00_principe_adaptatif.md` : logique générale, erreurs à repérer et profils observables.
|
||||
- `routage.md` : règles d'orientation après diagnostic.
|
||||
- `test_diagnostic/` : tests courts à utiliser avant de choisir un parcours.
|
||||
- `parcours_profils/` : exercices et reprises classés par profil pédagogique.
|
||||
- `svg/` : supports visuels du diagnostic et des parcours.
|
||||
|
||||
## Statut
|
||||
|
||||
Phase `7A` : kit adaptatif généré pour revue.
|
||||
|
||||
Les contenus restent éditoriaux : ils devront être validés dans l'interface avant assimilation complète au programme actif.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 01 - Visuel et partage
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève se trompe sur les parts égales, le sens du numérateur ou la comparaison de fractions représentées.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Une fraction parle toujours d'une unité. On partage cette unité en parts égales. Le dénominateur dit combien il y a de parts égales. Le numérateur dit combien de parts on prend.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Dessine une bande en `5` parts égales et colorie `2` parts. Écris la fraction.
|
||||
2. Parmi deux dessins, choisis celui qui représente vraiment `3/4`.
|
||||
3. Colorie `4/6` d'une bande déjà partagée.
|
||||
4. Explique pourquoi un dessin avec des parts inégales ne convient pas.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `2/5`
|
||||
- dessin avec quatre parts égales dont trois coloriées ;
|
||||
- quatre parts coloriées sur six ;
|
||||
- les parts doivent avoir la même taille pour compter correctement.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche `02_representer_fraction_partage`.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Parcours 02 - Verbal et vocabulaire
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève inverse numérateur et dénominateur, lit mal les fractions ou manque de mots pour expliquer.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Dans `3/4`, je lis d'abord le nombre du haut : `3`. Il dit combien de parts sont prises. Puis je lis le nombre du bas : `4`. Il dit en combien de parts égales l'unité est partagée.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Lis `1/2`, `2/3`, `3/4`, `5/8`.
|
||||
2. Dans `6/10`, entoure le numérateur.
|
||||
3. Dans `6/10`, souligne le dénominateur.
|
||||
4. Écris en fraction : `sept dixièmes`.
|
||||
5. Complète : le dénominateur indique `...`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `un demi`, `deux tiers`, `trois quarts`, `cinq huitièmes` ;
|
||||
- numérateur `6` ;
|
||||
- dénominateur `10` ;
|
||||
- `7/10` ;
|
||||
- le nombre de parts égales dans l'unité.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche `01_lire_ecrire_fraction_simple`.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 03 - Manipulation et concret
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève comprend mieux quand la fraction est reliée à une situation réelle.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Une fraction peut dire une part de gâteau, une longueur de ruban, une partie d'une tablette ou une part d'une collection.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Une tablette a `8` carrés. On mange `3` carrés. Quelle fraction de la tablette est mangée ?
|
||||
2. Une bande mesure une unité. Elle est partagée en `6` morceaux égaux. On garde `5` morceaux. Quelle fraction est gardée ?
|
||||
3. Il y a `12` jetons. On prend le quart. Combien de jetons prend-on ?
|
||||
4. Explique `2/3` avec une situation de partage.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `3/8`
|
||||
- `5/6`
|
||||
- `3` jetons
|
||||
- situation où une unité ou une collection est partagée en trois parts égales et deux parts sont prises.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre les fiches `02_representer_fraction_partage` et `08_fraction_d_une_quantite`.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 04 - Repères et demi-droite
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève a besoin de relier fractions, graduations et position entre deux entiers.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Sur une demi-droite, le dénominateur dit en combien d'intervalles égaux on partage l'unité. Le numérateur dit combien d'intervalles on avance depuis `0`.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Partage l'intervalle de `0` à `1` en `4` parties égales.
|
||||
2. Place `1/4`, `2/4`, `3/4`.
|
||||
3. Place `5/4` et explique pourquoi il est après `1`.
|
||||
4. Encadre `9/4` entre deux entiers.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- quatre intervalles égaux entre `0` et `1` ;
|
||||
- fractions placées dans l'ordre ;
|
||||
- `5/4` est une unité complète plus `1/4` ;
|
||||
- `9/4` est entre `2` et `3`.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre les fiches `03_fraction_sur_demi_droite_graduee`, `04_fraction_superieure_a_un` et `06_encadrer_fraction_entre_entiers`.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Parcours 05 - Progressif et charge réduite
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève comprend une notion isolée mais se trompe quand la page contient trop d'informations.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
On traite une seule idée à la fois : lire, puis représenter, puis comparer.
|
||||
|
||||
## Exercices
|
||||
|
||||
### Étape 1 - Lire
|
||||
|
||||
Lis seulement `3/4`.
|
||||
|
||||
Réponse : `trois quarts`.
|
||||
|
||||
### Étape 2 - Nommer
|
||||
|
||||
Dans `3/4`, indique seulement le dénominateur.
|
||||
|
||||
Réponse : `4`.
|
||||
|
||||
### Étape 3 - Dessiner
|
||||
|
||||
Dessine une bande en `4` parts égales.
|
||||
|
||||
Réponse : quatre parts de même taille.
|
||||
|
||||
### Étape 4 - Colorier
|
||||
|
||||
Colorie `3` parts.
|
||||
|
||||
Réponse : trois parts coloriées sur quatre.
|
||||
|
||||
### Étape 5 - Résumer
|
||||
|
||||
Écris la phrase : `3/4 signifie 3 parts prises sur 4 parts égales`.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche correspondant à l'étape qui a bloqué.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Parcours par profils observables
|
||||
|
||||
Chaque dossier propose une reprise ciblée après diagnostic.
|
||||
|
||||
Les parcours peuvent être combinés. Par exemple, un élève peut commencer par `01_visuel_partage`, puis passer à `02_verbal_vocabulaire` pour stabiliser les mots.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Routage adaptatif - fractions CM1
|
||||
|
||||
## Entrées observables
|
||||
|
||||
| Observation | Orientation principale | Orientation secondaire |
|
||||
| --- | --- | --- |
|
||||
| Inverse haut et bas dans `3/4` | `02_verbal_vocabulaire` | `01_visuel_partage` |
|
||||
| Colorie des parts inégales | `01_visuel_partage` | `03_manipulation_concret` |
|
||||
| Lit `5/8` comme `cinq barre huit` | `02_verbal_vocabulaire` | `05_progressif_charge_reduite` |
|
||||
| Place `3/4` après `3` sur la demi-droite | `04_reperes_demi_droite` | `01_visuel_partage` |
|
||||
| Compare `2/5` et `4/5` en regardant le dénominateur | `01_visuel_partage` | `02_verbal_vocabulaire` |
|
||||
| Se perd dans les énoncés longs | `05_progressif_charge_reduite` | profil dominant repéré |
|
||||
| Comprend avec objets mais pas avec symboles | `03_manipulation_concret` | `02_verbal_vocabulaire` |
|
||||
|
||||
## Décision rapide
|
||||
|
||||
1. Faire passer les quatre tests courts.
|
||||
2. Noter les erreurs, mais aussi les hésitations et reformulations spontanées.
|
||||
3. Choisir un parcours principal.
|
||||
4. Ajouter un parcours secondaire si deux difficultés sont nettes.
|
||||
5. Revenir ensuite à la fiche ou card du programme correspondant à la difficulté.
|
||||
|
||||
## Retour au programme
|
||||
|
||||
- Erreur sur lecture/écriture : reprendre `01_lire_ecrire_fraction_simple`.
|
||||
- Erreur sur dessin : reprendre `02_representer_fraction_partage`.
|
||||
- Erreur sur ligne graduée : reprendre `03_fraction_sur_demi_droite_graduee`.
|
||||
- Erreur sur `> 1` : reprendre `04_fraction_superieure_a_un`.
|
||||
- Erreur de comparaison : reprendre `05_comparer_fractions_simples`.
|
||||
- Erreur d'encadrement : reprendre `06_encadrer_fraction_entre_entiers`.
|
||||
- Erreur d'addition/soustraction : reprendre `07_additionner_soustraire_fractions_simples`.
|
||||
- Erreur sur collection : reprendre `08_fraction_d_une_quantite`.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# SVG du kit adaptatif fractions
|
||||
|
||||
Ces visuels servent de supports simples pour les tests et les parcours.
|
||||
|
||||
Ils sont volontairement moins denses que les fiches de leçon : chaque SVG porte une seule décision pédagogique.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="760" viewBox="0 0 1200 760">
|
||||
<rect width="1200" height="760" fill="#f7fafc"/>
|
||||
<rect x="60" y="50" width="1080" height="660" rx="28" fill="#ffffff" stroke="#d7e3df" stroke-width="6"/>
|
||||
<text x="100" y="120" font-family="Inter, Arial, sans-serif" font-size="44" font-weight="700" fill="#0f3d5e">Aide fractions</text>
|
||||
<text x="100" y="170" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Une unité, des parts égales, des parts prises.</text>
|
||||
|
||||
<rect x="120" y="230" width="420" height="160" rx="20" fill="#eef6ff" stroke="#bfd7f2"/>
|
||||
<text x="150" y="280" font-family="Inter, Arial, sans-serif" font-size="30" font-weight="700" fill="#0f3d5e">3/4</text>
|
||||
<text x="230" y="270" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">3 parts prises</text>
|
||||
<text x="230" y="325" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">4 parts égales</text>
|
||||
<line x1="150" y1="296" x2="205" y2="296" stroke="#1f2937" stroke-width="4"/>
|
||||
|
||||
<rect x="660" y="230" width="360" height="160" rx="20" fill="#fff7ed" stroke="#fed7aa"/>
|
||||
<rect x="720" y="292" width="240" height="58" fill="#ffffff" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="780" y1="292" x2="780" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="840" y1="292" x2="840" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="900" y1="292" x2="900" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<rect x="720" y="292" width="180" height="58" fill="#f97316" opacity="0.45"/>
|
||||
<text x="716" y="380" font-family="Inter, Arial, sans-serif" font-size="22" fill="#415466">3 parts sur 4</text>
|
||||
|
||||
<rect x="120" y="460" width="900" height="120" rx="20" fill="#f0fdf4" stroke="#bbf7d0"/>
|
||||
<line x1="200" y1="530" x2="900" y2="530" stroke="#1f2937" stroke-width="4"/>
|
||||
<line x1="200" y1="515" x2="200" y2="545" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="900" y1="515" x2="900" y2="545" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="375" y1="520" x2="375" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<line x1="550" y1="520" x2="550" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<line x1="725" y1="520" x2="725" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<circle cx="725" cy="530" r="10" fill="#16a34a"/>
|
||||
<text x="190" y="570" font-family="Inter, Arial, sans-serif" font-size="20" fill="#1f2937">0</text>
|
||||
<text x="893" y="570" font-family="Inter, Arial, sans-serif" font-size="20" fill="#1f2937">1</text>
|
||||
<text x="700" y="500" font-family="Inter, Arial, sans-serif" font-size="24" font-weight="700" fill="#166534">3/4</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="760" viewBox="0 0 1200 760">
|
||||
<rect width="1200" height="760" fill="#f7fafc"/>
|
||||
<rect x="60" y="50" width="1080" height="660" rx="28" fill="#ffffff" stroke="#d7e3df" stroke-width="6"/>
|
||||
<text x="100" y="120" font-family="Inter, Arial, sans-serif" font-size="44" font-weight="700" fill="#0f3d5e">Diagnostic fractions</text>
|
||||
<text x="100" y="170" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Lis, dessine, place, puis compare.</text>
|
||||
|
||||
<rect x="100" y="220" width="230" height="160" rx="18" fill="#eef6ff" stroke="#bfd7f2"/>
|
||||
<text x="130" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#0f3d5e">1. Lire</text>
|
||||
<text x="175" y="340" font-family="Inter, Arial, sans-serif" font-size="54" font-weight="700" fill="#1f2937">3/4</text>
|
||||
|
||||
<rect x="365" y="220" width="230" height="160" rx="18" fill="#fff7ed" stroke="#fed7aa"/>
|
||||
<text x="395" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#9a3412">2. Partager</text>
|
||||
<rect x="410" y="310" width="140" height="44" fill="#ffffff" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="445" y1="310" x2="445" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="480" y1="310" x2="480" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="515" y1="310" x2="515" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<rect x="410" y="310" width="105" height="44" fill="#f97316" opacity="0.45"/>
|
||||
|
||||
<rect x="630" y="220" width="230" height="160" rx="18" fill="#f0fdf4" stroke="#bbf7d0"/>
|
||||
<text x="660" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#166534">3. Placer</text>
|
||||
<line x1="680" y1="335" x2="815" y2="335" stroke="#1f2937" stroke-width="4"/>
|
||||
<line x1="680" y1="325" x2="680" y2="345" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="815" y1="325" x2="815" y2="345" stroke="#1f2937" stroke-width="3"/>
|
||||
<circle cx="781" cy="335" r="9" fill="#16a34a"/>
|
||||
<text x="670" y="370" font-family="Inter, Arial, sans-serif" font-size="18" fill="#1f2937">0</text>
|
||||
<text x="808" y="370" font-family="Inter, Arial, sans-serif" font-size="18" fill="#1f2937">1</text>
|
||||
|
||||
<rect x="895" y="220" width="205" height="160" rx="18" fill="#fef2f2" stroke="#fecaca"/>
|
||||
<text x="925" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#991b1b">4. Comparer</text>
|
||||
<text x="940" y="340" font-family="Inter, Arial, sans-serif" font-size="42" font-weight="700" fill="#1f2937">2/5 ? 4/5</text>
|
||||
|
||||
<rect x="120" y="460" width="960" height="140" rx="20" fill="#f8fafc" stroke="#dbe3ef"/>
|
||||
<text x="160" y="520" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#0f3d5e">Observer surtout les erreurs, les hésitations et les mots utilisés.</text>
|
||||
<text x="160" y="565" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Le diagnostic choisit une aide. Il ne note pas l'élève.</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,26 @@
|
||||
# Test 01 - Lecture et vocabulaire
|
||||
|
||||
## Objectif
|
||||
|
||||
Repérer si l'élève sait lire une fraction et identifier numérateur et dénominateur.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Lis `3/4`.
|
||||
2. Dans `3/4`, quel nombre indique le nombre de parts prises ?
|
||||
3. Dans `3/4`, quel nombre indique le nombre de parts égales dans l'unité ?
|
||||
4. Écris en fraction : `deux cinquièmes`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
1. `trois quarts`.
|
||||
2. `3`, le numérateur.
|
||||
3. `4`, le dénominateur.
|
||||
4. `2/5`.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Inversion haut/bas : parcours `02_verbal_vocabulaire`.
|
||||
- Lecture chiffre par chiffre : parcours `02_verbal_vocabulaire`.
|
||||
- Réussite orale mais erreur écrite : parcours `05_progressif_charge_reduite`.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Test 02 - Partage de l'unité
|
||||
|
||||
## Objectif
|
||||
|
||||
Vérifier que l'élève relie une fraction à des parts égales d'une unité.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Dessine une bande partagée en `4` parts égales.
|
||||
2. Colorie `3` parts.
|
||||
3. Écris la fraction coloriée.
|
||||
4. Explique pourquoi les parts doivent être égales.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- Bande partagée en quatre parts de même taille.
|
||||
- Trois parts coloriées.
|
||||
- Fraction `3/4`.
|
||||
- Réponse attendue : sinon la fraction ne décrit pas correctement le partage de l'unité.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Parts inégales : parcours `01_visuel_partage`.
|
||||
- Bon dessin mais explication faible : parcours `02_verbal_vocabulaire`.
|
||||
- Besoin d'une situation concrète : parcours `03_manipulation_concret`.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Test 03 - Demi-droite graduée
|
||||
|
||||
## Objectif
|
||||
|
||||
Voir si l'élève comprend qu'une fraction peut être placée entre deux nombres entiers.
|
||||
|
||||
## Questions
|
||||
|
||||
1. On partage le segment entre `0` et `1` en `4` parts égales.
|
||||
2. Place `1/4`, `2/4` et `3/4`.
|
||||
3. Quelle fraction est la plus proche de `1` ?
|
||||
4. `5/4` est-il avant ou après `1` ?
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `1/4`, `2/4`, `3/4` placés dans l'ordre entre `0` et `1`.
|
||||
- `3/4` est la plus proche de `1`.
|
||||
- `5/4` est après `1`.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Place `3/4` après `3` : parcours `04_reperes_demi_droite`.
|
||||
- Confond `4` graduations et `4` intervalles : parcours `04_reperes_demi_droite`.
|
||||
- Comprend avec bande mais pas avec ligne : combiner `01_visuel_partage` puis `04_reperes_demi_droite`.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Test 04 - Comparer et expliquer
|
||||
|
||||
## Objectif
|
||||
|
||||
Repérer si l'élève compare les fractions avec le bon critère.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Compare `2/5` et `4/5`.
|
||||
2. Compare `1/2` et `1/4`.
|
||||
3. Complète : `7/4` est entre `...` et `...`.
|
||||
4. Explique avec tes mots pourquoi `4/5` est plus grand que `2/5`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `4/5 > 2/5`.
|
||||
- `1/2 > 1/4`.
|
||||
- `7/4` est entre `1` et `2`.
|
||||
- Pour le même dénominateur, plus le numérateur est grand, plus on prend de parts.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Compare seulement les dénominateurs : parcours `01_visuel_partage`.
|
||||
- Ne sait pas encadrer `7/4` : reprendre `04_reperes_demi_droite`.
|
||||
- Bonne comparaison mais justification fragile : parcours `02_verbal_vocabulaire`.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Tests diagnostics - fractions CM1
|
||||
|
||||
Ces tests sont courts et doivent être utilisés avant de choisir un parcours adaptatif.
|
||||
|
||||
Ils ne servent pas à noter l'élève. Ils servent à comprendre quelle aide sera la plus efficace.
|
||||
|
||||
Ordre conseillé :
|
||||
|
||||
1. `01_lecture_vocabulaire.md`
|
||||
2. `02_partage_unite.md`
|
||||
3. `03_demi_droite.md`
|
||||
4. `04_comparer_expliquer.md`
|
||||
|
||||
@@ -21,6 +21,7 @@ from .curriculum import QUESTIONS
|
||||
from .program_content import (
|
||||
build_program_tree,
|
||||
decode_asset_token,
|
||||
format_adaptive_kit_context,
|
||||
get_content_status,
|
||||
get_lesson,
|
||||
list_lessons_for_grade,
|
||||
@@ -102,6 +103,18 @@ def build_student_program_response(student: models.Student, assignment: models.S
|
||||
)
|
||||
|
||||
|
||||
def format_student_adaptive_profile_context(profile: models.StudentAdaptiveProfile | None) -> str:
|
||||
if not profile:
|
||||
return ""
|
||||
return (
|
||||
"Profil adaptatif recommandé pour cet élève:\n"
|
||||
f"- Parcours: {profile.profile_title}\n"
|
||||
f"- Source: {profile.source}\n"
|
||||
f"- Notes professeur: {profile.notes or 'aucune'}\n"
|
||||
"Règle: utilise ce parcours comme aide prioritaire si l'élève hésite ou se trompe sur cette leçon.\n"
|
||||
)
|
||||
|
||||
|
||||
def clamp_lesson_position(lesson: dict, asset_index: int, section_index: int) -> tuple[int, int]:
|
||||
assets = lesson.get("assets") or []
|
||||
if not assets:
|
||||
@@ -427,6 +440,55 @@ def update_student_program_start(
|
||||
return build_student_program_response(student, assignment)
|
||||
|
||||
|
||||
@app.get("/admin/students/{student_id}/adaptive-profile", response_model=schemas.StudentAdaptiveProfileRead | None)
|
||||
def get_student_adaptive_profile(
|
||||
student_id: int,
|
||||
lesson_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(require_roles("teacher", "maintenance")),
|
||||
):
|
||||
student = db.query(models.Student).filter_by(id=student_id).first()
|
||||
if not student:
|
||||
raise HTTPException(status_code=404, detail="Élève introuvable")
|
||||
return (
|
||||
db.query(models.StudentAdaptiveProfile)
|
||||
.filter_by(student_id=student_id, lesson_id=lesson_id)
|
||||
.first()
|
||||
)
|
||||
|
||||
|
||||
@app.put("/admin/students/{student_id}/adaptive-profile", response_model=schemas.StudentAdaptiveProfileRead)
|
||||
def upsert_student_adaptive_profile(
|
||||
student_id: int,
|
||||
payload: schemas.StudentAdaptiveProfileRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(require_roles("teacher", "maintenance")),
|
||||
):
|
||||
student = db.query(models.Student).filter_by(id=student_id).first()
|
||||
if not student:
|
||||
raise HTTPException(status_code=404, detail="Élève introuvable")
|
||||
lesson = get_lesson(payload.lesson_id)
|
||||
if not lesson:
|
||||
raise HTTPException(status_code=404, detail="Leçon introuvable")
|
||||
|
||||
row = (
|
||||
db.query(models.StudentAdaptiveProfile)
|
||||
.filter_by(student_id=student_id, lesson_id=payload.lesson_id)
|
||||
.first()
|
||||
)
|
||||
if not row:
|
||||
row = models.StudentAdaptiveProfile(student_id=student_id, lesson_id=payload.lesson_id)
|
||||
row.diagnostic_id = payload.diagnostic_id
|
||||
row.profile_id = payload.profile_id
|
||||
row.profile_title = payload.profile_title
|
||||
row.notes = payload.notes
|
||||
row.source = payload.source
|
||||
db.add(row)
|
||||
db.commit()
|
||||
db.refresh(row)
|
||||
return row
|
||||
|
||||
|
||||
@app.get("/students/{student_id}/card-progress", response_model=list[schemas.LessonCardProgressRead])
|
||||
def list_card_progress(
|
||||
student_id: int,
|
||||
@@ -544,6 +606,17 @@ def start_session(
|
||||
)
|
||||
if first_section and first_section.get("context"):
|
||||
lesson_context += f"\nTexte pédagogique de la card courante:\n{first_section['context'].strip()}"
|
||||
adaptive_context = format_adaptive_kit_context(assigned_lesson)
|
||||
if adaptive_context:
|
||||
lesson_context += f"\n\n{adaptive_context}"
|
||||
adaptive_profile = (
|
||||
db.query(models.StudentAdaptiveProfile)
|
||||
.filter_by(student_id=student.id, lesson_id=assigned_lesson["id"])
|
||||
.first()
|
||||
)
|
||||
profile_context = format_student_adaptive_profile_context(adaptive_profile)
|
||||
if profile_context:
|
||||
lesson_context += f"\n\n{profile_context}"
|
||||
lesson_reply = build_lesson_reply(
|
||||
db,
|
||||
student.id,
|
||||
@@ -581,6 +654,7 @@ def chat(
|
||||
|
||||
lesson_context = ""
|
||||
if payload.lesson_title or payload.section_title or payload.asset_title:
|
||||
current_lesson = get_lesson(payload.lesson_id) if payload.lesson_id else None
|
||||
lesson_context = (
|
||||
f"Leçon courante: {payload.lesson_title or 'non précisée'}\n"
|
||||
f"Fiche courante: {payload.asset_title or 'non précisée'}\n"
|
||||
@@ -591,6 +665,18 @@ def chat(
|
||||
)
|
||||
if payload.section_context:
|
||||
lesson_context += f"\nTexte pédagogique de la card courante:\n{payload.section_context.strip()}"
|
||||
adaptive_context = format_adaptive_kit_context(current_lesson)
|
||||
if adaptive_context:
|
||||
lesson_context += f"\n\n{adaptive_context}"
|
||||
if current_lesson:
|
||||
adaptive_profile = (
|
||||
db.query(models.StudentAdaptiveProfile)
|
||||
.filter_by(student_id=student.id, lesson_id=current_lesson["id"])
|
||||
.first()
|
||||
)
|
||||
profile_context = format_student_adaptive_profile_context(adaptive_profile)
|
||||
if profile_context:
|
||||
lesson_context += f"\n\n{profile_context}"
|
||||
|
||||
if lesson_context:
|
||||
lesson_reply = build_lesson_reply(db, payload.student_id, payload.message, lesson_context)
|
||||
|
||||
@@ -121,6 +121,25 @@ class LessonCardProgress(Base):
|
||||
student = relationship("Student")
|
||||
|
||||
|
||||
class StudentAdaptiveProfile(Base):
|
||||
__tablename__ = "student_adaptive_profiles"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("student_id", "lesson_id", name="uq_student_adaptive_lesson"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
student_id: Mapped[int] = mapped_column(ForeignKey("students.id"), index=True)
|
||||
lesson_id: Mapped[str] = mapped_column(String(500), index=True)
|
||||
diagnostic_id: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
profile_id: Mapped[str] = mapped_column(String(500))
|
||||
profile_title: Mapped[str] = mapped_column(String(255))
|
||||
notes: Mapped[str] = mapped_column(Text, default="")
|
||||
source: Mapped[str] = mapped_column(String(50), default="teacher")
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
student = relationship("Student")
|
||||
|
||||
|
||||
class AssessmentAttempt(Base):
|
||||
__tablename__ = "assessment_attempts"
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@ def list_lessons() -> list[dict]:
|
||||
"cycle": cycle,
|
||||
"asset_count": len(assets),
|
||||
"assets": assets,
|
||||
"adaptive_kit": collect_adaptive_kit(lesson_dir),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -297,6 +298,7 @@ def save_review_state(state: dict) -> None:
|
||||
|
||||
def collect_lesson_status(lesson: dict) -> dict:
|
||||
lesson_dir = CONTENT_ROOT / lesson["id"]
|
||||
kit_dir = lesson_dir / "kit_adaptatif"
|
||||
return {
|
||||
"micro_fiches": len([path for path in lesson_dir.glob("*.md") if path.name.startswith(tuple("0123456789"))]),
|
||||
"lesson_svg": len(lesson.get("assets") or []),
|
||||
@@ -304,6 +306,9 @@ def collect_lesson_status(lesson: dict) -> dict:
|
||||
"contexts": len(list((lesson_dir / "card_contexts").rglob("Fiche*Card*.md"))),
|
||||
"text_exercises": len(list((lesson_dir / "exercices").rglob("exercices.md"))),
|
||||
"exercise_svg": len(list((lesson_dir / "exercices_svg").glob("*.svg"))),
|
||||
"adaptive_tests": len([path for path in (kit_dir / "test_diagnostic").glob("*.md") if path.name != "README.md"]) if kit_dir.exists() else 0,
|
||||
"adaptive_profiles": len([path for path in (kit_dir / "parcours_profils").glob("*.md") if path.name != "README.md"]) if kit_dir.exists() else 0,
|
||||
"adaptive_svg": len(list((kit_dir / "svg").glob("*.svg"))) if kit_dir.exists() else 0,
|
||||
"validations": sorted(path.stem for path in lesson_dir.glob("VALIDATION_PHASE_*.md")),
|
||||
}
|
||||
|
||||
@@ -318,6 +323,69 @@ def append_tree_child(parent: dict, key: str, label: str, node_type: str, path:
|
||||
return child
|
||||
|
||||
|
||||
def read_review_text(path: Path, max_chars: int = 6000) -> str:
|
||||
text = path.read_text(encoding="utf-8", errors="ignore").strip()
|
||||
return text[:max_chars]
|
||||
|
||||
|
||||
def collect_adaptive_kit(lesson_dir: Path) -> dict | None:
|
||||
kit_dir = lesson_dir / "kit_adaptatif"
|
||||
if not kit_dir.exists():
|
||||
return None
|
||||
|
||||
def markdown_items(folder: str) -> list[dict]:
|
||||
base = kit_dir / folder
|
||||
if not base.exists():
|
||||
return []
|
||||
return [
|
||||
{
|
||||
"id": path.relative_to(CONTENT_ROOT).as_posix(),
|
||||
"title": title_from_slug(path.stem),
|
||||
"content": read_review_text(path),
|
||||
}
|
||||
for path in sorted(base.glob("*.md"))
|
||||
if path.name != "README.md"
|
||||
]
|
||||
|
||||
supports = []
|
||||
svg_dir = kit_dir / "svg"
|
||||
if svg_dir.exists():
|
||||
for path in sorted(svg_dir.glob("*.svg")):
|
||||
relative = path.relative_to(CONTENT_ROOT).as_posix()
|
||||
supports.append(
|
||||
{
|
||||
"id": relative,
|
||||
"title": title_from_slug(path.stem),
|
||||
"url": build_asset_url(relative),
|
||||
}
|
||||
)
|
||||
|
||||
return {
|
||||
"id": kit_dir.relative_to(CONTENT_ROOT).as_posix(),
|
||||
"title": "Kit adaptatif",
|
||||
"diagnostics": markdown_items("test_diagnostic"),
|
||||
"profiles": markdown_items("parcours_profils"),
|
||||
"supports": supports,
|
||||
}
|
||||
|
||||
|
||||
def format_adaptive_kit_context(lesson: dict | None) -> str:
|
||||
kit = (lesson or {}).get("adaptive_kit")
|
||||
if not kit:
|
||||
return ""
|
||||
|
||||
diagnostics = ", ".join(item["title"] for item in kit.get("diagnostics") or [])
|
||||
profiles = ", ".join(item["title"] for item in kit.get("profiles") or [])
|
||||
return (
|
||||
"Kit adaptatif disponible pour cette leçon.\n"
|
||||
f"Tests diagnostics: {diagnostics or 'aucun'}.\n"
|
||||
f"Parcours de reprise: {profiles or 'aucun'}.\n"
|
||||
"Règle adaptative: si l'élève se trompe nettement, semble bloqué, inverse les notions ou demande de l'aide, "
|
||||
"ne change pas de card tout de suite. Propose une micro-question de diagnostic liée à la difficulté observée, "
|
||||
"puis oriente vers le parcours le plus probable en une phrase courte.\n"
|
||||
)
|
||||
|
||||
|
||||
def build_program_tree() -> dict:
|
||||
reviews = load_review_state()
|
||||
root = {
|
||||
@@ -365,6 +433,37 @@ def build_program_tree() -> dict:
|
||||
)
|
||||
section_node["url"] = section.get("url")
|
||||
section_node["review"] = reviews.get(section_key, {})
|
||||
lesson_dir = CONTENT_ROOT / lesson["id"]
|
||||
kit_dir = lesson_dir / "kit_adaptatif"
|
||||
if kit_dir.exists():
|
||||
kit_key = f"{lesson['id']}/kit_adaptatif"
|
||||
kit_node = append_tree_child(lesson_node, kit_key, "Kit adaptatif", "kit", kit_key)
|
||||
kit_node["review"] = reviews.get(kit_key, {})
|
||||
|
||||
tests_node = append_tree_child(kit_node, f"{kit_key}/test_diagnostic", "Tests diagnostics", "group", f"{kit_key}/test_diagnostic")
|
||||
for test_path in sorted((kit_dir / "test_diagnostic").glob("*.md")):
|
||||
if test_path.name == "README.md":
|
||||
continue
|
||||
relative = test_path.relative_to(CONTENT_ROOT).as_posix()
|
||||
test_node = append_tree_child(tests_node, relative, title_from_slug(test_path.stem), "diagnostic", relative)
|
||||
test_node["content"] = read_review_text(test_path)
|
||||
test_node["review"] = reviews.get(relative, {})
|
||||
|
||||
profiles_node = append_tree_child(kit_node, f"{kit_key}/parcours_profils", "Parcours profils", "group", f"{kit_key}/parcours_profils")
|
||||
for profile_path in sorted((kit_dir / "parcours_profils").glob("*.md")):
|
||||
if profile_path.name == "README.md":
|
||||
continue
|
||||
relative = profile_path.relative_to(CONTENT_ROOT).as_posix()
|
||||
profile_node = append_tree_child(profiles_node, relative, title_from_slug(profile_path.stem), "parcours", relative)
|
||||
profile_node["content"] = read_review_text(profile_path)
|
||||
profile_node["review"] = reviews.get(relative, {})
|
||||
|
||||
svg_node = append_tree_child(kit_node, f"{kit_key}/svg", "Supports visuels", "group", f"{kit_key}/svg")
|
||||
for svg_path in sorted((kit_dir / "svg").glob("*.svg")):
|
||||
relative = svg_path.relative_to(CONTENT_ROOT).as_posix()
|
||||
support_node = append_tree_child(svg_node, relative, title_from_slug(svg_path.stem), "support", relative)
|
||||
support_node["url"] = build_asset_url(relative)
|
||||
support_node["review"] = reviews.get(relative, {})
|
||||
return root
|
||||
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ class StudentAccountResponse(BaseModel):
|
||||
class ChatRequest(BaseModel):
|
||||
student_id: int
|
||||
message: str
|
||||
lesson_id: str | None = None
|
||||
lesson_title: str | None = None
|
||||
asset_title: str | None = None
|
||||
section_title: str | None = None
|
||||
@@ -124,6 +125,7 @@ class ProgramLesson(BaseModel):
|
||||
cycle: str
|
||||
asset_count: int
|
||||
assets: List[ProgramAsset] = []
|
||||
adaptive_kit: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class ProgramLessonListResponse(BaseModel):
|
||||
@@ -152,6 +154,30 @@ class StudentProgramStartRequest(BaseModel):
|
||||
section_index: int = Field(..., ge=0)
|
||||
|
||||
|
||||
class StudentAdaptiveProfileRequest(BaseModel):
|
||||
lesson_id: str = Field(..., min_length=1)
|
||||
diagnostic_id: str | None = None
|
||||
profile_id: str = Field(..., min_length=1)
|
||||
profile_title: str = Field(..., min_length=1)
|
||||
notes: str = ""
|
||||
source: str = "teacher"
|
||||
|
||||
|
||||
class StudentAdaptiveProfileRead(BaseModel):
|
||||
id: int
|
||||
student_id: int
|
||||
lesson_id: str
|
||||
diagnostic_id: str | None = None
|
||||
profile_id: str
|
||||
profile_title: str
|
||||
notes: str
|
||||
source: str
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class LessonCardState(BaseModel):
|
||||
lesson_id: str
|
||||
asset_path: str
|
||||
|
||||
@@ -129,3 +129,17 @@ Organisation :
|
||||
- `exercices_svg/08_fraction_d_une_quantite.svg`
|
||||
|
||||
Statut : SVG exercices initiaux prets pour relecture Phase 6B.
|
||||
|
||||
## Kit adaptatif genere
|
||||
|
||||
Phase 7A : un kit adaptatif de sous-theme est genere dans `kit_adaptatif/`.
|
||||
|
||||
Organisation :
|
||||
|
||||
- `kit_adaptatif/00_principe_adaptatif.md` : profils observables et erreurs a reperer.
|
||||
- `kit_adaptatif/routage.md` : choix du parcours apres diagnostic.
|
||||
- `kit_adaptatif/test_diagnostic/` : 4 tests courts.
|
||||
- `kit_adaptatif/parcours_profils/` : 5 parcours de reprise.
|
||||
- `kit_adaptatif/svg/` : 2 supports visuels XML valides.
|
||||
|
||||
Statut : kit adaptatif initial pret pour relecture Phase 7B.
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Principe adaptatif - fractions CM1
|
||||
|
||||
## Compétence cible
|
||||
|
||||
Comprendre une fraction simple comme un nombre qui exprime des parts égales d'une unité, puis utiliser cette compréhension pour lire, représenter, comparer et calculer avec des fractions simples.
|
||||
|
||||
## Faiblesses à détecter
|
||||
|
||||
- l'élève inverse numérateur et dénominateur ;
|
||||
- l'élève oublie que les parts doivent être égales ;
|
||||
- l'élève lit une fraction chiffre par chiffre sans utiliser les mots `demi`, `tiers`, `quart`, `cinquième` ;
|
||||
- l'élève ne sait pas relier fraction et dessin ;
|
||||
- l'élève place une fraction sur une demi-droite comme un entier isolé ;
|
||||
- l'élève compare seulement les nombres du haut ou seulement les nombres du bas ;
|
||||
- l'élève ne distingue pas fraction inférieure, égale ou supérieure à `1` ;
|
||||
- l'élève réussit un exercice visuel mais se bloque dès que la consigne devient verbale.
|
||||
|
||||
## Profils pédagogiques observables
|
||||
|
||||
### 1. Visuel et partage
|
||||
|
||||
L'élève comprend mieux avec une unité dessinée, des parts colorées, des bandes ou des disques fractionnés.
|
||||
|
||||
### 2. Verbal et vocabulaire
|
||||
|
||||
L'élève a besoin de dire les fractions, d'entendre les mots et de stabiliser le vocabulaire : numérateur, dénominateur, unité, parts égales.
|
||||
|
||||
### 3. Manipulation et concret
|
||||
|
||||
L'élève progresse avec des situations de partage : gâteau, bande, tablette, collection d'objets, recette.
|
||||
|
||||
### 4. Repères et demi-droite
|
||||
|
||||
L'élève doit travailler le passage de la fraction dessinée à la fraction placée sur une ligne graduée.
|
||||
|
||||
### 5. Progressif et charge réduite
|
||||
|
||||
L'élève sait parfois faire, mais se trompe quand il y a trop d'informations à traiter en même temps.
|
||||
|
||||
## Règle générale
|
||||
|
||||
Le diagnostic ne doit pas coller une étiquette à l'élève. Il sert seulement à choisir la meilleure première aide.
|
||||
|
||||
Professeur TOP doit pouvoir combiner deux profils si les réponses le justifient, par exemple `visuel et partage` puis `verbal et vocabulaire`.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Kit adaptatif - 01B fractions
|
||||
|
||||
## Rôle du dossier
|
||||
|
||||
Ce kit transforme le sous-thème `01B_fractions` en parcours adaptatif pour Professeur TOP.
|
||||
|
||||
Il sert à choisir rapidement une entrée de travail adaptée à l'élève après quelques réponses courtes :
|
||||
|
||||
- lecture et écriture des fractions simples ;
|
||||
- représentation d'un partage ;
|
||||
- placement sur demi-droite graduée ;
|
||||
- comparaison et encadrement ;
|
||||
- petites opérations sur fractions de même dénominateur ;
|
||||
- fraction d'une quantité.
|
||||
|
||||
## Structure
|
||||
|
||||
- `00_principe_adaptatif.md` : logique générale, erreurs à repérer et profils observables.
|
||||
- `routage.md` : règles d'orientation après diagnostic.
|
||||
- `test_diagnostic/` : tests courts à utiliser avant de choisir un parcours.
|
||||
- `parcours_profils/` : exercices et reprises classés par profil pédagogique.
|
||||
- `svg/` : supports visuels du diagnostic et des parcours.
|
||||
|
||||
## Statut
|
||||
|
||||
Phase `7A` : kit adaptatif généré pour revue.
|
||||
|
||||
Les contenus restent éditoriaux : ils devront être validés dans l'interface avant assimilation complète au programme actif.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 01 - Visuel et partage
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève se trompe sur les parts égales, le sens du numérateur ou la comparaison de fractions représentées.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Une fraction parle toujours d'une unité. On partage cette unité en parts égales. Le dénominateur dit combien il y a de parts égales. Le numérateur dit combien de parts on prend.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Dessine une bande en `5` parts égales et colorie `2` parts. Écris la fraction.
|
||||
2. Parmi deux dessins, choisis celui qui représente vraiment `3/4`.
|
||||
3. Colorie `4/6` d'une bande déjà partagée.
|
||||
4. Explique pourquoi un dessin avec des parts inégales ne convient pas.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `2/5`
|
||||
- dessin avec quatre parts égales dont trois coloriées ;
|
||||
- quatre parts coloriées sur six ;
|
||||
- les parts doivent avoir la même taille pour compter correctement.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche `02_representer_fraction_partage`.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Parcours 02 - Verbal et vocabulaire
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève inverse numérateur et dénominateur, lit mal les fractions ou manque de mots pour expliquer.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Dans `3/4`, je lis d'abord le nombre du haut : `3`. Il dit combien de parts sont prises. Puis je lis le nombre du bas : `4`. Il dit en combien de parts égales l'unité est partagée.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Lis `1/2`, `2/3`, `3/4`, `5/8`.
|
||||
2. Dans `6/10`, entoure le numérateur.
|
||||
3. Dans `6/10`, souligne le dénominateur.
|
||||
4. Écris en fraction : `sept dixièmes`.
|
||||
5. Complète : le dénominateur indique `...`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `un demi`, `deux tiers`, `trois quarts`, `cinq huitièmes` ;
|
||||
- numérateur `6` ;
|
||||
- dénominateur `10` ;
|
||||
- `7/10` ;
|
||||
- le nombre de parts égales dans l'unité.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche `01_lire_ecrire_fraction_simple`.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 03 - Manipulation et concret
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève comprend mieux quand la fraction est reliée à une situation réelle.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Une fraction peut dire une part de gâteau, une longueur de ruban, une partie d'une tablette ou une part d'une collection.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Une tablette a `8` carrés. On mange `3` carrés. Quelle fraction de la tablette est mangée ?
|
||||
2. Une bande mesure une unité. Elle est partagée en `6` morceaux égaux. On garde `5` morceaux. Quelle fraction est gardée ?
|
||||
3. Il y a `12` jetons. On prend le quart. Combien de jetons prend-on ?
|
||||
4. Explique `2/3` avec une situation de partage.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `3/8`
|
||||
- `5/6`
|
||||
- `3` jetons
|
||||
- situation où une unité ou une collection est partagée en trois parts égales et deux parts sont prises.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre les fiches `02_representer_fraction_partage` et `08_fraction_d_une_quantite`.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Parcours 04 - Repères et demi-droite
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève a besoin de relier fractions, graduations et position entre deux entiers.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
Sur une demi-droite, le dénominateur dit en combien d'intervalles égaux on partage l'unité. Le numérateur dit combien d'intervalles on avance depuis `0`.
|
||||
|
||||
## Exercices
|
||||
|
||||
1. Partage l'intervalle de `0` à `1` en `4` parties égales.
|
||||
2. Place `1/4`, `2/4`, `3/4`.
|
||||
3. Place `5/4` et explique pourquoi il est après `1`.
|
||||
4. Encadre `9/4` entre deux entiers.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- quatre intervalles égaux entre `0` et `1` ;
|
||||
- fractions placées dans l'ordre ;
|
||||
- `5/4` est une unité complète plus `1/4` ;
|
||||
- `9/4` est entre `2` et `3`.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre les fiches `03_fraction_sur_demi_droite_graduee`, `04_fraction_superieure_a_un` et `06_encadrer_fraction_entre_entiers`.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Parcours 05 - Progressif et charge réduite
|
||||
|
||||
## Pour quel élève ?
|
||||
|
||||
L'élève comprend une notion isolée mais se trompe quand la page contient trop d'informations.
|
||||
|
||||
## Reprise courte
|
||||
|
||||
On traite une seule idée à la fois : lire, puis représenter, puis comparer.
|
||||
|
||||
## Exercices
|
||||
|
||||
### Étape 1 - Lire
|
||||
|
||||
Lis seulement `3/4`.
|
||||
|
||||
Réponse : `trois quarts`.
|
||||
|
||||
### Étape 2 - Nommer
|
||||
|
||||
Dans `3/4`, indique seulement le dénominateur.
|
||||
|
||||
Réponse : `4`.
|
||||
|
||||
### Étape 3 - Dessiner
|
||||
|
||||
Dessine une bande en `4` parts égales.
|
||||
|
||||
Réponse : quatre parts de même taille.
|
||||
|
||||
### Étape 4 - Colorier
|
||||
|
||||
Colorie `3` parts.
|
||||
|
||||
Réponse : trois parts coloriées sur quatre.
|
||||
|
||||
### Étape 5 - Résumer
|
||||
|
||||
Écris la phrase : `3/4 signifie 3 parts prises sur 4 parts égales`.
|
||||
|
||||
## Retour programme
|
||||
|
||||
Reprendre la fiche correspondant à l'étape qui a bloqué.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Parcours par profils observables
|
||||
|
||||
Chaque dossier propose une reprise ciblée après diagnostic.
|
||||
|
||||
Les parcours peuvent être combinés. Par exemple, un élève peut commencer par `01_visuel_partage`, puis passer à `02_verbal_vocabulaire` pour stabiliser les mots.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Routage adaptatif - fractions CM1
|
||||
|
||||
## Entrées observables
|
||||
|
||||
| Observation | Orientation principale | Orientation secondaire |
|
||||
| --- | --- | --- |
|
||||
| Inverse haut et bas dans `3/4` | `02_verbal_vocabulaire` | `01_visuel_partage` |
|
||||
| Colorie des parts inégales | `01_visuel_partage` | `03_manipulation_concret` |
|
||||
| Lit `5/8` comme `cinq barre huit` | `02_verbal_vocabulaire` | `05_progressif_charge_reduite` |
|
||||
| Place `3/4` après `3` sur la demi-droite | `04_reperes_demi_droite` | `01_visuel_partage` |
|
||||
| Compare `2/5` et `4/5` en regardant le dénominateur | `01_visuel_partage` | `02_verbal_vocabulaire` |
|
||||
| Se perd dans les énoncés longs | `05_progressif_charge_reduite` | profil dominant repéré |
|
||||
| Comprend avec objets mais pas avec symboles | `03_manipulation_concret` | `02_verbal_vocabulaire` |
|
||||
|
||||
## Décision rapide
|
||||
|
||||
1. Faire passer les quatre tests courts.
|
||||
2. Noter les erreurs, mais aussi les hésitations et reformulations spontanées.
|
||||
3. Choisir un parcours principal.
|
||||
4. Ajouter un parcours secondaire si deux difficultés sont nettes.
|
||||
5. Revenir ensuite à la fiche ou card du programme correspondant à la difficulté.
|
||||
|
||||
## Retour au programme
|
||||
|
||||
- Erreur sur lecture/écriture : reprendre `01_lire_ecrire_fraction_simple`.
|
||||
- Erreur sur dessin : reprendre `02_representer_fraction_partage`.
|
||||
- Erreur sur ligne graduée : reprendre `03_fraction_sur_demi_droite_graduee`.
|
||||
- Erreur sur `> 1` : reprendre `04_fraction_superieure_a_un`.
|
||||
- Erreur de comparaison : reprendre `05_comparer_fractions_simples`.
|
||||
- Erreur d'encadrement : reprendre `06_encadrer_fraction_entre_entiers`.
|
||||
- Erreur d'addition/soustraction : reprendre `07_additionner_soustraire_fractions_simples`.
|
||||
- Erreur sur collection : reprendre `08_fraction_d_une_quantite`.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# SVG du kit adaptatif fractions
|
||||
|
||||
Ces visuels servent de supports simples pour les tests et les parcours.
|
||||
|
||||
Ils sont volontairement moins denses que les fiches de leçon : chaque SVG porte une seule décision pédagogique.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="760" viewBox="0 0 1200 760">
|
||||
<rect width="1200" height="760" fill="#f7fafc"/>
|
||||
<rect x="60" y="50" width="1080" height="660" rx="28" fill="#ffffff" stroke="#d7e3df" stroke-width="6"/>
|
||||
<text x="100" y="120" font-family="Inter, Arial, sans-serif" font-size="44" font-weight="700" fill="#0f3d5e">Aide fractions</text>
|
||||
<text x="100" y="170" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Une unité, des parts égales, des parts prises.</text>
|
||||
|
||||
<rect x="120" y="230" width="420" height="160" rx="20" fill="#eef6ff" stroke="#bfd7f2"/>
|
||||
<text x="150" y="280" font-family="Inter, Arial, sans-serif" font-size="30" font-weight="700" fill="#0f3d5e">3/4</text>
|
||||
<text x="230" y="270" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">3 parts prises</text>
|
||||
<text x="230" y="325" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">4 parts égales</text>
|
||||
<line x1="150" y1="296" x2="205" y2="296" stroke="#1f2937" stroke-width="4"/>
|
||||
|
||||
<rect x="660" y="230" width="360" height="160" rx="20" fill="#fff7ed" stroke="#fed7aa"/>
|
||||
<rect x="720" y="292" width="240" height="58" fill="#ffffff" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="780" y1="292" x2="780" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="840" y1="292" x2="840" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="900" y1="292" x2="900" y2="350" stroke="#1f2937" stroke-width="2"/>
|
||||
<rect x="720" y="292" width="180" height="58" fill="#f97316" opacity="0.45"/>
|
||||
<text x="716" y="380" font-family="Inter, Arial, sans-serif" font-size="22" fill="#415466">3 parts sur 4</text>
|
||||
|
||||
<rect x="120" y="460" width="900" height="120" rx="20" fill="#f0fdf4" stroke="#bbf7d0"/>
|
||||
<line x1="200" y1="530" x2="900" y2="530" stroke="#1f2937" stroke-width="4"/>
|
||||
<line x1="200" y1="515" x2="200" y2="545" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="900" y1="515" x2="900" y2="545" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="375" y1="520" x2="375" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<line x1="550" y1="520" x2="550" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<line x1="725" y1="520" x2="725" y2="540" stroke="#64748b" stroke-width="2"/>
|
||||
<circle cx="725" cy="530" r="10" fill="#16a34a"/>
|
||||
<text x="190" y="570" font-family="Inter, Arial, sans-serif" font-size="20" fill="#1f2937">0</text>
|
||||
<text x="893" y="570" font-family="Inter, Arial, sans-serif" font-size="20" fill="#1f2937">1</text>
|
||||
<text x="700" y="500" font-family="Inter, Arial, sans-serif" font-size="24" font-weight="700" fill="#166534">3/4</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,36 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="760" viewBox="0 0 1200 760">
|
||||
<rect width="1200" height="760" fill="#f7fafc"/>
|
||||
<rect x="60" y="50" width="1080" height="660" rx="28" fill="#ffffff" stroke="#d7e3df" stroke-width="6"/>
|
||||
<text x="100" y="120" font-family="Inter, Arial, sans-serif" font-size="44" font-weight="700" fill="#0f3d5e">Diagnostic fractions</text>
|
||||
<text x="100" y="170" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Lis, dessine, place, puis compare.</text>
|
||||
|
||||
<rect x="100" y="220" width="230" height="160" rx="18" fill="#eef6ff" stroke="#bfd7f2"/>
|
||||
<text x="130" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#0f3d5e">1. Lire</text>
|
||||
<text x="175" y="340" font-family="Inter, Arial, sans-serif" font-size="54" font-weight="700" fill="#1f2937">3/4</text>
|
||||
|
||||
<rect x="365" y="220" width="230" height="160" rx="18" fill="#fff7ed" stroke="#fed7aa"/>
|
||||
<text x="395" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#9a3412">2. Partager</text>
|
||||
<rect x="410" y="310" width="140" height="44" fill="#ffffff" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="445" y1="310" x2="445" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="480" y1="310" x2="480" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<line x1="515" y1="310" x2="515" y2="354" stroke="#1f2937" stroke-width="2"/>
|
||||
<rect x="410" y="310" width="105" height="44" fill="#f97316" opacity="0.45"/>
|
||||
|
||||
<rect x="630" y="220" width="230" height="160" rx="18" fill="#f0fdf4" stroke="#bbf7d0"/>
|
||||
<text x="660" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#166534">3. Placer</text>
|
||||
<line x1="680" y1="335" x2="815" y2="335" stroke="#1f2937" stroke-width="4"/>
|
||||
<line x1="680" y1="325" x2="680" y2="345" stroke="#1f2937" stroke-width="3"/>
|
||||
<line x1="815" y1="325" x2="815" y2="345" stroke="#1f2937" stroke-width="3"/>
|
||||
<circle cx="781" cy="335" r="9" fill="#16a34a"/>
|
||||
<text x="670" y="370" font-family="Inter, Arial, sans-serif" font-size="18" fill="#1f2937">0</text>
|
||||
<text x="808" y="370" font-family="Inter, Arial, sans-serif" font-size="18" fill="#1f2937">1</text>
|
||||
|
||||
<rect x="895" y="220" width="205" height="160" rx="18" fill="#fef2f2" stroke="#fecaca"/>
|
||||
<text x="925" y="270" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#991b1b">4. Comparer</text>
|
||||
<text x="940" y="340" font-family="Inter, Arial, sans-serif" font-size="42" font-weight="700" fill="#1f2937">2/5 ? 4/5</text>
|
||||
|
||||
<rect x="120" y="460" width="960" height="140" rx="20" fill="#f8fafc" stroke="#dbe3ef"/>
|
||||
<text x="160" y="520" font-family="Inter, Arial, sans-serif" font-size="28" font-weight="700" fill="#0f3d5e">Observer surtout les erreurs, les hésitations et les mots utilisés.</text>
|
||||
<text x="160" y="565" font-family="Inter, Arial, sans-serif" font-size="24" fill="#415466">Le diagnostic choisit une aide. Il ne note pas l'élève.</text>
|
||||
</svg>
|
||||
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,26 @@
|
||||
# Test 01 - Lecture et vocabulaire
|
||||
|
||||
## Objectif
|
||||
|
||||
Repérer si l'élève sait lire une fraction et identifier numérateur et dénominateur.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Lis `3/4`.
|
||||
2. Dans `3/4`, quel nombre indique le nombre de parts prises ?
|
||||
3. Dans `3/4`, quel nombre indique le nombre de parts égales dans l'unité ?
|
||||
4. Écris en fraction : `deux cinquièmes`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
1. `trois quarts`.
|
||||
2. `3`, le numérateur.
|
||||
3. `4`, le dénominateur.
|
||||
4. `2/5`.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Inversion haut/bas : parcours `02_verbal_vocabulaire`.
|
||||
- Lecture chiffre par chiffre : parcours `02_verbal_vocabulaire`.
|
||||
- Réussite orale mais erreur écrite : parcours `05_progressif_charge_reduite`.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Test 02 - Partage de l'unité
|
||||
|
||||
## Objectif
|
||||
|
||||
Vérifier que l'élève relie une fraction à des parts égales d'une unité.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Dessine une bande partagée en `4` parts égales.
|
||||
2. Colorie `3` parts.
|
||||
3. Écris la fraction coloriée.
|
||||
4. Explique pourquoi les parts doivent être égales.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- Bande partagée en quatre parts de même taille.
|
||||
- Trois parts coloriées.
|
||||
- Fraction `3/4`.
|
||||
- Réponse attendue : sinon la fraction ne décrit pas correctement le partage de l'unité.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Parts inégales : parcours `01_visuel_partage`.
|
||||
- Bon dessin mais explication faible : parcours `02_verbal_vocabulaire`.
|
||||
- Besoin d'une situation concrète : parcours `03_manipulation_concret`.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Test 03 - Demi-droite graduée
|
||||
|
||||
## Objectif
|
||||
|
||||
Voir si l'élève comprend qu'une fraction peut être placée entre deux nombres entiers.
|
||||
|
||||
## Questions
|
||||
|
||||
1. On partage le segment entre `0` et `1` en `4` parts égales.
|
||||
2. Place `1/4`, `2/4` et `3/4`.
|
||||
3. Quelle fraction est la plus proche de `1` ?
|
||||
4. `5/4` est-il avant ou après `1` ?
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `1/4`, `2/4`, `3/4` placés dans l'ordre entre `0` et `1`.
|
||||
- `3/4` est la plus proche de `1`.
|
||||
- `5/4` est après `1`.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Place `3/4` après `3` : parcours `04_reperes_demi_droite`.
|
||||
- Confond `4` graduations et `4` intervalles : parcours `04_reperes_demi_droite`.
|
||||
- Comprend avec bande mais pas avec ligne : combiner `01_visuel_partage` puis `04_reperes_demi_droite`.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# Test 04 - Comparer et expliquer
|
||||
|
||||
## Objectif
|
||||
|
||||
Repérer si l'élève compare les fractions avec le bon critère.
|
||||
|
||||
## Questions
|
||||
|
||||
1. Compare `2/5` et `4/5`.
|
||||
2. Compare `1/2` et `1/4`.
|
||||
3. Complète : `7/4` est entre `...` et `...`.
|
||||
4. Explique avec tes mots pourquoi `4/5` est plus grand que `2/5`.
|
||||
|
||||
## Réponses attendues
|
||||
|
||||
- `4/5 > 2/5`.
|
||||
- `1/2 > 1/4`.
|
||||
- `7/4` est entre `1` et `2`.
|
||||
- Pour le même dénominateur, plus le numérateur est grand, plus on prend de parts.
|
||||
|
||||
## Indices d'orientation
|
||||
|
||||
- Compare seulement les dénominateurs : parcours `01_visuel_partage`.
|
||||
- Ne sait pas encadrer `7/4` : reprendre `04_reperes_demi_droite`.
|
||||
- Bonne comparaison mais justification fragile : parcours `02_verbal_vocabulaire`.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Tests diagnostics - fractions CM1
|
||||
|
||||
Ces tests sont courts et doivent être utilisés avant de choisir un parcours adaptatif.
|
||||
|
||||
Ils ne servent pas à noter l'élève. Ils servent à comprendre quelle aide sera la plus efficace.
|
||||
|
||||
Ordre conseillé :
|
||||
|
||||
1. `01_lecture_vocabulaire.md`
|
||||
2. `02_partage_unite.md`
|
||||
3. `03_demi_droite.md`
|
||||
4. `04_comparer_expliquer.md`
|
||||
|
||||
@@ -150,6 +150,9 @@ function ProgramReviewPanel({ selectedNode, reviewForm, onReviewChange, onSaveRe
|
||||
<span>Contextes: {status.contexts ?? 0}</span>
|
||||
<span>Exercices: {status.text_exercises ?? 0}</span>
|
||||
<span>SVG exercices: {status.exercise_svg ?? 0}</span>
|
||||
<span>Tests adaptatifs: {status.adaptive_tests ?? 0}</span>
|
||||
<span>Parcours: {status.adaptive_profiles ?? 0}</span>
|
||||
<span>SVG kit: {status.adaptive_svg ?? 0}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -160,6 +163,12 @@ function ProgramReviewPanel({ selectedNode, reviewForm, onReviewChange, onSaveRe
|
||||
</button>
|
||||
)}
|
||||
|
||||
{selectedNode.content && !selectedNode.url && (
|
||||
<div className="program-node-text-preview">
|
||||
<pre>{selectedNode.content}</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{review.comment && (
|
||||
<div className="admin-review-note">
|
||||
<strong>Dernier commentaire</strong>
|
||||
@@ -387,6 +396,12 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const [activeSectionIndex, setActiveSectionIndex] = useState(0)
|
||||
const [programStart, setProgramStart] = useState({ asset_index: 0, section_index: 0 })
|
||||
const [cardProgress, setCardProgress] = useState([])
|
||||
const [adaptiveProfile, setAdaptiveProfile] = useState(null)
|
||||
const [adaptiveProfileForm, setAdaptiveProfileForm] = useState({
|
||||
diagnostic_id: '',
|
||||
profile_id: '',
|
||||
notes: '',
|
||||
})
|
||||
const [sessionCardStates, setSessionCardStates] = useState({})
|
||||
const [sessionLogs, setSessionLogs] = useState([])
|
||||
const [selectedSessionLog, setSelectedSessionLog] = useState(null)
|
||||
@@ -540,6 +555,8 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
setProgramLessons([])
|
||||
setProgramStart({ asset_index: 0, section_index: 0 })
|
||||
setCardProgress([])
|
||||
setAdaptiveProfile(null)
|
||||
setAdaptiveProfileForm({ diagnostic_id: '', profile_id: '', notes: '' })
|
||||
setSessionCardStates({})
|
||||
setSessionLogs([])
|
||||
setSelectedSessionLog(null)
|
||||
@@ -576,6 +593,9 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
useEffect(() => {
|
||||
if (selectedStudentId && studentProgram?.id) {
|
||||
loadCardProgress(selectedStudentId, studentProgram.id)
|
||||
if (!isStudentUser) {
|
||||
loadAdaptiveProfile(selectedStudentId, studentProgram.id)
|
||||
}
|
||||
}
|
||||
}, [selectedStudentId, studentProgram?.id])
|
||||
|
||||
@@ -913,6 +933,46 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAdaptiveProfile(studentId, lessonId) {
|
||||
if (!studentId || !lessonId || isStudentUser) return
|
||||
try {
|
||||
const data = await apiFetch(`/admin/students/${studentId}/adaptive-profile?lesson_id=${encodeURIComponent(lessonId)}`)
|
||||
setAdaptiveProfile(data || null)
|
||||
setAdaptiveProfileForm({
|
||||
diagnostic_id: data?.diagnostic_id || '',
|
||||
profile_id: data?.profile_id || '',
|
||||
notes: data?.notes || '',
|
||||
})
|
||||
} catch {
|
||||
setAdaptiveProfile(null)
|
||||
setAdaptiveProfileForm({ diagnostic_id: '', profile_id: '', notes: '' })
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAdaptiveProfile(event) {
|
||||
event.preventDefault()
|
||||
if (!selectedStudentId || !studentProgram?.id || !adaptiveProfileForm.profile_id) return
|
||||
const profile = (studentProgram.adaptive_kit?.profiles || []).find((item) => item.id === adaptiveProfileForm.profile_id)
|
||||
if (!profile) return
|
||||
try {
|
||||
const data = await apiFetch(`/admin/students/${selectedStudentId}/adaptive-profile`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
lesson_id: studentProgram.id,
|
||||
diagnostic_id: adaptiveProfileForm.diagnostic_id || null,
|
||||
profile_id: profile.id,
|
||||
profile_title: profile.title,
|
||||
notes: adaptiveProfileForm.notes,
|
||||
source: 'teacher',
|
||||
}),
|
||||
})
|
||||
setAdaptiveProfile(data)
|
||||
} catch (error) {
|
||||
setErrorMessage(error.message || 'Impossible d’enregistrer le profil adaptatif.')
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSessionLogs(studentId) {
|
||||
if (!studentId || isStudentUser) return
|
||||
try {
|
||||
@@ -1151,6 +1211,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
function getLessonContextPayload() {
|
||||
const { asset, section } = getLessonPosition()
|
||||
return {
|
||||
lesson_id: studentProgram?.id || null,
|
||||
lesson_title: activeLessonTitle,
|
||||
asset_title: asset?.title || null,
|
||||
section_title: section?.title || asset?.title || null,
|
||||
@@ -1553,6 +1614,7 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const lastUserMessage = [...messages].reverse().find((message) => message.role === 'user')
|
||||
const activeLessonTitle = studentProgram?.title || lessonFocus?.label || 'Cours du jour'
|
||||
const lessonAssets = studentProgram?.assets || []
|
||||
const adaptiveKit = studentProgram?.adaptive_kit || null
|
||||
const activeAsset = lessonAssets[activeAssetIndex] || lessonAssets[0] || null
|
||||
const activeSections = activeAsset?.sections || []
|
||||
const activeSection = activeSections[activeSectionIndex] || null
|
||||
@@ -1578,7 +1640,9 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const lessonSummary = lastAssistantMessage?.content || 'Aucune ancienne séance trouvée pour le moment. Professeur TOP commencera par une reprise courte.'
|
||||
const lastStudentWork = lastUserMessage?.content || 'Aucune réponse récente enregistrée.'
|
||||
const nextLessonAdvice = studentProgram
|
||||
? `Conseil: démarrer la leçon ${studentProgram.title} avec les fiches choisies par le professeur.`
|
||||
? adaptiveKit
|
||||
? `Conseil: si l’élève hésite, lancer le diagnostic adaptatif ${adaptiveKit.title.toLowerCase()} avant de poursuivre.`
|
||||
: `Conseil: démarrer la leçon ${studentProgram.title} avec les fiches choisies par le professeur.`
|
||||
: lessonFocus
|
||||
? `Conseil: reprendre ${lessonFocus.label}, puis lancer un exercice court pour équilibrer la progression.`
|
||||
: 'Conseil: démarrer une séance pour laisser Professeur TOP choisir la prochaine leçon.'
|
||||
@@ -1792,7 +1856,9 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
const averageComprehension = cardProgress.length
|
||||
? Math.round(cardProgress.reduce((sum, item) => sum + item.comprehension_score, 0) / cardProgress.length)
|
||||
: 0
|
||||
const reviewRows = programRows.filter(({ node }) => node.type === 'lesson' || node.type === 'fiche' || node.type === 'card')
|
||||
const reviewRows = programRows.filter(({ node }) =>
|
||||
['lesson', 'fiche', 'card', 'kit', 'diagnostic', 'parcours', 'support'].includes(node.type)
|
||||
)
|
||||
const reviewQueue = reviewRows.filter(({ node }) => ['needs_changes', 'blocked', 'draft', undefined].includes(node.review?.status))
|
||||
const selectedNodeStatus = selectedProgramNode?.status || {}
|
||||
const totalLessonCards = studentProgram?.assets?.reduce((sum, asset) => sum + (asset.sections?.length || 0), 0) || 0
|
||||
@@ -1967,6 +2033,17 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
<strong>{validatedCards}</strong>
|
||||
<p>{remainingCards} card(s) encore à travailler</p>
|
||||
</article>
|
||||
<article>
|
||||
<span>Kit adaptatif</span>
|
||||
<strong>{adaptiveProfile?.profile_title || (adaptiveKit ? 'Disponible' : 'Absent')}</strong>
|
||||
<p>
|
||||
{adaptiveProfile
|
||||
? 'Profil de reprise enregistré'
|
||||
: adaptiveKit
|
||||
? `${adaptiveKit.diagnostics?.length || 0} test(s), ${adaptiveKit.profiles?.length || 0} parcours`
|
||||
: 'Aucun diagnostic relié à cette leçon.'}
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div className="student-settings-grid">
|
||||
@@ -2013,6 +2090,77 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{adaptiveKit && (
|
||||
<section className="adaptive-kit-panel">
|
||||
<div className="admin-panel-head">
|
||||
<div>
|
||||
<span className="stage-label">Adaptatif</span>
|
||||
<h2>Diagnostic et parcours disponibles</h2>
|
||||
</div>
|
||||
<button type="button" className="secondary-button" onClick={() => setActiveAdminTab('validation')}>
|
||||
Valider le kit
|
||||
</button>
|
||||
</div>
|
||||
<div className="adaptive-kit-grid">
|
||||
<article>
|
||||
<strong>Tests diagnostics</strong>
|
||||
{(adaptiveKit.diagnostics || []).map((item) => (
|
||||
<span key={item.id}>{item.title}</span>
|
||||
))}
|
||||
</article>
|
||||
<article>
|
||||
<strong>Parcours profils</strong>
|
||||
{(adaptiveKit.profiles || []).map((item) => (
|
||||
<span key={item.id}>{item.title}</span>
|
||||
))}
|
||||
</article>
|
||||
<article>
|
||||
<strong>Supports visuels</strong>
|
||||
{(adaptiveKit.supports || []).map((item) => (
|
||||
<span key={item.id}>{item.title}</span>
|
||||
))}
|
||||
</article>
|
||||
</div>
|
||||
<form className="adaptive-profile-form" onSubmit={saveAdaptiveProfile}>
|
||||
<label>
|
||||
<span>Test diagnostic de référence</span>
|
||||
<select
|
||||
value={adaptiveProfileForm.diagnostic_id}
|
||||
onChange={(event) => setAdaptiveProfileForm({ ...adaptiveProfileForm, diagnostic_id: event.target.value })}
|
||||
>
|
||||
<option value="">Non précisé</option>
|
||||
{(adaptiveKit.diagnostics || []).map((item) => (
|
||||
<option key={item.id} value={item.id}>{item.title}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Parcours recommandé</span>
|
||||
<select
|
||||
value={adaptiveProfileForm.profile_id}
|
||||
onChange={(event) => setAdaptiveProfileForm({ ...adaptiveProfileForm, profile_id: event.target.value })}
|
||||
>
|
||||
<option value="">Choisir un parcours</option>
|
||||
{(adaptiveKit.profiles || []).map((item) => (
|
||||
<option key={item.id} value={item.id}>{item.title}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="wide-field">
|
||||
<span>Note de suivi</span>
|
||||
<textarea
|
||||
value={adaptiveProfileForm.notes}
|
||||
onChange={(event) => setAdaptiveProfileForm({ ...adaptiveProfileForm, notes: event.target.value })}
|
||||
placeholder="Exemple: inverse encore numérateur/dénominateur, commencer par le parcours verbal."
|
||||
/>
|
||||
</label>
|
||||
<button type="submit" disabled={!adaptiveProfileForm.profile_id}>
|
||||
Enregistrer le profil adaptatif
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<div className="admin-dashboard-grid">
|
||||
<section className="admin-preview">
|
||||
<h2>Compréhension des cards</h2>
|
||||
@@ -2150,11 +2298,13 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
{selectedProgramNode?.review?.status && <span className={`review-pill ${selectedProgramNode.review.status}`}>{selectedProgramNode.review.status}</span>}
|
||||
</div>
|
||||
<div className="validation-main-preview">
|
||||
{selectedProgramNode?.url ? (
|
||||
<img src={selectedProgramNode.url} alt={selectedProgramNode.label} />
|
||||
) : (
|
||||
<div className="empty-preview">Aucun visuel direct pour cet élément.</div>
|
||||
)}
|
||||
{selectedProgramNode?.url ? (
|
||||
<img src={selectedProgramNode.url} alt={selectedProgramNode.label} />
|
||||
) : selectedProgramNode?.content ? (
|
||||
<pre className="validation-text-preview">{selectedProgramNode.content}</pre>
|
||||
) : (
|
||||
<div className="empty-preview">Aucun visuel direct pour cet élément.</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -2167,6 +2317,9 @@ function TutorApp({ currentUser, onLogout }) {
|
||||
<span>Contextes: {selectedNodeStatus.contexts ?? '-'}</span>
|
||||
<span>Exercices: {selectedNodeStatus.text_exercises ?? '-'}</span>
|
||||
<span>SVG ex.: {selectedNodeStatus.exercise_svg ?? '-'}</span>
|
||||
<span>Tests: {selectedNodeStatus.adaptive_tests ?? '-'}</span>
|
||||
<span>Parcours: {selectedNodeStatus.adaptive_profiles ?? '-'}</span>
|
||||
<span>SVG kit: {selectedNodeStatus.adaptive_svg ?? '-'}</span>
|
||||
</div>
|
||||
<form className="admin-review-form" onSubmit={saveProgramReview}>
|
||||
<label>
|
||||
|
||||
@@ -216,7 +216,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
}
|
||||
|
||||
.student-report-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.student-report-grid article {
|
||||
@@ -350,6 +350,67 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.adaptive-kit-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
border: 1px solid #dbeafe;
|
||||
border-radius: 16px;
|
||||
background: #f8fbff;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.adaptive-kit-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.adaptive-kit-grid article {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.4rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 14px;
|
||||
background: white;
|
||||
padding: 0.85rem;
|
||||
}
|
||||
|
||||
.adaptive-kit-grid span {
|
||||
color: #475569;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.adaptive-profile-form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
border-top: 1px solid #dbeafe;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.adaptive-profile-form label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.adaptive-profile-form .wide-field,
|
||||
.adaptive-profile-form button {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.adaptive-profile-form textarea {
|
||||
min-height: 96px;
|
||||
resize: vertical;
|
||||
border: 1px solid #cfd7e6;
|
||||
border-radius: 12px;
|
||||
padding: 0.8rem 1rem;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.review-fullscreen {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -687,6 +748,23 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
font-size: 0.82rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
.program-node-text-preview,
|
||||
.validation-text-preview {
|
||||
min-height: 260px;
|
||||
max-height: 520px;
|
||||
overflow: auto;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
background: white;
|
||||
padding: 1rem;
|
||||
}
|
||||
.program-node-text-preview pre,
|
||||
.validation-text-preview {
|
||||
margin: 0;
|
||||
color: #14213d;
|
||||
font: 0.95rem/1.55 Inter, system-ui, sans-serif;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.admin-review-note p {
|
||||
margin: 0.35rem 0;
|
||||
line-height: 1.45;
|
||||
@@ -1218,6 +1296,8 @@ button:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.admin-students-layout,
|
||||
.student-report-grid,
|
||||
.student-settings-grid,
|
||||
.adaptive-kit-grid,
|
||||
.adaptive-profile-form,
|
||||
.validation-layout,
|
||||
.admin-program-browser {
|
||||
grid-template-columns: 1fr;
|
||||
|
||||
Reference in New Issue
Block a user