Repository: jupyter/nbgrader

Issue:
Nbgrader.generate_config=False still generates a config file
Running:
`
nbgrader quickstart <course_id> --Nbgrader.config_file=/home/course/.jupyter/nbgrader_config --Nbgrader.generate_config=False
`
still generates a config file. Even though I say =False to generating it. And I give the path to the real config I have.

The docs are also misleading because they say generating a config is False by default:
[see docs](https://nbgrader.readthedocs.io/en/latest/command_line_tools/nbgrader-quickstart.html?highlight=quickstart)
```
--NbGrader.generate_config=<Bool>
    Default: False
    Generate default config file.
```


#### My versions
Ubuntu 16.04
jupyterhub version 0.8.1
notebook version 5.4.0
jupyter version 4.4.0
nbgrader version 0.6.0.dev (Also referred to as Formgrader)
canvasapi 0.8.2


Discussion:

Patch:
diff --git a/nbgrader/apps/__init__.py b/nbgrader/apps/__init__.py
index da8192e0..12b32d9c 100644
--- a/nbgrader/apps/__init__.py
+++ b/nbgrader/apps/__init__.py
@@ -18,6 +18,7 @@ from .dbapp import (
     DbAssignmentAddApp, DbAssignmentRemoveApp, DbAssignmentImportApp, DbAssignmentListApp)
 from .updateapp import UpdateApp
 from .zipcollectapp import ZipCollectApp
+from .generateconfigapp import GenerateConfigApp
 from .nbgraderapp import NbGraderApp
 from .api import NbGraderAPI
 
@@ -50,5 +51,6 @@ __all__ = [
     'DbAssignmentListApp',
     'UpdateApp',
     'ZipCollectApp',
+    'GenerateConfigApp'
     'NbGraderAPI'
 ]
diff --git a/nbgrader/apps/generateconfigapp.py b/nbgrader/apps/generateconfigapp.py
new file mode 100644
index 00000000..6c0ea220
--- /dev/null
+++ b/nbgrader/apps/generateconfigapp.py
@@ -0,0 +1,34 @@
+# coding: utf-8
+
+import os
+
+from traitlets import Unicode
+from traitlets.config.application import catch_config_error
+from .baseapp import NbGrader
+
+
+class GenerateConfigApp(NbGrader):
+
+    name = u'nbgrader-generate-config'
+    description = u'Generates a default nbgrader_config.py file'
+    examples = ""
+
+    filename = Unicode(
+        "nbgrader_config.py",
+        help="The name of the configuration file to generate."
+    ).tag(config=True)
+
+    @catch_config_error
+    def initialize(self, argv=None):
+        super(GenerateConfigApp, self).initialize(argv)
+
+    def start(self):
+        super(GenerateConfigApp, self).start()
+        s = self.generate_config_file()
+
+        if os.path.exists(self.filename):
+            self.fail("Config file '{}' already exists".format(self.filename))
+
+        with open(self.filename, 'w') as fh:
+            fh.write(s)
+        self.log.info("New config file saved to '{}'".format(self.filename))
diff --git a/nbgrader/apps/nbgraderapp.py b/nbgrader/apps/nbgraderapp.py
index cde01949..dbfc55ed 100755
--- a/nbgrader/apps/nbgraderapp.py
+++ b/nbgrader/apps/nbgraderapp.py
@@ -35,6 +35,7 @@ from . import (
     DbApp,
     UpdateApp,
     ZipCollectApp,
+    GenerateConfigApp
 )
 
 aliases = {}
@@ -45,10 +46,6 @@ aliases.update({
 flags = {}
 flags.update(nbgrader_flags)
 flags.update({
-    'generate-config': (
-        {'NbGraderApp' : {'generate_config': True}},
-        "Generate a config file."
-    )
 })
 
 
@@ -234,7 +231,15 @@ class NbGraderApp(NbGrader):
                 Update nbgrader cell metadata to the most recent version.
                 """
             ).strip()
-        )
+        ),
+        generate_config=(
+            GenerateConfigApp,
+            dedent(
+                """
+                Generates a default nbgrader_config.py file.
+                """
+            ).strip()
+        ),
     )
 
     @default("classes")
@@ -280,19 +285,6 @@ class NbGraderApp(NbGrader):
         super(NbGraderApp, self).initialize(argv)
 
     def start(self):
-        # if we're generating a config file, then do only that
-        if self.generate_config:
-            s = self.generate_config_file()
-            filename = "nbgrader_config.py"
-
-            if os.path.exists(filename):
-                self.fail("Config file '{}' already exists".format(filename))
-
-            with open(filename, 'w') as fh:
-                fh.write(s)
-            self.log.info("New config file saved to '{}'".format(filename))
-            raise NoStart()
-
         # check: is there a subapp given?
         if self.subapp is None:
             print("No command given (run with --help for options). List of subcommands:\n")
diff --git a/nbgrader/apps/quickstartapp.py b/nbgrader/apps/quickstartapp.py
index 05925445..77154df3 100644
--- a/nbgrader/apps/quickstartapp.py
+++ b/nbgrader/apps/quickstartapp.py
@@ -99,7 +99,7 @@ class QuickStartApp(NbGrader):
         self.log.info("Generating example config file...")
         currdir = os.getcwd()
         os.chdir(course_path)
-        subprocess.call([sys.executable, "-m", "nbgrader", "--generate-config"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        subprocess.call([sys.executable, "-m", "nbgrader", "generate_config"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
         os.chdir(currdir)
         with open(os.path.join(course_path, "nbgrader_config.py"), "r") as fh:
             config = fh.read()
diff --git a/nbgrader/docs/source/build_docs.py b/nbgrader/docs/source/build_docs.py
index 976fab0e..943f4bb8 100644
--- a/nbgrader/docs/source/build_docs.py
+++ b/nbgrader/docs/source/build_docs.py
@@ -46,6 +46,7 @@ def autogen_command_line(root):
         'UpdateApp',
         'ValidateApp',
         'ZipCollectApp',
+        'GenerateConfigApp'
     ]
 
     print('Generating command line documentation')
diff --git a/nbgrader/docs/source/command_line_tools/index.rst b/nbgrader/docs/source/command_line_tools/index.rst
index b7250061..b10ba74a 100644
--- a/nbgrader/docs/source/command_line_tools/index.rst
+++ b/nbgrader/docs/source/command_line_tools/index.rst
@@ -8,6 +8,7 @@ Basic commands
     :maxdepth: 1
 
     nbgrader
+    nbgrader-generate-config
 
 Instructor commands
 -------------------
diff --git a/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem1.ipynb b/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem1.ipynb
index c1cd1475..48e896bb 100644
--- a/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem1.ipynb
+++ b/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem1.ipynb
@@ -136,7 +136,7 @@
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
       "\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-4-c7326d393566>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;34m\"\"\"Check that squares returns the correct output for several inputs\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m16\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m36\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m64\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m81\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m16\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m36\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m64\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m81\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m121\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m<ipython-input-4-f3fef5b9ed4e>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;34m\"\"\"Check that squares returns the correct output for several inputs\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m16\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m36\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m64\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m81\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msquares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m9\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m16\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m25\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m36\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m49\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m64\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m81\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m121\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
       "\u001b[0;31mAssertionError\u001b[0m: "
      ]
     }
@@ -274,7 +274,7 @@
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
       "\u001b[0;31mAssertionError\u001b[0m                            Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-8-3982d45be654>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;34m\"\"\"Check that sum_of_squares returns the correct answer for various inputs.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m385\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m506\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m<ipython-input-8-1a00eaa7c988>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;34m\"\"\"Check that sum_of_squares returns the correct answer for various inputs.\"\"\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m385\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0msum_of_squares\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m506\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
       "\u001b[0;31mAssertionError\u001b[0m: "
      ]
     }
@@ -384,7 +384,7 @@
      "traceback": [
       "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
       "\u001b[0;31mNotImplementedError\u001b[0m                       Traceback (most recent call last)",
-      "\u001b[0;32m<ipython-input-10-a3eb14613b08>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;31m# YOUR CODE HERE\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m<ipython-input-10-15b94d1fa268>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;31m# YOUR CODE HERE\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
       "\u001b[0;31mNotImplementedError\u001b[0m: "
      ]
     }
diff --git a/nbgrader/docs/source/user_guide/creating_and_grading_assignments.ipynb b/nbgrader/docs/source/user_guide/creating_and_grading_assignments.ipynb
index 7c6a3d68..83164922 100644
--- a/nbgrader/docs/source/user_guide/creating_and_grading_assignments.ipynb
+++ b/nbgrader/docs/source/user_guide/creating_and_grading_assignments.ipynb
@@ -855,8 +855,8 @@
      "output_type": "stream",
      "text": [
       "[AutogradeApp | WARNING] No nbgrader_config.py file found (rerun with --debug to see where nbgrader is looking)\n",
-      "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/bitdiddle/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/jupyter.png\n",
       "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/bitdiddle/ps1/timestamp.txt -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/timestamp.txt\n",
+      "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/bitdiddle/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/jupyter.png\n",
       "[AutogradeApp | INFO] Creating/updating student with ID 'bitdiddle': {}\n",
       "[AutogradeApp | INFO] SubmittedAssignment<ps1 for bitdiddle> submitted at 2015-02-02 14:58:23.948203\n",
       "[AutogradeApp | INFO] Overwriting files with master versions from the source directory\n",
@@ -878,8 +878,8 @@
       "[AutogradeApp | INFO] Executing notebook with kernel: python\n",
       "[AutogradeApp | INFO] Writing 2356 bytes to /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem2.ipynb\n",
       "[AutogradeApp | INFO] Setting destination file permissions to 444\n",
-      "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/hacker/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/jupyter.png\n",
       "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/hacker/ps1/timestamp.txt -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/timestamp.txt\n",
+      "[AutogradeApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted/hacker/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/jupyter.png\n",
       "[AutogradeApp | INFO] Creating/updating student with ID 'hacker': {}\n",
       "[AutogradeApp | INFO] SubmittedAssignment<ps1 for hacker> submitted at 2015-02-01 09:28:58.749302\n",
       "[AutogradeApp | INFO] Overwriting files with master versions from the source directory\n",
@@ -1060,15 +1060,15 @@
      "output_type": "stream",
      "text": [
       "[FeedbackApp | WARNING] No nbgrader_config.py file found (rerun with --debug to see where nbgrader is looking)\n",
-      "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/jupyter.png\n",
       "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/timestamp.txt -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/timestamp.txt\n",
+      "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/jupyter.png\n",
       "[FeedbackApp | INFO] Converting notebook /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem1.ipynb\n",
       "[FeedbackApp | INFO] Writing 275507 bytes to /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem1.html\n",
       "[FeedbackApp | INFO] Converting notebook /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/bitdiddle/ps1/problem2.ipynb\n",
       "[FeedbackApp | INFO] Writing 254432 bytes to /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem2.html\n",
       "[FeedbackApp | INFO] Setting destination file permissions to 644\n",
-      "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/hacker/ps1/jupyter.png\n",
       "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/timestamp.txt -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/hacker/ps1/timestamp.txt\n",
+      "[FeedbackApp | INFO] Copying /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/jupyter.png -> /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/hacker/ps1/jupyter.png\n",
       "[FeedbackApp | INFO] Converting notebook /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/problem1.ipynb\n",
       "[FeedbackApp | INFO] Writing 271810 bytes to /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/feedback/hacker/ps1/problem1.html\n",
       "[FeedbackApp | INFO] Converting notebook /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/autograded/hacker/ps1/problem2.ipynb\n",
diff --git a/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem1.html b/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem1.html
index d3a94acb..4bcbab02 100644
--- a/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem1.html
+++ b/nbgrader/docs/source/user_guide/feedback/bitdiddle/ps1/problem1.html
@@ -12079,7 +12079,7 @@ span.nbgrader-label {
 <pre>
 <span class="ansi-red-fg">---------------------------------------------------------------------------</span>
 <span class="ansi-red-fg">AssertionError</span>                            Traceback (most recent call last)
-<span class="ansi-green-fg">&lt;ipython-input-4-c7326d393566&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
+<span class="ansi-green-fg">&lt;ipython-input-4-f3fef5b9ed4e&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
 <span class="ansi-green-intense-fg ansi-bold">      1</span> <span class="ansi-blue-fg">&#34;&#34;&#34;Check that squares returns the correct output for several inputs&#34;&#34;&#34;</span>
 <span class="ansi-green-fg">----&gt; 2</span><span class="ansi-red-fg"> </span><span class="ansi-green-fg">assert</span> squares<span class="ansi-blue-fg">(</span><span class="ansi-cyan-fg">1</span><span class="ansi-blue-fg">)</span> <span class="ansi-blue-fg">==</span> <span class="ansi-blue-fg">[</span><span class="ansi-cyan-fg">1</span><span class="ansi-blue-fg">]</span>
 <span class="ansi-green-intense-fg ansi-bold">      3</span> <span class="ansi-green-fg">assert</span> squares<span class="ansi-blue-fg">(</span><span class="ansi-cyan-fg">2</span><span class="ansi-blue-fg">)</span> <span class="ansi-blue-fg">==</span> <span class="ansi-blue-fg">[</span><span class="ansi-cyan-fg">1</span><span class="ansi-blue-fg">,</span> <span class="ansi-cyan-fg">4</span><span class="ansi-blue-fg">]</span>
@@ -12236,7 +12236,7 @@ span.nbgrader-label {
 <pre>
 <span class="ansi-red-fg">---------------------------------------------------------------------------</span>
 <span class="ansi-red-fg">AssertionError</span>                            Traceback (most recent call last)
-<span class="ansi-green-fg">&lt;ipython-input-8-3982d45be654&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
+<span class="ansi-green-fg">&lt;ipython-input-8-1a00eaa7c988&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
 <span class="ansi-green-intense-fg ansi-bold">      1</span> <span class="ansi-blue-fg">&#34;&#34;&#34;Check that sum_of_squares returns the correct answer for various inputs.&#34;&#34;&#34;</span>
 <span class="ansi-green-fg">----&gt; 2</span><span class="ansi-red-fg"> </span><span class="ansi-green-fg">assert</span> sum_of_squares<span class="ansi-blue-fg">(</span><span class="ansi-cyan-fg">1</span><span class="ansi-blue-fg">)</span> <span class="ansi-blue-fg">==</span> <span class="ansi-cyan-fg">1</span>
 <span class="ansi-green-intense-fg ansi-bold">      3</span> <span class="ansi-green-fg">assert</span> sum_of_squares<span class="ansi-blue-fg">(</span><span class="ansi-cyan-fg">2</span><span class="ansi-blue-fg">)</span> <span class="ansi-blue-fg">==</span> <span class="ansi-cyan-fg">5</span>
@@ -12354,7 +12354,7 @@ span.nbgrader-label {
 <pre>
 <span class="ansi-red-fg">---------------------------------------------------------------------------</span>
 <span class="ansi-red-fg">NotImplementedError</span>                       Traceback (most recent call last)
-<span class="ansi-green-fg">&lt;ipython-input-10-a3eb14613b08&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
+<span class="ansi-green-fg">&lt;ipython-input-10-15b94d1fa268&gt;</span> in <span class="ansi-cyan-fg">&lt;module&gt;</span><span class="ansi-blue-fg">()</span>
 <span class="ansi-green-intense-fg ansi-bold">      1</span> <span class="ansi-red-fg"># YOUR CODE HERE</span>
 <span class="ansi-green-fg">----&gt; 2</span><span class="ansi-red-fg"> </span><span class="ansi-green-fg">raise</span> NotImplementedError<span class="ansi-blue-fg">(</span><span class="ansi-blue-fg">)</span>
 
diff --git a/nbgrader/docs/source/user_guide/managing_assignment_files.ipynb b/nbgrader/docs/source/user_guide/managing_assignment_files.ipynb
index 7dd5743f..e762013d 100644
--- a/nbgrader/docs/source/user_guide/managing_assignment_files.ipynb
+++ b/nbgrader/docs/source/user_guide/managing_assignment_files.ipynb
@@ -415,9 +415,9 @@
      "output_type": "stream",
      "text": [
       "total 40\n",
-      "-rw-r--r--  1 jhamrick  wheel  5733 Oct 29 13:17 jupyter.png\n",
-      "-rw-r--r--  1 jhamrick  wheel  8126 Oct 29 13:17 problem1.ipynb\n",
-      "-rw-r--r--  1 jhamrick  wheel  2318 Oct 29 13:17 problem2.ipynb\n"
+      "-rw-r--r--  1 jhamrick  wheel  5733 Apr 22 15:29 jupyter.png\n",
+      "-rw-r--r--  1 jhamrick  wheel  8126 Apr 22 15:29 problem1.ipynb\n",
+      "-rw-r--r--  1 jhamrick  wheel  2318 Apr 22 15:29 problem2.ipynb\n"
      ]
     }
    ],
@@ -585,8 +585,8 @@
      "output_type": "stream",
      "text": [
       "[SubmitApp | INFO] Source: /private/tmp/student_home/ps1\n",
-      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2017-10-29 13:17:40.891463 UTC\n",
-      "[SubmitApp | INFO] Submitted as: example_course ps1 2017-10-29 13:17:40.891463 UTC\n"
+      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2018-04-22 14:29:40.397476 UTC\n",
+      "[SubmitApp | INFO] Submitted as: example_course ps1 2018-04-22 14:29:40.397476 UTC\n"
      ]
     }
    ],
@@ -614,9 +614,9 @@
      "output_type": "stream",
      "text": [
       "total 8\n",
-      "drwxr-xr-x  3 jhamrick  wheel  102 Oct 29 13:17 Library\n",
-      "-rw-r--r--  1 jhamrick  wheel   91 Oct 29 13:17 nbgrader_config.py\n",
-      "drwxr-xr-x  5 jhamrick  wheel  170 Oct 29 13:17 ps1\n"
+      "drwxr-xr-x  3 jhamrick  wheel   96 Apr 22 15:29 Library\n",
+      "-rw-r--r--  1 jhamrick  wheel   91 Apr 22 15:29 nbgrader_config.py\n",
+      "drwxr-xr-x  5 jhamrick  wheel  160 Apr 22 15:29 ps1\n"
      ]
     }
    ],
@@ -644,7 +644,7 @@
      "output_type": "stream",
      "text": [
       "[ListApp | INFO] Submitted assignments:\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:40.891463 UTC\n"
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:40.397476 UTC\n"
      ]
     }
    ],
@@ -672,8 +672,8 @@
      "output_type": "stream",
      "text": [
       "[SubmitApp | INFO] Source: /private/tmp/student_home/ps1\n",
-      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2017-10-29 13:17:43.778886 UTC\n",
-      "[SubmitApp | INFO] Submitted as: example_course ps1 2017-10-29 13:17:43.778886 UTC\n"
+      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2018-04-22 14:29:43.070290 UTC\n",
+      "[SubmitApp | INFO] Submitted as: example_course ps1 2018-04-22 14:29:43.070290 UTC\n"
      ]
     }
    ],
@@ -701,8 +701,8 @@
      "output_type": "stream",
      "text": [
       "[ListApp | INFO] Submitted assignments:\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:40.891463 UTC\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:43.778886 UTC\n"
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:40.397476 UTC\n",
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:43.070290 UTC\n"
      ]
     }
    ],
@@ -737,7 +737,7 @@
      "output_type": "stream",
      "text": [
       "[SubmitApp | INFO] Source: /private/tmp/student_home/ps1\n",
-      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2017-10-29 13:17:46.694803 UTC\n",
+      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2018-04-22 14:29:46.167901 UTC\n",
       "[SubmitApp | WARNING] Possible missing notebooks and/or extra notebooks submitted for assignment ps1:\n",
       "    Expected:\n",
       "    \tproblem1.ipynb: MISSING\n",
@@ -745,7 +745,7 @@
       "    Submitted:\n",
       "    \tmyproblem1.ipynb: EXTRA\n",
       "    \tproblem2.ipynb: OK\n",
-      "[SubmitApp | INFO] Submitted as: example_course ps1 2017-10-29 13:17:46.694803 UTC\n"
+      "[SubmitApp | INFO] Submitted as: example_course ps1 2018-04-22 14:29:46.167901 UTC\n"
      ]
     }
    ],
@@ -805,7 +805,7 @@
      "output_type": "stream",
      "text": [
       "[SubmitApp | INFO] Source: /private/tmp/student_home/ps1\n",
-      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2017-10-29 13:17:48.185532 UTC\n",
+      "[SubmitApp | INFO] Destination: /tmp/exchange/example_course/inbound/jhamrick+ps1+2018-04-22 14:29:47.497419 UTC\n",
       "[SubmitApp | CRITICAL] Assignment ps1 not submitted. There are missing notebooks for the submission:\n",
       "    Expected:\n",
       "    \tproblem1.ipynb: MISSING\n",
@@ -931,9 +931,9 @@
      "output_type": "stream",
      "text": [
       "[ListApp | INFO] Submitted assignments:\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:40.891463 UTC\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:43.778886 UTC\n",
-      "[ListApp | INFO] example_course jhamrick ps1 2017-10-29 13:17:46.694803 UTC\n"
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:40.397476 UTC\n",
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:43.070290 UTC\n",
+      "[ListApp | INFO] example_course jhamrick ps1 2018-04-22 14:29:46.167901 UTC\n"
      ]
     }
    ],
@@ -987,9 +987,9 @@
      "output_type": "stream",
      "text": [
       "total 0\n",
-      "drwxr-xr-x  3 jhamrick  staff  102 May 31 19:10 bitdiddle\n",
-      "drwxr-xr-x  3 jhamrick  staff  102 May 31 19:10 hacker\n",
-      "drwxr-xr-x  3 jhamrick  staff  102 Oct 29 13:17 jhamrick\n"
+      "drwxr-xr-x  3 jhamrick  staff  96 May 31  2017 bitdiddle\n",
+      "drwxr-xr-x  3 jhamrick  staff  96 May 31  2017 hacker\n",
+      "drwxr-xr-x  3 jhamrick  staff  96 Apr 22 15:29 jhamrick\n"
      ]
     }
    ],
diff --git a/nbgrader/docs/source/user_guide/managing_assignment_files_manually.ipynb b/nbgrader/docs/source/user_guide/managing_assignment_files_manually.ipynb
index 12db2131..d3828217 100644
--- a/nbgrader/docs/source/user_guide/managing_assignment_files_manually.ipynb
+++ b/nbgrader/docs/source/user_guide/managing_assignment_files_manually.ipynb
@@ -180,8 +180,8 @@
      "output_type": "stream",
      "text": [
       "total 64\n",
-      "-rw-r--r--  1 jhamrick  staff  18465 Oct 29 13:15 notebooks.zip\n",
-      "-rw-r--r--  1 jhamrick  staff   8870 Oct 29 13:16 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n"
+      "-rw-r--r--  1 jhamrick  staff  18465 Oct 29 20:04 notebooks.zip\n",
+      "-rw-r--r--  1 jhamrick  staff   8870 Apr 22 15:28 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n"
      ]
     }
    ],
@@ -259,8 +259,8 @@
      "output_type": "stream",
      "text": [
       "total 64\n",
-      "-rw-r--r--  1 jhamrick  staff  18465 Oct 29 13:15 notebooks.zip\n",
-      "-rw-r--r--  1 jhamrick  staff   8870 Oct 29 13:16 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n"
+      "-rw-r--r--  1 jhamrick  staff  18465 Oct 29 20:04 notebooks.zip\n",
+      "-rw-r--r--  1 jhamrick  staff   8870 Apr 22 15:28 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n"
      ]
     }
    ],
@@ -355,10 +355,10 @@
       "[ZipCollectApp | INFO] Using file extractor: ExtractorPlugin\n",
       "[ZipCollectApp | INFO] Using file collector: FileNameCollectorPlugin\n",
       "[ZipCollectApp | WARNING] Directory not found. Creating: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted\n",
-      "[ZipCollectApp | INFO] Extracting from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip\n",
-      "[ZipCollectApp | INFO]   Extracting to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks\n",
       "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
       "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
+      "[ZipCollectApp | INFO] Extracting from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip\n",
+      "[ZipCollectApp | INFO]   Extracting to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks\n",
       "[ZipCollectApp | INFO] Start collecting files...\n",
       "[ZipCollectApp | INFO] Parsing file: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
       "[ZipCollectApp | WARNING] Skipped submission with no match information provided: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
@@ -410,15 +410,15 @@
      "output_type": "stream",
      "text": [
       "total 24\n",
-      "drwxr-xr-x  8 jhamrick  staff   272 Oct 29 13:17 notebooks\n",
-      "-rw-r--r--  1 jhamrick  staff  8870 Oct 29 13:17 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
+      "drwxr-xr-x  8 jhamrick  staff   256 Apr 22 15:29 notebooks\n",
+      "-rw-r--r--  1 jhamrick  staff  8870 Apr 22 15:29 ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
       "total 88\n",
-      "-rw-rw-r--  1 jhamrick  staff  5733 Oct 29 13:17 ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
-      "-rw-rw-r--  1 jhamrick  staff  7954 Oct 29 13:17 ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb\n",
-      "-rw-rw-r--  1 jhamrick  staff  2288 Oct 29 13:17 ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb\n",
-      "-rw-rw-r--  1 jhamrick  staff  5733 Oct 29 13:17 ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png\n",
-      "-rw-rw-r--  1 jhamrick  staff  9072 Oct 29 13:17 ps1_hacker_attempt_2016-01-30-16-30-10_myproblem1.ipynb\n",
-      "-rw-rw-r--  1 jhamrick  staff  2418 Oct 29 13:17 ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb\n"
+      "-rw-rw-r--  1 jhamrick  staff  5733 Apr 22 15:29 ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
+      "-rw-rw-r--  1 jhamrick  staff  7954 Apr 22 15:29 ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb\n",
+      "-rw-rw-r--  1 jhamrick  staff  2288 Apr 22 15:29 ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb\n",
+      "-rw-rw-r--  1 jhamrick  staff  5733 Apr 22 15:29 ps1_hacker_attempt_2016-01-30-16-30-10_jupyter.png\n",
+      "-rw-rw-r--  1 jhamrick  staff  9072 Apr 22 15:29 ps1_hacker_attempt_2016-01-30-16-30-10_myproblem1.ipynb\n",
+      "-rw-rw-r--  1 jhamrick  staff  2418 Apr 22 15:29 ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb\n"
      ]
     }
    ],
@@ -448,8 +448,8 @@
      "output_type": "stream",
      "text": [
       "total 0\n",
-      "drwxr-xr-x  3 jhamrick  staff  102 Oct 29 13:17 bitdiddle\n",
-      "drwxr-xr-x  3 jhamrick  staff  102 Oct 29 13:17 hacker\n"
+      "drwxr-xr-x  3 jhamrick  staff  96 Apr 22 15:29 bitdiddle\n",
+      "drwxr-xr-x  3 jhamrick  staff  96 Apr 22 15:29 hacker\n"
      ]
     }
    ],
@@ -469,9 +469,9 @@
      "output_type": "stream",
      "text": [
       "total 40\n",
-      "-rw-r--r--  1 jhamrick  staff  8870 Oct 29 13:17 problem1.ipynb\n",
-      "-rw-rw-r--  1 jhamrick  staff  2418 Oct 29 13:17 problem2.ipynb\n",
-      "-rw-r--r--  1 jhamrick  staff    19 Oct 29 13:17 timestamp.txt\n"
+      "-rw-r--r--  1 jhamrick  staff  8870 Apr 22 15:29 problem1.ipynb\n",
+      "-rw-rw-r--  1 jhamrick  staff  2418 Apr 22 15:29 problem2.ipynb\n",
+      "-rw-r--r--  1 jhamrick  staff    19 Apr 22 15:29 timestamp.txt\n"
      ]
     }
    ],
@@ -567,10 +567,10 @@
       "[ZipCollectApp | INFO] Using file extractor: ExtractorPlugin\n",
       "[ZipCollectApp | INFO] Using file collector: CustomPlugin\n",
       "[ZipCollectApp | WARNING] Clearing existing files in /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted\n",
-      "[ZipCollectApp | INFO] Extracting from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip\n",
-      "[ZipCollectApp | INFO]   Extracting to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks\n",
       "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
       "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
+      "[ZipCollectApp | INFO] Extracting from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/archive/notebooks.zip\n",
+      "[ZipCollectApp | INFO]   Extracting to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks\n",
       "[ZipCollectApp | INFO] Start collecting files...\n",
       "[ZipCollectApp | INFO] Parsing file: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
       "[ZipCollectApp | WARNING] Skipped submission with no match information provided: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_jupyter.png\n",
@@ -584,18 +584,18 @@
       "[ZipCollectApp | INFO] Parsing file: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
       "[ZipCollectApp | WARNING] 4 files collected, 3 files skipped\n",
       "[ZipCollectApp | INFO] Start transfering files...\n",
-      "[ZipCollectApp | WARNING] Clearing existing files in /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1\n",
-      "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb\n",
-      "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem1.ipynb\n",
-      "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb\n",
-      "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem2.ipynb\n",
-      "[ZipCollectApp | INFO] Creating timestamp: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/timestamp.txt\n",
       "[ZipCollectApp | WARNING] Clearing existing files in /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1\n",
       "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_hacker_attempt_2016-01-30-16-30-10_problem2.ipynb\n",
       "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem2.ipynb\n",
       "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/ps1_hacker_attempt_2016-01-30-20-30-10_problem1.ipynb\n",
       "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/problem1.ipynb\n",
-      "[ZipCollectApp | INFO] Creating timestamp: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/timestamp.txt\n"
+      "[ZipCollectApp | INFO] Creating timestamp: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/hacker/ps1/timestamp.txt\n",
+      "[ZipCollectApp | WARNING] Clearing existing files in /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1\n",
+      "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem1.ipynb\n",
+      "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem1.ipynb\n",
+      "[ZipCollectApp | INFO] Copying from: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/downloaded/ps1/extracted/notebooks/ps1_bitdiddle_attempt_2016-01-30-15-30-10_problem2.ipynb\n",
+      "[ZipCollectApp | INFO]   Copying to: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/problem2.ipynb\n",
+      "[ZipCollectApp | INFO] Creating timestamp: /Users/jhamrick/project/tools/nbgrader/nbgrader/docs/source/user_guide/submitted_zip/bitdiddle/ps1/timestamp.txt\n"
      ]
     }
    ],
diff --git a/nbgrader/docs/source/user_guide/managing_the_database.ipynb b/nbgrader/docs/source/user_guide/managing_the_database.ipynb
index e3f216fb..94b48f3f 100644
--- a/nbgrader/docs/source/user_guide/managing_the_database.ipynb
+++ b/nbgrader/docs/source/user_guide/managing_the_database.ipynb
@@ -246,7 +246,7 @@
      "output_type": "stream",
      "text": [
       "[DbStudentAddApp | INFO] Creating/updating student with ID 'bitdiddle': {'last_name': 'Bitdiddle', 'first_name': 'Ben', 'email': None}\n",
-      "[DbStudentAddApp | INFO] Creating/updating student with ID 'hacker': {'last_name': 'Hacker', 'first_name': 'Alyssa', 'email': None}\n"
+      "[DbStudentAddApp | INFO] Creating/updating student with ID 'hacker': {'first_name': 'Alyssa', 'last_name': 'Hacker', 'email': None}\n"
      ]
     }
    ],
@@ -322,8 +322,8 @@
      "output_type": "stream",
      "text": [
       "[DbStudentImportApp | INFO] Importing from: 'students.csv'\n",
-      "[DbStudentImportApp | INFO] Creating/updating Student with id 'bitdiddle': {'email': None, 'first_name': 'Ben', 'last_name': 'Bitdiddle'}\n",
-      "[DbStudentImportApp | INFO] Creating/updating Student with id 'hacker': {'email': None, 'first_name': 'Alyssa', 'last_name': 'Hacker'}\n"
+      "[DbStudentImportApp | INFO] Creating/updating Student with id 'bitdiddle': {'first_name': 'Ben', 'email': None, 'last_name': 'Bitdiddle'}\n",
+      "[DbStudentImportApp | INFO] Creating/updating Student with id 'hacker': {'first_name': 'Alyssa', 'email': None, 'last_name': 'Hacker'}\n"
      ]
     }
    ],


Test patch:
diff --git a/nbgrader/tests/apps/test_nbgrader.py b/nbgrader/tests/apps/test_nbgrader.py
index 6d23fe5f..0666c521 100644
--- a/nbgrader/tests/apps/test_nbgrader.py
+++ b/nbgrader/tests/apps/test_nbgrader.py
@@ -15,19 +15,6 @@ class TestNbGrader(BaseTestApp):
         """Is the help displayed when no subapp is given?"""
         run_nbgrader([], retcode=0)
 
-    def test_generate_config(self):
-        """Is the config file properly generated?"""
-
-        # it already exists, because we create it in conftest.py
-        os.remove("nbgrader_config.py")
-
-        # try recreating it
-        run_nbgrader(["--generate-config"])
-        assert os.path.isfile("nbgrader_config.py")
-
-        # does it fail if it already exists?
-        run_nbgrader(["--generate-config"], retcode=1)
-
     def test_check_version(self, capfd):
         """Is the version the same regardless of how we run nbgrader?"""
         out1 = '\n'.join(
diff --git a/nbgrader/tests/apps/test_nbgrader_generate_config.py b/nbgrader/tests/apps/test_nbgrader_generate_config.py
new file mode 100644
index 00000000..7b95b401
--- /dev/null
+++ b/nbgrader/tests/apps/test_nbgrader_generate_config.py
@@ -0,0 +1,24 @@
+import os
+
+from .. import run_nbgrader
+from .base import BaseTestApp
+
+
+class TestNbGraderGenerateConfig(BaseTestApp):
+
+    def test_help(self):
+        """Does the help display without error?"""
+        run_nbgrader(["generate_config", "--help-all"])
+
+    def test_generate_config(self):
+        """Is the config file properly generated?"""
+
+        # it already exists, because we create it in conftest.py
+        os.remove("nbgrader_config.py")
+
+        # try recreating it
+        run_nbgrader(["generate_config"])
+        assert os.path.isfile("nbgrader_config.py")
+
+        # does it fail if it already exists?
+        run_nbgrader(["generate_config"], retcode=1)