Exchanging assignment files manually¶
After an assignment has been created using
nbgrader generate_assignment
, the instructor must actually release
that assignment to students. This page describes how to do that using
your institution’s existing learning management system, assuming that
the students will fetch the assignments from - and submit their
assignments to - the learning management system.
If this is not the case and you are using nbgrader in a shared server
environment (e.g. JupyterHub), you can do this with an exchange
implementation (see :doc:managing_assignment_files
).
Distributing assignments to students and collecting them can be a
logistical nightmare. The previous page discussed the built-in exchange
directory, but that is not the only option (and in fact, was added later
on). One can also distribute and collect files by other means, such as
though your institution’s learning management system. If you are relying
on your institution’s learning management system to get the submitted
versions of notebooks back from students, nbgrader
has some built-in
functionality to make theat easier (putting the files in the right place
into the course directory via an importer).
One can also do this fully manually, by sending files around. This may be useful during the testing phase.
Releasing assignments¶
In short, to release an assignment, send the files at
release/{assignment_id}/*
to your students. For example, you might
post the files on your course page.
Submitting assignments¶
When an assignment is submitted, it needs to be placed in
submitted/{student_id}/{assignment_id}/*
. The rest of this page
describes the built-in ways to do this, if students upload them to a
learning management system and you can download them all at once in an
archive. This is called collecting the assignment.
Collecting assignments¶
See also
- nbgrader zip collect
Command line options for
nbgrader zip_collect
- ZipCollect plugins
Plugins for
nbgrader zip_collect
- The philosophy and the approach
More details on how the nbgrader hierarchy is structured.
- Configuration options
Details on
nbgrader_config.py
Once the students have submitted their assignments and you have
downloaded these assignment files from your institution’s learning
management system, you can get theses files back into nbgrader
by
using the nbgrader zip_collect
sub-command.
Directory Structure:¶
nbgrader zip_collect
makes a few assumptions about how the downloaded assignment files are organized. By default, nbgrader zip_collect
assumes that your downloaded assignment files will be organized with the following directory structure:
{downloaded}/{assignment_id}/{collect_step}/
where:
The
downloaded
directory is the main directory where your downloaded assignment files are placed. This location can also be customized (see the configuration options) so that you can run the nbgrader commands from anywhere on your system, but still have them operate on the same directory.assignment_id
corresponds to the unique name of an assignment.The
collect_step
sub-directory corresponds to a step in thezip_collect
workflow.
Workflow¶
The workflow for using nbgrader zip_collect
is
You, as an instructor, place submitted files/archives in
{downloaded}/{assignment_id}/{archive_directory}
You run
nbgrader zip_collect {assignment_id}
, which will:
Extract these archive files - or copy non-archive files - to
{downloaded}/{assignment_id}/{extracted_directory}Copy these extracted files to
{submitted_directory}/{student_id}/{assignment_id}/{notebook_id}.ipynb
At this point you can use
nbgrader autograde
to grade the files in the submitted directory (See Autograde assignments).
There are a few subtleties about how nbgrader zip_collect
determines the correct student and notebook ids, which we’ll go through in the sections below.
Step 1: Download submission files or archives¶
The first step in the collect process is to extract files from any archive (zip) files - and copy any non-archive files - found in the following directory:
{downloaded}/{assignment_id}/{archive_directory}/
where:
The
archive_directory
contains the actual submission files or archives downloaded from your institution’s learning management system.nbgrader zip_collect
assumes you have already created this directory and placed all downloaded submission files or archives in this directory.
For demo purposes we have already created the directories needed by the
nbgrader zip_collect
sub-command and placed the downloaded
assignment submission files and archive (zip) files in there. For
example we have one .zip
file and one .ipynb
file:
%%bash
ls -l downloaded/ps1/archive
total ##
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] jupyter.png
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] notebooks.zip
-rw------- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
But before we can run the nbgrader zip_collect
sub-command we first
need to specify a few config options:
%%file nbgrader_config.py
c = get_config()
# Only set for demo purposes so as to not mess up the other documentation
c.CourseDirectory.submitted_directory = 'submitted_zip'
# Only collect submitted notebooks with valid names
c.ZipCollectApp.strict = True
# Apply this regular expression to the extracted file filename (absolute path)
c.FileNameCollectorPlugin.named_regexp = (
'.*_(?P<student_id>\w+)_attempt_'
'(?P<timestamp>[0-9\-]+)_'
'(?P<file_id>.*)'
)
Overwriting nbgrader_config.py
Setting the strict
flag True
skips any submitted notebooks with
invalid names.
By default the nbgrader zip_collect
sub-command uses the
FileNameCollectorPlugin
to collect files from the
extracted_directory
. This is done by sending each filename
(absolute path) through to the FileNameCollectorPlugin
, which in
turn applies a named group regular expression (named_regexp
) to the
filename.
The FileNameCollectorPlugin
returns None
if the given file
should be skipped or it returns an object that must contain the
student_id
and file_id
data, and can optionally contain the
timestamp
, first_name
, last_name
, and email
data.
Thus if using the default FileNameCollectorPlugin
you must at least
supply the student_id
and file_id
named groups. This plugin
assumes all extracted files have the same filename or path structure
similar to the downloaded notebook:
%%bash
ls -l downloaded/ps1/archive
total ##
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] jupyter.png
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] notebooks.zip
-rw------- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
Note
When collecting files in assignment sub-folders the file_id
data must include the relative path to
{assignment_id}
and the filename in order to preserve the assignment directory structure.
If you wish to use a custom plugin for the ZipCollectApp
see ZipCollect plugins for more information.
Before we extract the files, we also need to have run
nbgrader generate_assignment
:
%%bash
nbgrader generate_assignment "ps1" --IncludeHeaderFooter.header=source/header.ipynb --force
[GenerateAssignmentApp | WARNING] Removing existing assignment: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/release/ps1
[GenerateAssignmentApp | INFO] Copying [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/source/./ps1/jupyter.png -> [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/release/./ps1/jupyter.png
[GenerateAssignmentApp | INFO] Updating/creating assignment 'ps1': {}
[GenerateAssignmentApp | INFO] Converting notebook [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/source/./ps1/problem1.ipynb
[GenerateAssignmentApp | INFO] Writing [size] bytes to [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/release/ps1/problem1.ipynb
[GenerateAssignmentApp | INFO] Converting notebook [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/source/./ps1/problem2.ipynb
[GenerateAssignmentApp | INFO] Writing [size] bytes to [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/release/ps1/problem2.ipynb
[GenerateAssignmentApp | INFO] Setting destination file permissions to 644
Step 2: Extract, collect, and copy files¶
With the nbgrader_config.py
file created we can now run the nbgrader zip_collect
sub-command. This will:
Extract archive - or copy non-archive - files from the
{archive_directory}
into the following directory:{downloaded}/{assignment_id}/{extracted_directory}/
Then collect and copy files from the
extracted_directory
above to the studentssubmitted_directory
:{submitted_directory}/{student_id}/{assignment_id}/{notebook_id}.ipynb
For example:
%%bash
nbgrader zip_collect ps1 --force
[ZipCollectApp | INFO] Using file extractor: ExtractorPlugin
[ZipCollectApp | INFO] Using file collector: FileNameCollectorPlugin
[ZipCollectApp | WARNING] Directory not found. Creating: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted
[ZipCollectApp | INFO] Extracting from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip
[ZipCollectApp | INFO] Extracting to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/jupyter.png
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | INFO] Start collecting files...
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_myproblem1.ipynb
[ZipCollectApp | WARNING] Skipped notebook with invalid name 'myproblem1.ipynb'
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | WARNING] 4 files collected, 4 files skipped
[ZipCollectApp | INFO] Start transfering files...
[ZipCollectApp | WARNING] Directory not found. Creating: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem1.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem2.ipynb
[ZipCollectApp | INFO] Creating timestamp: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/timestamp.txt
[ZipCollectApp | WARNING] Directory not found. Creating: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem2.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem1.ipynb
[ZipCollectApp | INFO] Creating timestamp: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/timestamp.txt
After running the nbgrader zip_collect
sub-command, the archive
(zip) files were extracted - and the non-archive files were copied - to
the extracted_directory
:
%%bash
ls -l downloaded/ps1/extracted/
ls -l downloaded/ps1/extracted/notebooks/
total ##
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] jupyter.png
drwxrwxr-x 1 nb_user nb_group [size] [date] [time] notebooks
-rw------- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
total ##
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-16-30-10_myproblem1.ipynb
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb
By default archive files will be extracted into their own sub-directory in the extracted_directory
and any archive files, within archive files, will also be extracted into their own sub-directory along the path. To change this default behavior you can write your own extractor plugin for zip_collect
(see ZipCollect plugins).
These extracted files were then collected and copied into the students submitted_directory
:
%%bash
ls -l submitted_zip
total ##
drwxrwxr-x 1 nb_user nb_group [size] [date] [time] bitdiddle
drwxrwxr-x 1 nb_user nb_group [size] [date] [time] hacker
%%bash
ls -l submitted_zip/hacker/ps1/
total ##
-rw------- 1 nb_user nb_group [size] [date] [time] problem1.ipynb
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] problem2.ipynb
-rw-rw-r-- 1 nb_user nb_group [size] [date] [time] timestamp.txt
Custom plugins¶
See also
- ZipCollect plugins
Plugins for
nbgrader zip_collect
Unfortunately, for the demo above, the timestamp strings from the filenames did not parse correctly:
%%bash
cat submitted_zip/hacker/ps1/timestamp.txt
2016-01-31 06:00:00
This is an issue with the underlying dateutils
package used by
nbgrader
. But not to worry, we can easily create a custom collector
plugin to correct the timestamp strings when the files are collected,
for example:
%%file plugin.py
from nbgrader.plugins import FileNameCollectorPlugin
class CustomPlugin(FileNameCollectorPlugin):
def collect(self, submission_file):
info = super(CustomPlugin, self).collect(submission_file)
if info is not None:
info['timestamp'] = '{}-{}-{} {}:{}:{}'.format(
*tuple(info['timestamp'].split('-'))
)
return info
Writing plugin.py
%%bash
# Use force flag to overwrite existing files
nbgrader zip_collect --force --collector=plugin.CustomPlugin ps1
[ZipCollectApp | INFO] Using file extractor: ExtractorPlugin
[ZipCollectApp | INFO] Using file collector: CustomPlugin
[ZipCollectApp | WARNING] Clearing existing files in [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted
[ZipCollectApp | INFO] Extracting from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip
[ZipCollectApp | INFO] Extracting to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/archive/jupyter.png
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | INFO] Start collecting files...
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png
[ZipCollectApp | WARNING] Skipped submission with no match information provided: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_myproblem1.ipynb
[ZipCollectApp | WARNING] Skipped notebook with invalid name 'myproblem1.ipynb'
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb
[ZipCollectApp | INFO] Parsing file: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | WARNING] 4 files collected, 4 files skipped
[ZipCollectApp | INFO] Start transfering files...
[ZipCollectApp | WARNING] Clearing existing files in [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem1.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem2.ipynb
[ZipCollectApp | INFO] Creating timestamp: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/timestamp.txt
[ZipCollectApp | WARNING] Clearing existing files in [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem2.ipynb
[ZipCollectApp | INFO] Copying from: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb
[ZipCollectApp | INFO] Copying to: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem1.ipynb
[ZipCollectApp | INFO] Creating timestamp: [NB_GRADER_ROOT]/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/timestamp.txt
The --force
flag is used this time to overwrite existing extracted
and submitted files. Now if we check the timestamp we see it parsed
correctly:
%%bash
cat submitted_zip/hacker/ps1/timestamp.txt
2016-01-30 20:30:10
Note that there should only ever be one instructor who runs the
nbgrader zip_collect
command (and there should probably only be one
instructor – the same instructor – who runs
nbgrader generate_assignment
, nbgrader autograde
and
nbgrader formgrade
as well). However this does not mean that only
one instructor can do the grading, it just means that only one
instructor manages the assignment files. Other instructors can still
perform grading by accessing the formgrader URL.