text: Title: Formgrade does not create the `root` directory if it doesn't exist
  Body: Can someone confirm that a minimal notebook with nbgrader does _not_ create the `CourseDirectory.root` directory if it does not exist
  
  ### Operating system
  
  Docker instance: `jupyter/minimal-notebook:dc9744740e12`
  
  ### `nbgrader --version`
  
  nbgrader version 0.7.0.dev
  
  (installed with `pip install -e git+https://github.com/jupyter/nbgrader.git#egg=nbgrader `)
  
  ### `jupyterhub --version` (if used with JupyterHub)
  
  Not used
  
  ### `jupyter notebook --version` 
  
  6.1.5
  
  ### Expected behavior
  
  UI _new Assignment_ creates the required folders
  
  ### Actual behavior
  
  UI _new Assignment_ fails to find the required folder
  
  ### Steps to reproduce the behavior
  
  Build a docker image:
  
  ```
  FROM jupyter/minimal-notebook:dc9744740e12
  USER root
  RUN mkdir -p /srv/nbgrader/exchange
  USER $NB_USER
  RUN conda install --quiet --yes 'nbconvert==5.6.1' 
  USER root
  WORKDIR /srv/
  RUN pip install -e git+https://github.com/jupyter/nbgrader.git#egg=nbgrader 
  COPY nbgrader_config.py /etc/jupyter
  ENV JUPYTER_CONFIG_DIR /etc/jupyter
  
  # Requred by the notebook 6.0.0 update
  RUN fix-permissions $CONDA_DIR \
      && fix-permissions /srv/nbgrader/exchange
  
  RUN jupyter nbextension install --sys-prefix --py nbgrader \
      && jupyter nbextension enable --sys-prefix validate_assignment/main --section=tree \
      && jupyter serverextension enable --sys-prefix nbgrader.server_extensions.validate_assignment \
      && jupyter nbextension enable --sys-prefix assignment_list/main --section=tree \
      && jupyter serverextension enable --sys-prefix nbgrader.server_extensions.assignment_list \
      && jupyter nbextension enable --sys-prefix create_assignment/main \
      && jupyter nbextension enable --sys-prefix formgrader/main --section=tree \
      && jupyter serverextension enable --sys-prefix nbgrader.server_extensions.formgrader
  
  WORKDIR $HOME
  USER $NB_USER
  ```
  
  and in `nbgrader_config.py` we simply have:
  ```
  c.CourseDirectory.course_id = 'My Course'
  c.CourseDirectory.root = f"/home/jovyan/My Course"
  c.Exchange.path_includes_course = True
  ```
  
  make the docker image:
  
      docker build -t test .
  
  Run the notebook:
  
      docker run -it --rm -p 8888:8888 test
  
  .... and click on the link to open.
  
  To show the error:
  
  * Click on the  `formgrader` tab
  * Click on the `Add New Assignment...` link
  * Check the logged output:
  ```
  [E 14:38:00.989 NotebookApp] Uncaught exception PUT /formgrader/api/assignment/tree (172.17.0.1)
      HTTPServerRequest(protocol='http', host='127.0.0.1:8888', method='PUT', uri='/formgrader/api/assignment/tree', version='HTTP/1.1', remote_ip='172.17.0.1')
      Traceback (most recent call last):
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2285, in _wrap_pool_connect
          return fn()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 363, in connect
          return _ConnectionFairy._checkout(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 773, in _checkout
          fairy = _ConnectionRecord.checkout(pool)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 492, in checkout
          rec = pool._do_get()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/impl.py", line 238, in _do_get
          return self._create_connection()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 308, in _create_connection
          return _ConnectionRecord(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 437, in __init__
          self.__connect(first_connect_check=True)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 657, in __connect
          pool.logger.debug("Error on connect(): %s", e)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 69, in __exit__
          exc_value, with_traceback=exc_tb,
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
          raise exception
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 652, in __connect
          connection = pool._invoke_creator(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/strategies.py", line 114, in connect
          return dialect.connect(*cargs, **cparams)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 488, in connect
          return self.dbapi.connect(*cargs, **cparams)
      sqlite3.OperationalError: unable to open database file
      
      The above exception was the direct cause of the following exception:
      
      Traceback (most recent call last):
        File "/opt/conda/lib/python3.7/site-packages/tornado/web.py", line 1701, in _execute
          result = method(*self.path_args, **self.path_kwargs)
        File "/opt/conda/lib/python3.7/site-packages/tornado/web.py", line 3178, in wrapper
          return method(self, *args, **kwargs)
        File "/srv/src/nbgrader/nbgrader/server_extensions/formgrader/base.py", line 108, in wrapper
          return f(self, *args, **kwargs)
        File "/srv/src/nbgrader/nbgrader/server_extensions/formgrader/base.py", line 117, in wrapper
          return f(self, *args, **kwargs)
        File "/srv/src/nbgrader/nbgrader/server_extensions/formgrader/apihandlers.py", line 145, in put
          self.gradebook.update_or_create_assignment(assignment_id, **assignment)
        File "/srv/src/nbgrader/nbgrader/server_extensions/formgrader/base.py", line 38, in gradebook
          gb = Gradebook(self.db_url, self.coursedir.course_id)
        File "/srv/src/nbgrader/nbgrader/api.py", line 1351, in __init__
          db_exists = len(self.engine.table_names()) > 0
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2265, in table_names
          with self._optional_conn_ctx_manager(connection) as conn:
        File "/opt/conda/lib/python3.7/contextlib.py", line 112, in __enter__
          return next(self.gen)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2049, in _optional_conn_ctx_manager
          with self._contextual_connect() as conn:
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2251, in _contextual_connect
          self._wrap_pool_connect(self.pool.connect, None),
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2289, in _wrap_pool_connect
          e, dialect, self
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 1555, in _handle_dbapi_exception_noconnection
          sqlalchemy_exception, with_traceback=exc_info[2], from_=e
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
          raise exception
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/base.py", line 2285, in _wrap_pool_connect
          return fn()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 363, in connect
          return _ConnectionFairy._checkout(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 773, in _checkout
          fairy = _ConnectionRecord.checkout(pool)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 492, in checkout
          rec = pool._do_get()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/impl.py", line 238, in _do_get
          return self._create_connection()
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 308, in _create_connection
          return _ConnectionRecord(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 437, in __init__
          self.__connect(first_connect_check=True)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 657, in __connect
          pool.logger.debug("Error on connect(): %s", e)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 69, in __exit__
          exc_value, with_traceback=exc_tb,
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
          raise exception
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/pool/base.py", line 652, in __connect
          connection = pool._invoke_creator(self)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/strategies.py", line 114, in connect
          return dialect.connect(*cargs, **cparams)
        File "/opt/conda/lib/python3.7/site-packages/sqlalchemy/engine/default.py", line 488, in connect
          return self.dbapi.connect(*cargs, **cparams)
      sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unable to open database file
      (Background on this error at: http://sqlalche.me/e/e3q8)
  [E 14:38:00.999 NotebookApp] {
        "Host": "127.0.0.1:8888",
        "Connection": "keep-alive",
        "Content-Length": "65",
        "Accept": "application/json, text/javascript, */*; q=0.01",
        "Dnt": "1",
        "X-Csrftoken": "2|0c030e9b|3af24406faa6dc29d14c374be4b43d22|1606497265",
        "X-Requested-With": "XMLHttpRequest",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
        "Content-Type": "application/json",
        "Origin": "http://127.0.0.1:8888",
        "Sec-Fetch-Site": "same-origin",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Dest": "empty",
        "Referer": "http://127.0.0.1:8888/formgrader",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8",
        "Cookie": "_ga=GA1.1.595002085.1567063186; _xsrf=2|0c030e9b|3af24406faa6dc29d14c374be4b43d22|1606497265; username-127-0-0-1-8888=\"2|1:0|10:1607611060|23:username-127-0-0-1-8888|44:ZWQ0MzUzNzhlOTYxNDc3ZDhkMDc0MDQ0YzNjN2MwMTQ=|fb5b9eb46d042d4cf8ec089304e200f45bb2e32396b5043645252f4ac705d592\""
      }
  [E 14:38:00.999 NotebookApp] 500 PUT /formgrader/api/assignment/tree (172.17.0.1) 23.42ms referer=http://127.0.0.1:8888/formgrader
  ```
  
  My thought is that the validator for `root` in `coursedir.py` (here: https://github.com/jupyter/nbgrader/blob/master/nbgrader/coursedir.py#L224 ) should really read something like
  
  ```
      @validate('root')
      def _validate_root(self, proposal: Bunch) -> str:
          path = os.path.abspath(proposal['value'])
          if path != proposal['value']:
              self.log.warning("root '%s' is not absolute, standardizing it to '%s", proposal['value'], path)
          if not os.path.exists(path):
              os.mkdirs(path)
          else:
              if not os.path.isdir(path)
                   self.log.warning("root '%s' already exists, but is not a directory", proposal['value'])
          return path
  ```