Instructor Quickstart¶
A step-by-step guide to setting up and grading your first assignment with mograder.
1. Install mograder¶
2. Set up the course directory¶
Create the standard directory structure:
my-course/
mograder.toml # configuration (optional)
source/
hw1/
hw1.py # source notebook (with solutions)
release/ # generated (solutions stripped)
submitted/ # student submissions
autograded/ # autograde output
feedback/ # HTML feedback
A minimal mograder.toml:
See Configuration for all options.
3. Write a source notebook¶
Create a notebook with marimo edit source/hw1/hw1.py. Source notebooks are standard marimo notebooks with three conventions:
PEP 723 dependencies¶
Add a metadata block at the top so dependencies install automatically:
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "marimo",
# "numpy",
# "mograder",
# ]
# ///
Solution markers¶
Wrap model solutions in ### BEGIN SOLUTION / ### END SOLUTION. When you run mograder generate, these are replaced with # YOUR CODE HERE and pass:
@app.cell
def _(np):
x = None
y = None
### BEGIN SOLUTION
x = np.linspace(0, 2 * np.pi, 50)
y = np.sin(x)
### END SOLUTION
return x, y
For written-response cells, assign to response_text inside a solution block. The release version is automatically converted to an editable mo.md() block:
@app.cell
def _(mo):
response_text = "*Write your analysis here...*"
### BEGIN SOLUTION
response_text = r"""
The finite difference method approximates derivatives using nearby
function values. Central differences achieve second-order accuracy...
"""
### END SOLUTION
mo.md(response_text)
return (response_text,)
Autograding checks¶
Import check from mograder.runtime and call it with a label and a list of (condition, failure_message) tuples:
from mograder.runtime import check
check(
"Q1: Array creation",
[
(x.shape == (50,), f"x should have shape (50,), got {x.shape}"),
(abs(x[0]) < 1e-10, "x should start at 0"),
],
)
Use mo.stop() with an empty-checks call for a "waiting" state before the student has written code:
@app.cell(hide_code=True)
def _(check, mo, x):
mo.stop(x is None, check("Q1: Array creation", []))
check("Q1: Array creation", [
(x.shape == (50,), f"x should have shape (50,), got {x.shape}"),
])
return
Hidden tests¶
You can add checks that students cannot see but which run during autograde. Wrap them in ### BEGIN HIDDEN TESTS / ### END HIDDEN TESTS:
check("Q1: Array creation", [
(x.shape == (50,), f"Expected shape (50,), got {x.shape}"),
])
### BEGIN HIDDEN TESTS
check("Q1: Edge cases", [
(abs(x[0]) < 1e-10, "x should start at 0"),
])
### END HIDDEN TESTS
Hidden tests are stripped from the release notebook and reinjected from the source during mograder autograde. See Source Notebooks: Hidden tests for details.
Choosing a grading mode¶
Holistic mode (single 0-100 mark assigned by a marker):
Per-question marks (auto + manual):
from mograder.runtime import Grader
# === MOGRADER: MARKS ===
_marks = {"Q1": 10, "Q2": 15, "Analysis": 60}
grader = Grader(_marks)
check = grader.check
Questions matching a check() label are auto-scored with partial credit: marks are proportional to the weight of passing checks. Each check tuple can optionally include a weight as a third element (default 1):
check("Q2: Finite differences", [
(isinstance(dydx, np.ndarray), "result should be ndarray"), # weight 1
(dydx.shape == x.shape, "shape should match"), # weight 1
(np.max(np.abs(dydx - np.cos(x))) < 0.05, "max error < 0.05", 3), # weight 3
])
# If only the first two pass: earned = round(15 * 2/5, 1) = 6.0/15
The question key is the text before the first colon in the label, so check("Q1: Array creation", [...]) maps to "Q1". Questions without a matching check (e.g. "Analysis") are scored manually by the marker.
Call grader.scores() in a cell to display a reactive score table (including fractional marks for partial credit).
4. Generate the release notebook¶
Strip solutions and embed integrity hashes:
This creates release/hw1/hw1.py with solutions removed and cell hashes embedded. Use --dry-run to preview, --validate to check markers only.
5. Distribute to students¶
6. Collect submissions¶
7. Autograde¶
This:
- Auto-discovers the source notebook in
source/hw1/ - Checks submission integrity against the source (detects tampered cells)
- Executes each notebook via
marimo export html - Parses check results from the output
- Injects grading cells (verification summary + marker feedback placeholders)
- Writes results to
gradebook.db - Saves grading copies to
autograded/hw1/
Options:
-j 8— parallel workers (default: 4)--timeout 600— per-notebook timeout (default: 300s)--safety-check— scan for dangerous code before execution--max-memory 2048— memory limit in MB--force— re-grade all even if output is up to date
8. Manual grading with the grader¶
This opens a marimo web app with four tabs:
- Assignments — overview with pipeline status and action buttons
- Submissions — per-student status with marks breakdown
- Grading — navigate between students, set marks and feedback
- Students — cross-assignment marks table
For a persistent server deployment:
9. Export feedback¶
This exports graded notebooks to HTML in feedback/hw1/, injecting the marker's mark and feedback as a callout. If you've configured late penalties in mograder.toml, they are applied automatically during feedback export:
Use --no-penalties to skip penalty computation, or --due-date to override the deadline.
10. Upload grades¶
Worked example¶
The examples/ directory in the mograder repository contains two complete example assignments:
examples/source/demo-assignment/— per-question marks (numerical methods)examples/source/demo-holistic/— holistic grading (string processing)
Try the full workflow:
cd examples
mograder generate demo-assignment demo-holistic
mograder autograde demo-assignment
mograder feedback demo-assignment
Hub deployment¶
For cloud-hosted assignment delivery, the hub provides a multi-user server where students access assignments through a browser without installing mograder locally.
# mograder.toml
[hub]
port = 8080
notebooks_dir = "hub-notebooks"
release_dir = "hub-release"
session_ttl = 3600
Publish workflow:
- Generate release notebooks:
mograder generate A1 - Upload to Moodle:
mograder moodle upload A1 - Publish to hub:
mograder hub publish A1 --url $HUB_URL --token $TOKEN - Warm the dependency cache:
mograder hub warm-cache --url $HUB_URL --token $TOKEN
The hub requires MOGRADER_HUB_SECRET to be set on the server. Use --headless when running on a remote server. Students export their work from the hub and upload to Moodle for submission.
See Hub usage guide and Hub deployment guide for details.
Next steps¶
- Grader API Reference — detailed documentation of
check(),Grader, andhint() - Security — threat model and hardening options for autograde
- Student Setup Guide — share this with your students
- Configuration — full
mograder.tomlreference - Hub — multi-user hub server for cloud-hosted assignment delivery