This commit is contained in:
2026-05-06 19:55:37 +02:00
parent 7a7994e8b6
commit b41993c455
43 changed files with 1563 additions and 14 deletions

View File

@@ -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)

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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é.

View File

@@ -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.

View File

@@ -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`.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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`.

View File

@@ -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`