Repository: jupyter/nbgrader

Issue:
Error generating assignment and managing students
Firstly, I apologize if this is not the proper place to report issues like this.  I am exploring nbgrader, and have had some trouble getting it to work.

I have installed nbgrader on a local Jupyterhub installation and have been working through the example notebooks.

I had to create a `~/.jupyter/nbgrader_config.py` file that has the following contents:
```
c = get_config()
c.CourseDirectory.root = '~/Jupyter/ChEn6703_test_nbgrader'
```
(note that I used the full path above, but replaced the prefix with `~` for security reasons)

There are a few strange things going on though:

## Problems with student entries
1. When I go to the `Manage Students` section of nbgrader, it doesn't show any students.
1. When I do `nbgrader db student list --log-level='DEBUG'` I get something which is inconsistent with the empty list in the `Manage Students` dialog.
```
[DbStudentListApp | DEBUG] Searching ['~/Jupyter/ChEn6703_test_nbgrader', '~/.jupyter', '/usr/etc/jupyter', '/usr/local/etc/jupyter', '/etc/jupyter'] for config files
[DbStudentListApp | DEBUG] Looking for jupyter_config in /etc/jupyter
[DbStudentListApp | DEBUG] Looking for jupyter_config in /usr/local/etc/jupyter
[DbStudentListApp | DEBUG] Looking for jupyter_config in /usr/etc/jupyter
[DbStudentListApp | DEBUG] Looking for jupyter_config in ~/.jupyter
[DbStudentListApp | DEBUG] Looking for jupyter_config in ~/Jupyter/ChEn6703_test_nbgrader
[DbStudentListApp | DEBUG] Looking for nbgrader_config in /etc/jupyter
[DbStudentListApp | DEBUG] Looking for nbgrader_config in /usr/local/etc/jupyter
[DbStudentListApp | DEBUG] Looking for nbgrader_config in /usr/etc/jupyter
[DbStudentListApp | DEBUG] Looking for nbgrader_config in ~/.jupyter
[DbStudentListApp | DEBUG] Loaded config file: ~/.jupyter/nbgrader_config.py
[DbStudentListApp | DEBUG] Looking for nbgrader_config in ~/Jupyter/ChEn6703_test_nbgrader
[DbStudentListApp | DEBUG] Loaded config file: ~/Jupyter/ChEn6703_test_nbgrader/nbgrader_conf
ig.py
[DbStudentListApp | DEBUG] Looking for nbgrader_config in ~/Jupyter/ChEn6703_test_nbgrader
[DbStudentListApp | DEBUG] Loaded config file: ~/Jupyter/ChEn6703_test_nbgrader/nbgrader_conf
ig.py
There are 1 students in the database:
1 (Flinstone, Fred) -- None
```
3. If I manually enter a student in the `Manage Students` dialog, the student shows up and then disappears.

## Problems when generating the assignment
When I go to `Manage Assignments` and click on the `Generate` icon for the example `ps1` assignment, I get:
```
[INFO] Copying ~/Jupyter/ChEn6703_test_nbgrader/source/./ps1/jupyter.png -> ~/Jupyter/ChEn6703_test_nbgrader/release/./ps1/jupyter.png
[INFO] Updating/creating assignment 'ps1': {}
[INFO] Converting notebook ~/Jupyter/ChEn6703_test_nbgrader/source/./ps1/problem1.ipynb
[ERROR] There was an error processing assignment: ~/Jupyter/ChEn6703_test_nbgrader/source/./ps1
[ERROR] Traceback (most recent call last):
      File "/usr/local/lib/python3.5/dist-packages/nbgrader/converters/base.py", line 288, in convert_notebooks
        self.convert_single_notebook(notebook_filename)
      File "/usr/local/lib/python3.5/dist-packages/nbgrader/converters/base.py", line 244, in convert_single_notebook
        output, resources = self.exporter.from_filename(notebook_filename, resources=resources)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/exporters/exporter.py", line 172, in from_filename
        return self.from_file(f, resources=resources, **kw)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/exporters/exporter.py", line 190, in from_file
        return self.from_notebook_node(nbformat.read(file_stream, as_version=4), resources=resources, **kw)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/exporters/notebook.py", line 31, in from_notebook_node
        nb_copy, resources = super(NotebookExporter, self).from_notebook_node(nb, resources, **kw)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/exporters/exporter.py", line 132, in from_notebook_node
        nb_copy, resources = self._preprocess(nb_copy, resources)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/exporters/exporter.py", line 309, in _preprocess
        nbc, resc = preprocessor(nbc, resc)
      File "/usr/local/lib/python3.5/dist-packages/nbconvert/preprocessors/base.py", line 47, in __call__
        return self.preprocess(nb,resources)
      File "/usr/local/lib/python3.5/dist-packages/nbgrader/preprocessors/headerfooter.py", line 23, in preprocess
        with open(self.header, 'r') as fh:
    FileNotFoundError: [Errno 2] No such file or directory: 'source/header.ipynb'
    
[WARNING] Removing failed assignment: ~/Jupyter/ChEn6703_test_nbgrader/release/ps1
[ERROR] There was an error processing assignment 'ps1' for student '.'
[ERROR] Please see the the above traceback for details on the specific errors on the above failures.
Traceback
```
I have tried regenerating the entire class and this issue persists.

If I try the command line approach: `nbgrader assign source/ps1/` I get:
```
[AssignApp | ERROR] No notebooks were matched by ~/Jupyter/ChEn6703_test_nbgrader/source/./s
```
Note the `source/./s` which seems problematic.

Any ideas on this?

Discussion:

Patch:
diff --git a/nbgrader/converters/base.py b/nbgrader/converters/base.py
index c4af98b2..5fabe801 100644
--- a/nbgrader/converters/base.py
+++ b/nbgrader/converters/base.py
@@ -66,7 +66,12 @@ class BaseConverter(LoggingConfigurable):
         self.exporter = self.exporter_class(parent=self, config=self.config)
         for pp in self.preprocessors:
             self.exporter.register_preprocessor(pp)
-        self.convert_notebooks()
+        currdir = os.getcwd()
+        os.chdir(self.coursedir.root)
+        try:
+            self.convert_notebooks()
+        finally:
+            os.chdir(currdir)
 
     @default("classes")
     def _classes_default(self):
diff --git a/nbgrader/preprocessors/headerfooter.py b/nbgrader/preprocessors/headerfooter.py
index 56c5dc27..83c6aaa6 100644
--- a/nbgrader/preprocessors/headerfooter.py
+++ b/nbgrader/preprocessors/headerfooter.py
@@ -8,8 +8,8 @@ from ..nbgraderformat import read as read_nb
 class IncludeHeaderFooter(NbGraderPreprocessor):
     """A preprocessor for adding header and/or footer cells to a notebook."""
 
-    header = Unicode("", help="Path to header notebook").tag(config=True)
-    footer = Unicode("", help="Path to footer notebook").tag(config=True)
+    header = Unicode("", help="Path to header notebook, relative to the root of the course directory").tag(config=True)
+    footer = Unicode("", help="Path to footer notebook, relative to the root of the course directory").tag(config=True)
 
     def preprocess(self, nb, resources):
         """Concatenates the cells from the header and footer notebooks to the


Test patch:
diff --git a/nbgrader/tests/apps/test_nbgrader_assign.py b/nbgrader/tests/apps/test_nbgrader_assign.py
index 85539e3e..73575ef2 100644
--- a/nbgrader/tests/apps/test_nbgrader_assign.py
+++ b/nbgrader/tests/apps/test_nbgrader_assign.py
@@ -277,3 +277,15 @@ class TestNbGraderAssign(BaseTestApp):
         run_nbgrader(["assign", "ps1", "--no-db", "--no-metadata"])
         assert os.path.exists(join(course_dir, "release", "ps1", "p1.ipynb"))
 
+    def test_header(self, course_dir):
+        """Does the relative path to the header work?"""
+        self._empty_notebook(join(course_dir, 'source', 'ps1', 'foo.ipynb'))
+        self._empty_notebook(join(course_dir, 'source', 'header.ipynb'))
+        with open("nbgrader_config.py", "a") as fh:
+            fh.write("""c.CourseDirectory.db_assignments = [dict(name="ps1")]\n""")
+            fh.write("""c.IncludeHeaderFooter.header = "source/header.ipynb"\n""")
+        run_nbgrader(["assign", "ps1"])
+        assert os.path.isfile(join(course_dir, "release", "ps1", "foo.ipynb"))
+
+
+