Créer un QCM et les corriger automatiquement, c’est une chose que l’on peut faire avec https://www.auto-multiple-choice.net/fr/install/, mais ceci n’est installable facilement que sur Linux ou MacOS. L’installer sous windows nécessite d’installer un émulateur Linux.
J’envisage donc de me lancer dans un projet, mais je ne sais pas trop où je vais aller…
Créer un QCM et les corriger automatiquement: l’idée générale
Dans cet article, je vous expliquais comment créer un QCM en \(\LaTeX\) à l’aide de Python, mais ce dernier n’était pas interactif.
Là, le projet est de créer des QCM interactifs (donc, sur lesquels ont peut choisir une réponse ou insérer un texte) qui pourraient être corriger automatiquement.
L’idée est de ne pas s’embêter, donc de partir d’un fichier texte classique avec une structure de la forme:
title=QCM sur n'importe quoi
style=text
Saisir un chiffre impair
1,3,5,7,9
style=qcm
Le triangle de dimensions 3 cm, 4 cm et 5 cm est-il rectangle ?
Oui,Non
Oui
Chaque question est déterminée par la ligne : “style=…“. On aurait le choix entre:
- “text” si la réponse attendue est à saisir au clavier
- “qcm” si la question est à choix multiple
Créer un QCM et les corriger automatiquement: le traitement en Python
Assumons le fait que le fichier précédent soit nommé questionnaire.txt.
Le premier script Python serait chargé de lire ce fichier, de construire un fichier \(\LaTeX\) et de le compiler.
Ensuite, il faut un second script Python pour lire le PDF complété et vérifier si les réponses sont correctes ou non.
Pour les deux scripts, il me faut une fonction commune, celle qui permet de lire le fichier texte (questionnaire.txt pour notre exemple), qui en extrait les données essentielles et les renvoie sous forme de dictionnaire. Ce sera la fonction suivante, que je vais mettre dans le fichier funcaux.py:
def parse_questionnaire(file_path):
with open(file_path, 'r') as file:
lines = file.readlines()
data = {}
current_style = None
question = None
options = None
for line in lines:
line = line.strip()
if not line:
continue
if line.startswith('title='):
data['title'] = line.split('=', 1)[1].strip()
elif line.startswith('style='):
current_style = line.split('=', 1)[1].strip()
else:
if current_style == 'text':
if question is None:
question = line
else:
data[question] = {'type': current_style, 'answers': line}
question = None
elif current_style == 'qcm':
if question is None:
question = line
elif options is None:
options = line.split(',')
else:
if question not in data:
data[question] = {'type': current_style, 'options': options, 'correct_answer': line}
question = None
options = None
return data
Construction du QCM en \(\LaTeX\)
Le premier script Python sera mis dans le fichier create_qcm.py:
import os
from funcaux import parse_questionnaire
"""
Fonction qui créé le fichier tex
"""
def tex(dic, answer = False):
title = False
tex = r"""\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{fourier}
\usepackage{hyperref}
\usepackage{tabularx}
\usepackage{cellspace}
\setlength{\cellspacetoplimit}{3mm}
\setlength{\cellspacebottomlimit}{1mm}
\usepackage[hmargin=1cm, vmargin=2cm]{geometry}
\newcounter{questions}
\setcounter{questions}{0}
\newcommand{\question}{\stepcounter{questions}\arabic{questions}}
\setlength{\parindent}{0pt}
\begin{document}
"""
if 'title' in questionnaire_data.keys():
tex += "\\title{"+dic['title']+"}\n\\date{}\\maketitle\n\begin{Form}\n"
del dic['title']
elif answer == True:
tex += "\\title{Correction}\n\\date{}\\maketitle\n"
tex += r"""\begin{tabularx}{\linewidth}{|Sc|X|Sl|}
\hline"""
for key, value in dic.items():
if value['type'] == 'text':
if answer == False:
tex += "\\question & " + key + " & \\TextField[name=q\\arabic{questions},align=1, height=0.4em]{} \\\\\n\hline\n"
else:
tex += "\\question & " + key + " & "+value['answers']+" \\\\\n\hline\n"
elif value['type'] == 'qcm':
tex += "\\question & " + key + " & \\begin{minipage}{30mm}\n"
if answer == False:
tex += "\\ChoiceMenu[combo,name=q\\arabic{questions}, width=1em]{}{"
for v in value['options']:
tex += v + ","
tex = tex[:-1] # pour supprimer la dernière virgule
tex += "}\n"
else:
tex += value['correct_answer'] + "\n"
tex += "\\end{minipage}\\\\\n"
tex += r"""\hline
\end{tabularx}"""
if answer == False:
tex += "\end{Form}\n"
tex += "\end{document}"
return tex
if __name__ == '__main__':
file_path = 'questionnaire.txt'
questionnaire_data = parse_questionnaire(file_path)
doc_tex = tex(questionnaire_data)
with open('questionnaire.tex', 'w', encoding='utf-8') as file:
file.write(doc_tex)
# compilation PdfLaTeX dans le répertoire courant
cmd = 'echo Compilation du fichier '+doc_tex
cmd = "pdflatex -shell-escape -synctex=1 -interaction=nonstopmode questionnaire.tex"
os.system(cmd)
# Pour le QCM corrigé
doc_tex_corr = tex(questionnaire_data,answer=True)
with open('questionnaire_corr.tex', 'w', encoding='utf-8') as file:
file.write(doc_tex_corr)
# compilation PdfLaTeX dans le répertoire courant
cmd = 'echo Compilation du fichier '+doc_tex
cmd = "pdflatex -shell-escape -synctex=1 -interaction=nonstopmode questionnaire_corr.tex"
os.system(cmd)
Le script génère via pdfLaTeX le PDF. Avec le fichier texte précédent, cela donne:
Si on choisit de le sauvegarder sous le nom de questionnaire_complete.pdf, on aura ceci:
Traitement du PDF complété en Python
Le second script est pis dans le fichier correction.py:
import pypdf
import re
from funcaux import parse_questionnaire
def clean_value(value):
# Supprimer les caractères de contrôle et les séquences d'échappement
if isinstance(value, str):
# Utiliser une expression régulière pour supprimer les caractères non alphabétiques
value = re.sub(r'[^a-zA-Z]', '', value)
return value
def format_pdf_fields(fields):
formatted_output = {}
for key, value in fields.items():
if value.get('/FT') == '/Tx': # Champ de texte
formatted_output[key] = value.get('/V', '')
elif value.get('/FT') == '/Ch': # Champ de choix (combo ou liste)
if '/V' in value:
formatted_output[key] = value['/V']
return formatted_output
def correction(file_path):
# Récupération du formulaire du PDF rendu
rendu = 'questionnaire_complete.pdf'
reader = pypdf.PdfReader(rendu)
output = reader.get_fields()
# Formatage de la sortie
formatted_output = format_pdf_fields(output)
#print( formatted_output )
# Lecture du fichier texte
questionnaire_data = parse_questionnaire(file_path)
#print(questionnaire_data)
# Analyse des réponses
dico_correction = {}
question = 0
for key, value in questionnaire_data.items():
if key != 'title':
question += 1
key_output = f"q{question}"
if value['type'] == 'text':
possible_answers = [x for x in value['answers'].split(',')]
if formatted_output[key_output] in possible_answers:
dico_correction[key_output] = True
else:
dico_correction[key_output] = False
if value['type'] == 'qcm':
if formatted_output[key_output] == value['correct_answer']:
dico_correction[key_output] = True
else:
dico_correction[key_output] = False
return dico_correction
if __name__ == '__main__':
print( correction( 'questionnaire.txt' ) )
Ce dernier affiche:
{'q1': True, 'q2': True}
Et la suite…
Ce n’est qu’une base et si cela intéresse beaucoup de personnes, alors je pourrais faire évoluer ce projet.
On peut imaginer par exemple que chaque élève sauvegarde sont PDF avec le nom et le prénom, et l’envoie à l’enseignant(e). L’enseignant peut alors mettre les QCM dans un répertoire et faire tourner un script chargé de tout corriger.
Pour la création de QCM sur écrans, il existe de nombreuses solutions. L’intérêt d’Auto Multiple Choice (AMC) est de permettre de faire remplir les questionnaires à la main, puis de les scanner et de les corriger automatiquement. C’est un outil extrêmement utile pour l’évaluation. Cependant, il n’est pas disponible sous Windows. Or, les sources du programme sont ouvertes ; les porter sur Windows serait donc un service précieux pour ceux qui n’utilisent ni Linux ni macOS.
Le projet Auto Multiple Choice est distribué sous licence libre (AGPL v1.0) et hébergé sur GitLab, ce qui signifie que les contributions externes sont les bienvenues et que vous pouvez tout à fait proposer un portage Windows.
https://gitlab.com/a10684/auto-multiple-choice/
Malheureusement, je ne peux pas contribuer car mes connaissances sont tout de même limitées et je ne saurais pas exploiter les sources. Il serait en effet bon que quelqu’un de compétent puisse faire un programme qui tourne sous Windows, mais quand je regard les sources, je ne comprends même pas ce qu’il faut faire: il y a tellement d’extensions que je ne connais pas… C’est dommage car ça a l’air en effet très bien!