This is a system for assigning and automatic grading of Jupyter notebooks. Communication between students and the grading system is implemented via email. To release assignments to students, Google Drive is used.
A short explanation of the grader work:
-
Teacher sets up the course backend: downloads the source code, adds lessons and publishes them for students.
-
Student downloads an assignment from the Google Drive folder, solves it, composes an email message with the solution files attached, and sends it to the teacher.
-
The grading system parses email messages, grades them with tests prepared by the teacher (hidden from students).
-
If the submission is correct, the feedback message with grades is sent to the student.
If something is wrong with the submission, the error message is sent to the student.
First, you should download the source code and rename .env.template
file
to .env
. How to specify environment variables in the .env
file is written
below.
Set the name of the course (for example, "Python course") to COURSE_NAME
variable.
The value of TEACHER_EMAIL
is used in feedbacks or in service messages which
are sent when the grading system fails.
The exchanger is responsible for fetching new submissions and sending feedbacks after grading. This implementation uses email communication via Gmail API.
To set it up:
- Create a project on the Google Cloud.
- Enable Gmail API, create an OAuth client account for desktop applications, and download its keys in json format. See Gmail API docs for details.
- Insert the keys as a json string in the
.env
file underGMAIL_CREDS
variable. - Set
GMAIL_FETCH_LABEL
(for example,"Python course for masters"
). Each message with this label will be considered as a submission. - Create a Gmail filter. It can be created manually via Gmail client or
automatically. In the latter case, it is necessary to
set
GMAIL_FETCH_KEYWORD
andGMAIL_FETCH_ALIAS
. All messages that are addressed toGMAIL_FETCH_ALIAS
and containGMAIL_FETCH_KEYWORD
in the subject will be marked withGMAIL_FETCH_LABEL
. - Set
GMAIL_SENDER_NAME
andGMAIL_SENDER_EMAIL
to specify the "from" part of outgoing messages.
The grading system uses a database. It can be MySQL , PostgreSQL, or any other database that is supported by SQLAlchemy.
You must set DB_DRIVER
, DB_HOST
, and DB_PORT
for database connection. It
is considered that DB_NAME
is used to store the course data,
and DB_GRADER_USER
with DB_GRADER_PASSWORD
has superuser permissions for
the database specified by the DB_NAME
variable. DB_ROOT_PASSWORD
is used
only when the database is created in a docker container.
The DB_NAME
database must be previously created. However, all the necessary
tables will be created and populated automatically.
When the grading system fails, it is necessary to notify the teacher. In this
case, a service email with exception traceback is sent to the TEACHER_EMAIL
via SMTP server. So, the parameters SERVICE_EMAIL_LOGIN
, SERVICE_EMAIL_PASSWORD
must be specified to connect to the SMTP
server SERVICE_EMAIL_SERVER
via SERVICE_EMAIL_PORT
port.
All the necessary dependencies are listed in requirements.txt
. So, you should
install Python 3.9 (or newer) on your system, create a virtual environment, and
install packages as usual:
pip install -r requirements.txt
To enable the necessary extensions in Jupyter, run this in the terminal:
jupyter nbextension install --sys-prefix --py nbgrader --overwrite
jupyter nbextension enable --sys-prefix --py nbgrader
jupyter serverextension enable --sys-prefix --py nbgrader
The grading system
uses nbgrader
tool under the hood. Thus, details of assignments creating, description of the
folder structure, and content of the nbgrader_config.py
file can be
found in its docs.
To add an assignment, you should launch Jupyter from the project root, move
to Formgrader
page and click on Add new assignment
. Use can
use add_lesson.py
script to do the same.
Open the assignment source folder, add a notebook and necessary files. Note that the name of the notebook must match the assignment name.
The notebook should contain tasks for students. Each task must consist of the following cells:
- One read-only markdown cell with the text of pattern
TODO: <Task name>
. These task names will be automatically parsed for feedbacks. The pattern of task names can be changed indefinitions.py
. - One or several cells with
### BEGIN SOLUTION
and### END SOLUTION
comments where the solution should be written. Mark these cells asAutograded answer
. See, nbgrader docs for more details. - One cell with
### BEGIN HIDDEN TESTS
and### END HIDDEN TESTS
comments for tests. Mark these cells asAutograded tests
. See, nbgrader docs for more details. Specify max score for each test cell. The sum score of the assignment should be equal to 100.
Lesson template can be found here.
After lesson content is prepared, click Generate
in the Formgrader
page of
Jupyter to create a release version of the lesson. Now, you can see how your
assignment will look to students.
To add a user, you can launch Jupyter from the project root, move
to Formgrader
, next to Manage Students
, and click on Add new student
.
Next, specify all the fields Student ID
, First name
, Last name
, Email
.
The Student ID
and Email
must be unique and non-empty.
You can also use add_user.py
script which automatically creates student ids,
validates emails, and makes other necessary checks.
When you are ready to send assignments to students, you can
run publish_lesson.py
script. This will generate student versions of the
lessons and upload them to Google Drive.
Before this, you must complete some preparations:
- Enable Google Drive API for your project created at Step 3, create a service account, and download its keys in json format. See Google Drive API docs for details.
- Insert the keys as a json string in the
.env
file underGDRIVE_CREDS
. Copy email of the service account. - Create a folder
GDRIVE_PUBLISH_FOLDER
in your Google Drive where you are going to store public version of assignments. Share this folder and grant " editor" permissions with the email from the previous step.
It's time to run publish_lesson.py
script. This will create all necessary
folders in your Google Drive folder, share the folders with links, and upload
files. However, this will be made automatically when the grading system
starting.
Do not modify cloud release folders on your own. If you want to change something, make this locally and run the release script again. Shared folders and their links will stay the same, however, their content will be changed.
To start the grading process, run main.py
from the project root.
Student should download assignments from the Google Drive release folder,
insert solutions and send files as attachments to GMAIL_FETCH_ALIAS
. It is
considered that each message with a submission has a subject of the
structure "GMAIL_FETCH_KEYWORD
/ lesson name
". In several minutes, the
corresponding feedback will be sent to the student.
Progress of students can be found in Formgrader
page of Jupyter.