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.

Documentation officielle de nbgrader

Pour plus d'informations sur nbgrader, je vous renvoie à la documentation en ligne. https://nbgrader.readthedocs.io/en/stable/

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

Installation sur le système

pip install SQLAlchemy

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

pip install nbgrader

On active enfin l'extension dans jupyter :

jupyter nbextension install --sys-prefix --py nbgrader --overwrite
jupyter nbextension enable --sys-prefix --py nbgrader
jupyter serverextension enable --sys-prefix --py nbgrader

Créez le dossier d'échange

mkdir -p /srv/nbgrader/exchange
chmod ugo+rw /srv/nbgrader/exchange

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

c = get_config()
c.CourseDirectory.course_id = "cours_MON_LOGIN"
c.Exchange.root = '/home/MON_LOGIN'
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.

Cré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 :

screenshot

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

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 ?

screenshot

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.

screenshot

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.

É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 :

from math import sqrt
def isPrime(n):
    ### BEGIN SOLUTION
    if n==2 or n==3:
        return True
    if n%2 == 0 or n<=1:
        return False
    d=3
    while n%d!=0 and d <= sqrt(n):
        d+=2
    return n%d!=0
    ### END SOLUTION

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

É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

assert isPrime(5) == True
assert isPrime(9) == False
assert isPrime(17) == True
assert isPrime(21) == False
### BEGIN HIDDEN TESTS
assert isPrime(0) == False
assert isPrime(1) == False
assert isPrime(2) == True
assert isPrime(3) == True
### END HIDDEN TESTS

Balises 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 :

screenshot

Editez 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

screenshot

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 :

screenshot

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

screenshot

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

Retourner sur la page Formgrader

screenshot

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.

screenshot

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

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

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

eleve1

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.

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

screenshot

eleve2

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.

def isPrime(n):
    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 :

submitted
|_ eleve1
   |_ testPremier
      |_ DS1.ipynb
|_ eleve2
   |_ testPremier
      |_ DS1.ipynb

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

screenshot

Cliquez sur le chiffre 2 pour voir les réponses

screenshot

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 !

nbgrader autograde "testPremier"

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

screenshot

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 :

screenshot

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

Correction de eleve1

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

screenshot

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

screenshot

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

Générer les feedback

Une fois les évaluations annotées par le professeur, celui-ci peut générer et envoyer ses annotations aux élèves au format html. Cela se fait automatiquement au moyen des boutons ci-dessous :

screenshot

Côté élève, depuis l'onglet Assignments, lorsque des annotations sont disponibles de la part du professeur, un bouton Fetch Feedback apparaît, permettant de les récupérer. L'élève peut ainsi voir les annotations laissées par le professeur. Voici à quoi ressemble une vue élève avec des feedbacks disponibles :

screenshot

⚠️ Attention ! ⚠️

Les feedbacks ne sont correctement gérés que depuis la version 6.1 de nbgrader. Vérifiez votre numéro de version en cas de doutes grâce à la commande `nbgrader --version` à taper dans une fenêtre de terminal ouverte sur Jupyter.