jupyter / nbgrader

A system for assigning and grading notebooks
https://nbgrader.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.3k stars 317 forks source link

Assignment creation with formgrader extension silently fails for invalid timezone offset #1834

Open jeflem opened 1 year ago

jeflem commented 1 year ago

Using the formgrader extension for JupyterLab on a JupyterHub the GUI for assignment creation provides the optional 'Timezone as UTC offset' field. If input is not a valid timezone offset, at first glance assignment is created (appears in list of all assignments), but creation fails silently (after closing and reopening the formgrader the new assignment is gone).

The logs show the cause:

[E 2023-09-20 13:52:03.557 ServerApp] Uncaught exception PUT /services/c-a1ae7c6ac0ea63f4/formgrader/api/assignment/testass (::ffff:10.0.2.100)
    HTTPServerRequest(protocol='http', host='192.168.178.28:8000', method='PUT', uri='/services/c-a1ae7c6ac0ea63f4/formgrader/api/assignment/testass', version='HTTP/1.1', remote_ip='::ffff:10.0.2.100')
    Traceback (most recent call last):
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/api.py", line 1575, in find_assignment
        .one()
         ^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/sqlalchemy/orm/query.py", line 2793, in one
        return self._iter().one()  # type: ignore
               ^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/sqlalchemy/engine/result.py", line 1827, in one
        return self._only_one_row(
               ^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/sqlalchemy/engine/result.py", line 760, in _only_one_row
        raise exc.NoResultFound(
    sqlalchemy.exc.NoResultFound: No row was found when one was required

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/api.py", line 1598, in update_or_create_assignment
        assignment = self.find_assignment(name)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/api.py", line 1577, in find_assignment
        raise MissingEntry("No such assignment: {}".format(name))
    nbgrader.api.MissingEntry: No such assignment: testass

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/tornado/web.py", line 1784, in _execute
        result = method(*self.path_args, **self.path_kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/tornado/web.py", line 3290, in wrapper
        return method(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/server_extensions/formgrader/base.py", line 109, in wrapper
        return f(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/server_extensions/formgrader/base.py", line 118, in wrapper
        return f(self, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/server_extensions/formgrader/apihandlers.py", line 145, in put
        self.gradebook.update_or_create_assignment(assignment_id, **assignment)
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/api.py", line 1600, in update_or_create_assignment
        assignment = self.add_assignment(name, **kwargs)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/api.py", line 1545, in add_assignment
        kwargs['duedate'] = utils.parse_utc(kwargs['duedate'])
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/nbgrader/utils.py", line 205, in parse_utc
        ts = dateutil.parser.parse(ts)
             ^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/dateutil/parser/_parser.py", line 1368, in parse
        return DEFAULTPARSER.parse(timestr, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/envs/jhub/lib/python3.11/site-packages/dateutil/parser/_parser.py", line 643, in parse
        raise ParserError("Unknown string format: %s", timestr)
    dateutil.parser._parser.ParserError: Unknown string format: 2023-09-29T23:59 some_text
[E 2023-09-20 13:52:03.561 ServerApp] {
      "X-Forwarded-Host": "192.168.178.28:8000",
      "X-Forwarded-Proto": "http",
      "X-Forwarded-Port": "8000",
      "X-Forwarded-For": "::ffff:10.0.2.100",
      "Cookie": "service-c-a1ae7c6ac0ea63f4=[secret]; _xsrf=[secret]; MoodleSession=[secret]; service-kore-oauth-state=[secret]; kore-sessionid=[secret]; MOODLEID1_=[secret]; jupyterhub-session-id=[secret]",
      "Connection": "close",
      "Dnt": "1",
      "Origin": "http://192.168.178.28:8000",
      "Content-Length": "89",
      "X-Requested-With": "XMLHttpRequest",
      "X-Csrftoken": "2|fb3d16fc|6de75f8d7b8a8e520d8658c218cfb440|1694278905",
      "Content-Type": "application/json",
      "Referer": "http://192.168.178.28:8000/services/c-a1ae7c6ac0ea63f4/formgrader",
      "Accept-Encoding": "gzip, deflate",
      "Accept-Language": "en-US,en;q=0.5",
      "Accept": "application/json, text/javascript, */*; q=0.01",
      "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0",
      "Host": "192.168.178.28:8000"
    }
[E 2023-09-20 13:52:03.561 ServerApp] 500 PUT /services/c-a1ae7c6ac0ea63f4/formgrader/api/assignment/testass (u3@::ffff:10.0.2.100) 6.30ms

Input from the timezone field is passed to dateutil package without any error checking. We may pass arbitrary strings, which seems to be a purely academic problem. But even an offset of 2 fails without message to the user, because dateutil expects +2.

Versions

Debian 12 (bookworm) nbgrader version: latest from main branch JupyterHub 4.0.2 JupyterLab 4.0.4

Expected behavior

Error message in formgrader GUI if input to timezone offset field is not a valid timezone offset. Either let the user correct his or her input or do not create the assignment.

Actual behavior

No error message (without looking into the server logs). Assignment seems to be created successfully, but then disappears.

Steps to reproduce the behavior

  1. Open formgrader in JupyterLab via 'Nbgrader' > 'Formgrader'.
  2. Click 'Add new assignment...'.
  3. Fill the form with a due date and timezone offset 2.
  4. Click 'Save'.
  5. Close and reopen the formgrader tab.
jeflem commented 1 year ago

Issue #821 could be related and a path to a good solution, but don't know what the 'add new notebook' dialog is.