nbgrader : évaluation et correction automatique de classeurs

nbgrader est un outil développé par et pour les professeurs pour créer et évaluer des devoirs riches et interactifs dans des classeurs Jupyter.

nbgrader peut être utilisé seul pour créer des devoirs. Il permet à partir d'une version complète rédigée par le professeur d'extraire automatiquement un document que l'élève devra compléter et rendre. Le professeur aura la charge de collecter tous les travaux élèves et de les placer dans une arborescence adéquate. A l'issue de quoi, nbgrader pourra réaliser l'évaluation automatique de tous les travaux élèves et les noter selon le barême établi par le professeur. Le professeur peut prévoir des questions plus ouvertes qu'il évaluera manuellement, en complément de la procédure automatique.

Lorsqu'il est utilisé conjointement avec jupyterHub (version multi-utilisateurs de jupyter), la procédure de distribution et de récupération des devoirs est automatisée ce qui rend l'ensemble du processus totalement automatique !

Dans la vidéo ci-dessous - en anglais - vous aurez une démonstration des potentialités de cet outil.

nbgrader A Tool for Creating and Grading Assignments in the Jupyter Notebook

ComplémentDocumentation officielle de nbgrader

Pour plus d'informations sur nbgrader, je vous renvoie à la documentation en ligne.

https://nbgrader.readthedocs.io/en/stable/

Attention

Ce guide s'adresse à des personnes à l'aise avec python, jupyter et l'installation de modules avec PIP.

nbgrader sur Jupyter

Nous supposerons dans cette partie que vous disposez d'une installation Jupyter opérationnelle. J'ai réalisé tous mes tests sous Linux à partir d'une image Docker jupyter/minimal-notebook.

Il n'est bien sûr pas nécessaire d'utiliser docker et vous pouvez tout à fait utiliser votre installation habituelle de Jupyter (anaconda ou autre).

Installation de nbgrader

MéthodeInstallation sur le système

nbgrader nécessite la version 1.2.x de SQLAlchemy. Il ne fonctionnera pas avec la version 1.3 ou ultérieure. Il faut donc commencer par installer cette version particulière

1
pip install SQLAlchemy==1.2.19

On installe ensuite nbgrader de manière tout à fait classique

1
pip install nbgrader

On active enfin l'extension dans jupyter :

1
jupyter nbextension install --sys-prefix --py nbgrader --overwrite
2
jupyter nbextension enable --sys-prefix --py nbgrader
3
jupyter serverextension enable --sys-prefix --py nbgrader

Créez le dossier d'échange

1
mkdir -p /srv/nbgrader/exchange
2
chmod ugo+rw /srv/nbgrader/exchange
MéthodeConfiguration initiale

Créer le fichier de configuration nbgrader_config.py dans le dossier .jupyter de votre espace personnel (ou autre dossier dans le path de jupyter) avec le contenu suivant.

1
c = get_config()
2
c.Exchange.course_id = "cours_MON_LOGIN"
3
c.Exchange.root = '/home/MON_LOGIN'
4
c.IncludeHeaderFooter.header = "source/header.ipynb"

Si vous n'utilisez pas docker, vous adapterez le chemin '/home/MON_LOGIN' en indiquant l'emplacement de vos classeurs jupyter.

MéthodeCréer un fichier d'entête

Le fichier de configuration fait référence à un classeur source/header.ipynb qu'il faut créer dans votre arborescence Jupyter. Cette entête sera ajoutée systématiquement aux classeurs qui seront générés pour les élèves. Il peut par exemple contenir une cellule markdown avec un titre et des instructions pour la remise du travail au professeur via l'ENT.

Il faut créer ce fichier, même de manière très sommaire, avant de pouvoir commencer à utiliser nbgrader. Une fois cette étape réalisée, vous pouvez commencer la découverte de ce formidable outil ! A ce stade, votre environnement jupyter doit ressembler à ceci :

Vous remarqez la présence

  • du dossier source qui contient header.ipynb

  • des boutons Formgrader et Assignments dans les onglets du haut

L'installation est terminée. Redémarrez Jupyter afin de prendre en compte les modifications.

Utiliser nbgrader

Dans cette partie, nous allons

  • créer un devoir avec nbgrader

  • créer deux élèves factices

  • leur distribuer le devoir

  • fabriquer les réponses et les récupérer

  • évaluer les réponses

Créer un devoir
Méthode

Cliquez sur le bouton Formgrader. La fenêtre suivante doit apparaître. Si vous avez une erreur 404, peut-être n'avez-vous pas redémarré Jupyter suite à l'installation ?

Cliquez sur le bouton Add new assignment pour créer un nouveau devoir.

Saisissez le nom du devoir SANS ESPACES ni caractères spéciaux. Disons testPremier.

Notre devoir est à l'état de brouillon. Nous devons le fabriquer en cliquant sur testPremier. testPremier est un dossier créé dans l'arborescence Jupyter dans le dossier source. Il peut contenir un ou plusieurs classeurs notebook.

ExempleÉcrire une fonction de test de primalité

Dans notre exemple, nous allons demander aux élèves d'écrire une fonction testant si un entier n passé en paramètres est premier.

Le professeur va écrire ses instructions et la fonction qu'il souhaite faire réaliser :

1
from math import sqrt
2
def isPrime(n):
3
    ### BEGIN SOLUTION
4
    if n==2 or n==3:
5
        return True
6
    if n%2 == 0 or n<=1:
7
        return False
8
    d=3
9
    while n%d!=0 and d <= sqrt(n):
10
        d+=2
11
    return n%d!=0
12
    ### END SOLUTION
AttentionBalise BEGIN et END SOLUTION

Vous remarquez dans le code professeur les commentaires ### BEGIN SOLUTION et ### END SOLUTION

Ces commentaires indiquent à nbgrader le code qui devra être retiré des classeurs élèves. Il est important de respecter cette syntaxe à la lettre.

ExempleÉcrire les tests de validation

Il faut à présent écrire les tests permettant de valider la fonction écrite par l'élève. On utilise pour cela la commande assert.

Voici un exemple

1
assert isPrime(5) == True
2
assert isPrime(9) == False
3
assert isPrime(17) == True
4
assert isPrime(21) == False
5
### BEGIN HIDDEN TESTS
6
assert isPrime(0) == False
7
assert isPrime(1) == False
8
assert isPrime(2) == True
9
assert isPrime(3) == True
10
### END HIDDEN TESTS
AttentionBalises BEGIN et END HIDDEN TESTS

Vous remarquez la encore la présence de balises spéciales qu'il faudra reproduire à l'identique : La partie du code comprise entre ### BEGIN HIDDEN TESTS et ### END HIDDEN TESTS sera masquée sur les classeurs élèves. Cela permet de ne pas leur dévoiler sur quelles valeurs sera évaluée leur fonction et ainsi tester quelques cas limites auxquels ils n'auraient pas forcément pensé.

Le classeur du professeur est terminé. Voilà ce que vous devez obtenir à ce stade :

MéthodeEditez les métadonnées des cellules

A présent il faut indiquer quelle cellule devra être évaluée, quelle cellule devra être utilisée par les tests et quelles cellules ne doivent pas être modifiées. Pour cela, il faut commencer par faire apparaître la barre d'outils des cellules dans le menu View / Cell Toolbar / Create assigment

Sélectionnez

  • Autograded answer pour la cellule contenant la fonction isPrime à compléter par les élèves

  • Autograder tests pour la cellule contenant les tests à réaliser. Vous indiquez alors le nb de points désiré.

  • Read-only pour les cellules qui ne devront pas être modifiées.

Voici ce que cela donne :

ComplémentVérification

Cliquez sur le bouton Validate dans la barre d'icônes pour vérifier que votre fonction passe bien les tests ! ! Le devoir est terminé, tout est prêt pour l'étape suivante. N'oubliez pas d'enregistrer le classeur avant de quitter la page.

Créer deux élèves factices

Il nous manque des élèves ! nous allons en créer deux. Pour cela, sur la page d'acceuil de Jupyter, retournez sur l'outil Formgrader puis Manage Students. Cliquez sur Add new student pour ajouter un élève.

Générer le devoir pour les élèves

Retourner sur la page Formgrader

puis cliquez sur le bouton generate. La fenêtre de log vous indique que tout s'est bien passé - j'espère ! et deux nouvelles icônes sont apparues. Cliquez sur la loupe (preview) pour visualiser le classeur que vous distribuez aux élèves.

Comme vous le voyez :

  • L'entête header à été ajoutée

  • Le code solution a été enlevé et remplacé par un commentaire invitant les élèves à compléter

  • Le code de validation est proposé sans la partie que vous avez choisi de masquer

  • Les cellules marquées en lecture seule ne sont pas modifiables !

Distribuer le devoir

Le devoir ainsi généré se trouve dans un dossier release créé pour l'occasion. Il contient le dossier testPremier avec le fichier DS1.ipynb.

Cliquez sur le bouton Release.

A vous de distribuer le ou les documents aux élèves par le moyen de votre choix (messagerie, ENT etc...).

Remarque

C'est sur cette partie de distribution et la suivante pour la récupération que l'outil jupyterHub se révèle particulièrement pertinent car il automatise complètement cette partie. Nous verrons la configuration de jupyterHub un peu plus tard dans ce tutoriel. Néanmoins il n'est pas nécessaire de l'utiliser. C'est ce que nous allons voir maintenant.

Récupérer les travaux des élèves
Fondamental

La procédure décrite ici concerne les installations hors jupyterHub. Si vous disposez d'un serveur jupyterhub, la récupération des travaux élève est automatique en cliquant sur le bouton collect.

La récupération des travaux se fait au travers d'une arborescence spécifique : 

Méthode
  • Créez un dossier submitted à la racine de l'arborescence

  • Créez deux sous dossiers eleve1 et eleve2 (autant que d'élèves créés en fait...)

  • Dans chaque sous dossier élève, copiez l'arborescence de votre devoir, donc dans notre exemple testPremier/DS1.ipynb

  • Complétez pour les deux élèves les fichiers DS1 avec des réponses. Par exemple eleve1 peut bien répondre et eleve2 va faire n'importe quoi ! Voici ce que je propose comme réponse pour chacun d'eux :

Exempleeleve1

L'élève 1 complète la fonction isPrime, peut créer des nouvelles cellules pour tester son code, et clique sur le bouton Validate pour s'assurer qu'il passe les tests.

1
def isPrime(n):
2
    L=1
3
    if n in {2,3, 5, 7, 11, 13, 17, 19, 23, 29, 31}:
4
        return True
5
    elif n<=1 or n%2==0 or n%3==0 or n%5==0:
6
        return False
7
    else:
8
        while L*L<n:
9
            for inc in [6,4,2,4,2,4,6,2]:
10
                L+=inc
11
                if n%L==0:
12
                    return False
13
        return True
Exempleeleve2

L'élève 2 procède de même mais croit faire le malin en examinant les vérifications du professeur :) En apparence, la cellule de vérification s'exécute sans erreurs.

1
def isPrime(n):
2
    return n in [5,17]

Mais il ne sait pas que le professeur à d'autre tests cachés :)

Les productions des élèves sont récupérées dans l'arborescence :

1
submitted
2
|_ eleve1
3
   |_ testPremier
4
      |_ DS1.ipynb
5
|_ eleve2
6
   |_ testPremier
7
      |_ DS1.ipynb
Complément

La récupération des productions d'élèves est assurément la partie la plus fastidieuse de tout le processus. Néanmoins avec un peu de pratique des langages de script (bash par exemple), il est possible d'automatiser le dépôt des productions élèves dans l'arborescence. En effet, notre ENT nous fournit un zip contenant une arborescence un peu similaire avec un dossier du nom des élèves contenant le fichier à rendre.

Évaluer les réponses automatiquement

Vient le moment le plus agréable : la correction ! ! Retournez sur Formgrader, les 2 productions apparaissent dans la colonne Submissions

Cliquez sur le chiffre 2 pour voir les réponses.

En cliquant sur l'éclair vous déclencherez la correction pour l'élève. Néanmoins cela peut être fastidieux de le faire pour 24. Cela peut être automatisé en déployant la zone Instructions puis cliquant sur le lien command line pour faire apparaître une console. Il suffira juste de taper la commande indiquée pour corriger tous les élèves d'un coup !

1
nbgrader autograde "testPremier"

Fermez la console et revenez sur la page de soumissions : les élèves sont corrigés :)

eleve1 a eu 2/2 et eleve2 qui a cru faire le malin a eu 0/2 car son programme foireux n'a pas passé les tests cachés du professeur rusé :)

Personnaliser les évaluations

Un professeur consciencieux voudra tout de même regarder de près les soumissions de ses élèves ! Pour cela allez dans Formgrader puis cliquez sur le bouton Manual Grading. Cliquez sur le devoir DS1 :

Les copies sont anonymisées ! cliquez sur l’œil pour voir le nom de l'élève si vous voulez savoir !

ExempleCorrection de eleve1

Cliquez sur submission1 et saisissez des commentaires personnalisés. Vous pouvez également attribuer des points bonus.

ExempleCorrection de eleve 2

En cliquant sur le bouton Next-> en haut à droite, vous accédez à la copie de eleve2. On voit sur quel tests la fonction a échoué. L'élève ne mérite aucun points pour la méthode utilisée, on laisse la note à 0.

Complément

Le bouton aide ( ?) en bas à droite donne des raccourcis clavier très pratiques sur cette page.