Prompt file imported from Binwinwinw/moncoachscolaire (
.github/prompts/quiz-generator-python.prompt.md). Copyright stays with the author.
@workspace Génère ou corrige un script Python generate_<matiere>_<niveau>.py selon le pattern standard MonCoachScolaire.
Contexte
Les scripts de génération de quiz se trouvent dans dev/tools/quiz/.
Chaque script produit deux séries de fichiers JSON dans un dossier <matiere>_<niveau>_quizzes/ :
quiz/<id>.json— questions sans réponses (affichage côté élève)quiz_answers/<id>.json— réponses + explications (validation côté API)
Voir dev\tools\quiz\enrichment\generator\generate_0templateIA.py et dev\tools\quiz\enrichment\generator\generate_0template.py comme référence.
Résumé du workflow
- Créer un script
generate_<matiere>_<niveau>.pyavecSCRIPT_DIRrelatif. - Générer deux dossiers :
quiz/etquiz_answers/. - Supporter seulement
qcmetvrai-faux. - Écrire
created_aten UTC ISO 8601 et rejeter tout type invalide.
Checklist de correction (7 points obligatoires)
Pour les nouveaux scripts, ne générer que les types qcm et vrai-faux.
Avant de générer ou corriger un script, vérifier chaque point :
-
Import datetime moderne
from datetime import UTC, datetimeInterdit :
datetime.utcnow(),import datetimeseul. -
Constantes de chemin SCRIPT_DIR-relatifs
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) XXX_OUTPUT_DIR = os.path.join(SCRIPT_DIR, "<matiere>_<niveau>_quizzes") XXX_QUIZ_DIR = os.path.join(XXX_OUTPUT_DIR, "quiz") XXX_ANSWERS_DIR = os.path.join(XXX_OUTPUT_DIR, "quiz_answers")Interdit : chemins codés en dur comme
"quizzes"ou"answers". -
datetime ISO 8601 UTC dans make_quiz
"created_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"), -
Dispatch complet dans make_answers Les deux branches valides sont :
if q["type"] == "qcm": → correct_option elif q["type"] == "vrai-faux": → correct (bool) else: → raise ValueError(...) -
Type
"vrai-faux"avec tiret (jamais"vrai_faux") Vérifier globalement avecgrep -n "vrai_faux"— aucune occurrence acceptée. -
Complétion des IDs jusqu'au max déclaré Si le bloc de commentaire annonce IDs 699–746, 48 tuples doivent être présents. Compter avec :
len(quizzes_data). -
**Pipeline write_quiz_files() + if **name****
def write_quiz_files(): os.makedirs(XXX_QUIZ_DIR, exist_ok=True) os.makedirs(XXX_ANSWERS_DIR, exist_ok=True) for qid, title, subject, level, questions in quizzes_data: quiz = make_quiz(qid, title, subject, level, questions) answers = make_answers(qid, title, subject, level, questions) with open(os.path.join(XXX_QUIZ_DIR, f"{qid}.json"), "w", encoding="utf-8") as f: json.dump(quiz, f, ensure_ascii=False, indent=2) with open(os.path.join(XXX_ANSWERS_DIR, f"{qid}.json"), "w", encoding="utf-8") as f: json.dump(answers, f, ensure_ascii=False, indent=2) print(f"{len(quizzes_data)} quiz générés dans {XXX_OUTPUT_DIR}") if __name__ == "__main__": write_quiz_files()
Template complet d'un nouveau script
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Génération des quiz <Matière> <Niveau> – IDs <min>–<max>
<N> quizzes x 8 questions = <N*8> questions
Thèmes : <liste thèmes>
Pattern : qcm, vrai-faux, qcm, vrai-faux, qcm, vrai-faux, qcm, vrai-faux
"""
import json
import os
from datetime import UTC, datetime
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
XXX_OUTPUT_DIR = os.path.join(SCRIPT_DIR, "<matiere>_<niveau>_quizzes")
XXX_QUIZ_DIR = os.path.join(XXX_OUTPUT_DIR, "quiz")
XXX_ANSWERS_DIR = os.path.join(XXX_OUTPUT_DIR, "quiz_answers")
os.makedirs(XXX_QUIZ_DIR, exist_ok=True)
os.makedirs(XXX_ANSWERS_DIR, exist_ok=True)
def make_quiz(qid, title, subject, level, questions):
return {
"id": qid,
"title": title,
"subject": subject,
"level": level,
"created_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
"questions": [
{k: v for k, v in q.items()
if k not in ("correct_option", "correct", "correct_answer", "explanation")}
for q in questions
]
}
def make_answers(qid, title, subject, level, questions):
answers = []
for q in questions:
if q["type"] == "qcm":
answers.append({
"question_id": q["id"],
"correct_option": q["correct_option"],
"explanation": q["explanation"]
})
elif q["type"] == "vrai-faux":
answers.append({
"question_id": q["id"],
"correct": q["correct"],
"explanation": q["explanation"]
})
else:
raise ValueError(f"Type de question invalide : {q['type']}. Utiliser uniquement 'qcm' ou 'vrai-faux'.")
return {
"quiz_id": qid,
"title": title,
"subject": subject,
"level": level,
"answers": answers
}
quizzes_data = [
# =========================================================
# BLOC 1 – <THEME> (<ID_MIN>–<ID_MAX>)
# =========================================================
(<ID>, "<Titre du quiz>", "<Matière>", "<Niveau>", [
{"id": "<ID>_1", "type": "qcm",
"question": "...",
"options": ["Option A", "Option B", "Option C", "Option D"],
"correct_option": "Option A",
"explanation": "Explication pédagogique complète."},
{"id": "<ID>_2", "type": "vrai-faux",
"question": "Affirmation vraie ou fausse ?",
"correct": True,
"explanation": "Explication pédagogique complète."},
{"id": "<ID>_3", "type": "qcm",
"question": "Question à choix multiples ?",
"options": ["Option A", "Option B", "Option C", "Option D"],
"correct_option": "Option A",
"explanation": "Explication pédagogique complète."},
# … 8 questions par quiz
]),
]
def write_quiz_files():
os.makedirs(XXX_QUIZ_DIR, exist_ok=True)
os.makedirs(XXX_ANSWERS_DIR, exist_ok=True)
for qid, title, subject, level, questions in quizzes_data:
quiz = make_quiz(qid, title, subject, level, questions)
answers = make_answers(qid, title, subject, level, questions)
with open(os.path.join(XXX_QUIZ_DIR, f"{qid}.json"), "w", encoding="utf-8") as f:
json.dump(quiz, f, ensure_ascii=False, indent=2)
with open(os.path.join(XXX_ANSWERS_DIR, f"{qid}.json"), "w", encoding="utf-8") as f:
json.dump(answers, f, ensure_ascii=False, indent=2)
print(f"{len(quizzes_data)} quiz <Matière> <Niveau> générés dans {XXX_OUTPUT_DIR}")
if __name__ == "__main__":
write_quiz_files()
Structure d'une question par type
QCM
{"id": "700_1", "type": "qcm",
"question": "Question à choix multiples ?",
"options": ["Option A", "Option B", "Option C", "Option D"],
"correct_option": "Option A",
"explanation": "Explication complète et pédagogique."}
Vrai-Faux
{"id": "700_2", "type": "vrai-faux",
"question": "Affirmation à valider ou infirmer.",
"correct": True, # ou False
"explanation": "Explication complète et pédagogique."}
Commande de validation après génération
# Exécution
.venv\Scripts\python.exe dev/tools/quiz/generate_<matiere>_<niveau>.py
# Vérification du nombre de fichiers générés (Windows PowerShell)
(Get-ChildItem "dev/tools/quiz/<matiere>_<niveau>_quizzes/quiz/*.json").Count
(Get-ChildItem "dev/tools/quiz/<matiere>_<niveau>_quizzes/quiz_answers/*.json").Count
Les deux comptages doivent être égaux à len(quizzes_data).
Répartition des IDs dans le projet
| Script | IDs | Matière | Niveau |
|---|---|---|---|
generate_svt_3eme.py |
651–698 | SVT | 3ème |
generate_pc_3eme.py |
699–746 | Physique-Chimie | 3ème |
| (prochain script) | 747–… | À définir | À définir |
Toujours vérifier l'ID max existant avant de créer un nouveau script.