Changelog

v0.5.0 (2026-05-07)

Contributors: Philipp Emmo Tobias Risius (@PhilippRisius)

Developed with assistance from Claude (Anthropic) — see commit trailers for per-commit attribution.

Note

Compatibility: a v0.5.0 release notebook embeds a redacted answer key (no correct flags) that v0.4.0’s display JS does not tolerate. Students rendering a v0.5.0-generated assignment must install nbgrader-jupyterquiz ≥ v0.5.0. Instructors can still autograde older releases with v0.5.0 — the autograder side is unaffected.

Changes

  • Parser rework — paired delimiters ((...), [...], {...}, <...>) balance their own pair, so (Correct (with caveats)) parses as one feedback field with content Correct (with caveats). Other delimiter characters inside a paired field are inert: (feedback { ) parses cleanly. Backslash escapes (\(, \), \\) handle deliberately unmatched same-pair characters, so an emoticon like :( inside feedback can be written as :\( (PR/26).

  • Same-character delimiters ("...") accept \" for a literal " and \\ for a literal \; other backslash sequences (\int, \alpha, etc.) pass through unchanged so LaTeX content authors don’t need to double their backslashes (PR/26).

  • Multi-line content is supported in any field — continuation lines must be indented strictly deeper than the opener (matching markdown-list semantics). Code blocks (`` `...` ) ignore the indentation rule and tolerate any character until the closing triple-backtick, so multi-line fenced code works without escaping.  An unclosed field raises a clearer ``ParseError naming the missing delimiter (PR/26).

  • Documentation reorganised along Diataxis: the toctree groups pages under Get started, Tutorial, How-to, Worked examples, Reference, Explanation, and Project captions. The ad-hoc usage landing page is removed; the example downloads moved to a proper Worked examples.

  • New Tutorial page walks through install → quiz authoring → release → submit → autograde end-to-end in about ten minutes.

  • New How-to section with three focused recipes: displaying a quiz without nbgrader, writing a numeric range question, and mixing graded with manually-graded content in one task cell.

  • Graded Quizzes “Where the answer key lives” section gains a Threat model subsection spelling out what the redaction protects against (DOM inspection, source reading, base64 reverse-engineering) and what it doesn’t (per-answer feedback strings as a leaky channel, stand-alone display_quiz).

  • nbgrader Pipeline “Collecting and grading” section replaced — it described the pre-v0.4.0 self-checking-only world. It now describes the actual sidecar + autograde flow.

  • New worked example nb3-physics-rich-content.ipynb (in the Worked examples page) combines every feature in one annotated notebook: MathJax in question and answer labels, numeric value and range matching with precision, a code-block question, per-question points (including fractional weights), and a self-check warm-up inside a graded task (PR/24).

Fixes

  • The instructor-declared question type (SC / MC) is now authoritative at both parse time and render time. Previously the display silently switched a SC question with two + answers to many-choice semantics (and vice-versa for MC with one +), producing responses the grader would then score as 0 because of the type mismatch. SC with anything other than exactly one correct answer is now a hard ParseError; MC with 0 or 1 correct answers is allowed but logs a warning. Both warnings and errors are now routed through CreateQuiz.log so they appear in nbgrader’s UI output with the offending cell’s grade_id (PR/21).

  • hide_correctness=true (auto-on for graded quizzes) now strips the answer key from the display JSON embedded into the release notebook. Previously, the full correct flags on multiple-choice answers and the value / range matchers on numeric answers shipped to the student’s browser; only the visual feedback was hidden. The autograder receives its copy from a separate hidden-tests block (stripped by ClearHiddenTests, restored by OverwriteCells), so this change does not affect grading. Per-answer feedback strings are intentionally preserved. Self-check quizzes (hide_correctness=false) still ship the full key, since the JS needs it to colour buttons (PR/22).

  • Deselected MC/many-choice answer buttons keep their dark text colour instead of inheriting the surrounding container’s light text — previously invisible on dark JupyterHub themes after a select-then-deselect. A new --jq-mc-button-text palette variable drives both the default and deselected states (PR/23).

  • MathJax expressions ($...$) in question and feedback strings render correctly with encoded=false quizzes. Previously MathJax rewrote the embedded JSON in place, breaking JSON.parse; the hidden span now carries tex2jax_ignore / mathjax_ignore classes so MathJax skips it. Default encoded=true quizzes were not affected (PR/23).

v0.4.0 (2026-04-17)

Contributors: Philipp Emmo Tobias Risius (@PhilippRisius)

Developed with assistance from Claude (Anthropic) — see commit trailers for per-commit attribution.

Changes

  • Added graded-quiz mode: quizzes inside nbgrader Manually Graded Task cells are now auto-graded end-to-end. Student responses are persisted to a responses.json sidecar as they answer; nbgrader autograde reads the sidecar and awards partial credit. Introduces grade_quiz(), the QuizResult / QuestionResult dataclasses, and a static HTML review rendered into the autograded cell output (visible in generate_feedback). Cross-frontend (JupyterLab 4, Notebook 7, classic Notebook); VS Code’s Jupyter extension is not supported.

  • Added the {N} question-line marker for per-question points, supporting fractional weights (e.g. {0.5}). Points render as a badge next to each question; the badge-display rule is “show on every question iff at least one question in the quiz carries an explicit {N}”.

  • Added quiz-level graded=false option to opt a single quiz out of auto-grading inside a task cell (self-check mode) while leaving the task cell’s own points intact for any manual grading of surrounding content. Added quiz-level hide_correctness override, independently toggleable from graded.

  • Added hide-correctness feedback mode (auto-enabled for graded quizzes) — MC/many-choice/numeric questions show a neutral “Selected: … / Deselected: …” state instead of green/red so students can’t guess their way to the right answer.

  • Replaced <label>``+hidden ``<input type=radio> with a plain <button type=button> for MC answers. Eliminates the label→radio click synthesis that otherwise double-fired the click handler in hide-mode toggles.

  • Added a dedicated graded-quizzes documentation page covering the workflow end-to-end.

  • Instructor config changed: register CreateQuiz with c.GenerateAssignment.preprocessors.insert(0, ...) (not .append(...)). The new auto-generated autograder cells must run before nbgrader’s SaveCells and ClearHiddenTests.

  • Added string question schema (validate.Schema.STR). The previous SCHEMATA dispatch was missing the string type — constructing a string-type question dict raised KeyError before schema validation.

  • Removed the capture_responses public API. It was a browser- DOM-only shim that never integrated with nbgrader autograde and whose interface was incompatible with caller code (prev_div_id was interpolated as a JS expression, not a string). The graded- quiz workflow is the supported replacement.

  • Published the two end-to-end validation notebooks (nb1-geography.ipynb and nb2-python.ipynb) as documentation downloads — they exercise every graded/self-check mode and serve as copyable starting templates for instructors.

Fixes

  • nbgrader-pipeline.rst no longer refers to the deprecated nbgrader assign command — it’s generate_assignment in nbgrader ≥ 0.9.

  • Numeric range display bug: + [0, 10] now includes the upper bound (10 was previously rejected due to a strict < comparison) and the match is no longer gated on answer.feedback being set. Reported during v0.3.0 end-to-end validation.

v0.3.0 (2026-04-14)

Contributors: Philipp Emmo Tobias Risius (@PhilippRisius)

Developed with assistance from Claude (Anthropic) — see commit trailers for per-commit attribution.

Changes

  • Implemented quiz-level option parsing (parse_quiz_options) — the #### Quiz header now accepts encoded, inline, hidden, and filename options as space-separated key=value pairs (PR/12).

  • Added a preprocessor test suite covering the happy path, cell transformation, multi-quiz notebooks, enforce_metadata, ParseError handling, all four option modes, and filesystem edge cases — 21 tests, 100 % line coverage of grader/preprocessor.py (PR/12).

  • Added a display-module test suite covering colour-palette key symmetry, display_quiz parameter guards, build_styles CSS injection, and load_questions_script loader paths — 9 tests (PR/12).

  • Added a docs CI job (tox -e docs with -W --keep-going) so Sphinx build failures are caught on every PR, not only after ReadTheDocs runs (PR/13).

  • Refactored the display colour palettes _DEFAULT_COLORS and _FDSP_COLORS to module-level constants (no behaviour change; enables palette-symmetry tests) (PR/12).

  • Added the initial public documentation set: quiz-syntax reference, nbgrader-pipeline integration guide, and display-options reference; plus a Diataxis-guided correction pass against the actual code behaviour (PR/11).

  • Documented quiz-level options with a reference table in docs/quiz-syntax.rst (PR/12).

Fixes

  • Registered an autodoc-skip-member handler in docs/conf.py to prevent duplicate-object-description warnings for symbols re-exported via __init__.__all__; the ReadTheDocs build now passes under fail_on_warning: true (PR/13).

  • Resolved all outstanding Sphinx build warnings in the initial docs set (PR/11).

  • Dropped Exception.add_note() from grader/preprocessor.py — the call required Python 3.11+ (PEP 678) but the project targets 3.10+ (PR/12).

  • Widened the sphinx version constraint from <8.2 to >=8.1.3 so the tox -e docs environment installs the same major version as ReadTheDocs (PR/13).

  • Bumped pip to 26.0 to address GHSA-4xh5-x5gv-qwph and GHSA-6vgw-5pg2-w6jp (PR/9).

v0.2.0 (2026-04-13)

Contributors: Philipp Emmo Tobias Risius (@PhilippRisius)

Changes

  • Merged fork of jupyterquiz (v2.9.6.4) as nbgrader_jupyterquiz.display subpackage, removing the external dependency.

  • Ported nbgrader preprocessor and quiz parsing from the legacy plugin into nbgrader_jupyterquiz.grader.

  • Added unit tests for quiz parsing, schema validation, and encoding.