Skip to main content

Cadence — live student progress dashboards for Jupyter teaching

Project description

Cadence — live student progress dashboards for Jupyter

cadence-edu is the Jupyter-side half of Cadence: a tiny set of magics and helpers that let a teacher register checkpoints in a notebook, students answer them inline from their own notebooks with check("id", value), and a teacher dashboard shows live solve counts, attempts-to-first-correct, and the most common wrong answers in real time.

No grader. No autograder pipeline. No login required for quick one-off lessons. Just pip install, %load_ext cadence, and a join code on the projector.

pip install cadence-edu

Quickstart (5 minutes)

The fastest path: let the CLI mint both notebooks for you, then edit in place.

pip install cadence-edu

cadence-cli new teacher --name "Fibonacci warm-up"   # writes ./teacher-setup.ipynb
cadence-cli new student --name "Fibonacci warm-up"   # writes ./student.ipynb

Each starter has the right magics pre-wired — %load_ext cadence, %cadence_create_lesson / %cadence_session, a YAML registration block, an example check(...) — so you can jupyter notebook straight into editing real content.

If you'd rather copy a cell by hand:

Teacher notebook (run once per lesson, keep it private):

%load_ext cadence
%cadence_create_lesson "Fibonacci warm-up"
# → prints a join code (e.g. soup-river-42) and the dashboard URL

%cadence_register fib-10 --comparator numeric --expected '{"value": 55}'
%cadence_register greet  --comparator exact   --expected '"hello"'

%cadence_self_test            # verifies your expected answers parse correctly
%cadence_show_join            # big-text join code for the projector

Student notebook (distributed to the class — reusable every term):

%load_ext cadence
%cadence_session soup-river-42 "Alice Smith"

from cadence import check
check("greet", "hello")
check("fib-10", fib(10))

The teacher's dashboard updates live as students submit. That's the whole product.

Discovering commands as you go: type %cadence_<Tab> for autocompletion, %cadence_register? for argparse-style help on any magic, or %cadence_help for a one-page cheatsheet of every Cadence magic with its exact syntax. After typing %cadence_lesson (or %cadence_course) the names cached in ~/.cadence/lessons.yaml tab-complete too.


Concepts

Term What it is
Lesson One notebook's worth of checkpoints. Created with %cadence_create_lesson. Has its own join code + teacher token. Anyone can create one — no account required.
Course A named group of notebook lessons (e.g. "Fall 2026"). Created with %cadence_create_course. Requires %cadence_login. Students join the course with one code, then pick a notebook.
Checkpoint One expected answer in a lesson. Has an ID, a comparator (exact/numeric/set/regex/manual), an optional hint, and an optional worked solution. Registered with %cadence_register.
Session A student's enrollment in a lesson or course. Started with %cadence_session <code> "<name>".

Teacher workflow

Creating a single lesson (no account needed)

%load_ext cadence
%cadence_create_lesson "Week 3: Fibonacci"

The first run creates the lesson and caches the teacher token in ~/.cadence/lessons.yaml. Re-running with the same name reactivates the cached lesson — safe to run at the top of every notebook session. Use --force if you genuinely want a second lesson with the same name (different token, different join code).

Creating a course of lessons (account required)

%load_ext cadence
%cadence_login                    # prompts for username + password
                                  # or: %cadence_login --token <jwt>
%cadence_whoami                   # confirm who's logged in
                                  # %cadence_logout clears the cached JWT

%cadence_create_course "Fall 2026 Statistics"
%cadence_add_notebook "Week 1 — Variables"
%cadence_add_notebook "Week 2 — Distributions"

%cadence_add_notebook creates a brand-new lesson inside the active course. To pull in a lesson you already created standalone, use %cadence_attach_lesson "My Lesson" --to "Fall 2026 Statistics".

The first lesson or course creation prompts inline to accept the Terms of Service; if you'd rather accept up front in a script, run %cadence_accept_terms first.

Registering checkpoints

%cadence_register fib-10 \
    --comparator numeric \
    --expected '{"value": 55, "tolerance": 0.001}' \
    --hint "Remember: fib(0)=0, fib(1)=1." \
    --order 2

Comparators and the --expected shape they take:

Comparator --expected Match rule
exact '"hello"' or '{"value": "hello"}' str(submitted).strip() == str(value).strip()
numeric '{"value": 55}' or '{"value": 3.14, "tolerance": 0.001}' abs(submitted - value) <= tolerance
set '{"value": [1, 2, 3]}' set(submitted) == set(value) (order-independent)
regex '{"pattern": "^[A-Z].*"}' re.match(pattern, str(submitted))
manual (none) Student self-attests with mark_done("id")

Re-running %cadence_register with the same ID updates the checkpoint in place.

Bulk registration for big lessons — drop a whole YAML body into one cell:

%%cadence_register_yaml
- id: setup.mean-value
  comparator: numeric
  expected: {value: 49.5, tolerance: 0.001}
  hint: average of 0..99
- id: discovery.higgs-peak
  comparator: exact
  expected: 125
  reveal_after: 3
  solution_code: |
    bin_edges = np.arange(100, 151)
    counts, _ = np.histogram(m_gg, bins=bin_edges)
    int(bin_edges[np.argmax(counts)])
  allow_submissions: true

Or keep the YAML in version control alongside your notebook and load it:

%cadence_register_yaml_file checkpoints/week3.yaml

Verifying before class

%cadence_self_test

Submits each checkpoint's own expected answer and prints a pass/fail table. Catches typos in --expected and bad tolerance bounds. Regex checkpoints are skipped (can't auto-synthesize a matching string).

Displaying the join code

%cadence_show_join

Big-text rendering of the active lesson or course's join code — designed for projector / screen share.

Managing existing lessons and courses

%cadence_lesson "Week 3: Fibonacci"           # reactivate a cached lesson
%cadence_course "Fall 2026 Statistics"        # reactivate a cached course

%cadence_clone_lesson "Fall 2026 Week 3" --as "Spring 2027 Week 3"
                                              # duplicate (fresh code + token)

%cadence_attach_lesson "Lab 1" --to "Fall 2026 Statistics"
%cadence_detach_lesson "Lab 1" --from "Fall 2026 Statistics"

%cadence_delete_lesson "Old test lesson" --yes    # wipes the lesson + ALL student data
%cadence_delete_course "Fall 2025" --yes          # wipes the course; attached lessons survive

Rotating a leaked teacher token

%cadence_rotate_token                         # mints a fresh teacher_token
%cadence_rotate_token --also-join-code        # full revocation: also reissues the join code
                                              # (existing student notebooks break)
%cadence_rotate_token --course                # rotate the active course's token

The local ~/.cadence/lessons.yaml is updated in place and a fresh dashboard URL is printed.

Code submissions (optional)

For checkpoints registered with --allow-submissions, students can use %%cadence_submit <id> to send the cell's source code to your dashboard for review:

%%cadence_submit fib-recursive
def fib(n):
    return n if n <= 1 else fib(n-1) + fib(n-2)
fib(10)

The cell still runs normally — the student sees their output. The source is sent alongside.

Retention controls

%cadence_set_retention --days 30              # active lesson
%cadence_set_retention --days 90 --course     # active course

Retention can only be shortened, never extended. To lengthen it you'd clone the lesson and migrate fresh students over — a design constraint, not a bug.


Student workflow

Joining

%load_ext cadence
%cadence_session soup-river-42 "Alice Smith"

If the join code belongs to a course, the magic prints the list of notebooks and reminds you to pick one:

%cadence_notebook "Week 1 — Variables"

Answering checkpoints

from cadence import check, show_hint, show_solution, mark_done, submit_image

result = check("fib-10", fib(10))       # returns a CheckResult
# result.is_correct, result.attempt_num, result.hint
# Also renders in the cell as a coloured ✅/❌ chip.

show_hint("fib-10")                     # fetches the teacher's hint
show_solution("fib-10")                 # fetches the worked solution if revealed
mark_done("manual-checkpoint")          # self-attest a manual checkpoint
submit_image("plot-1", fig)             # for `--allow-submissions` image checkpoints

Timed answers

%%cadence_time fib-10
def fib(n):
    return n if n <= 1 else fib(n-1) + fib(n-2)
fib(10)

Measures wall-clock time and submits the last-expression value as the answer. Only the first correct attempt's time contributes to the dashboard histogram, so re-running a known-good cell doesn't pollute the stats. If the cell raises, nothing is submitted.

Your data rights (GDPR)

cadence-edu ships explicit support for the three Article-15/17/20 rights:

%cadence_my_data                # Article 15 — see everything stored about this session
%cadence_export_my_data         # Article 20 — dump it as JSON
%cadence_export_my_data --path ~/my-cadence-data.json
%cadence_delete_my_data --yes   # Article 17 — wipe attempts, submissions, the session itself

%cadence_delete_my_data cannot be undone and clears the active session from the kernel afterwards.


CLI helpers

The package also installs a cadence-cli for two jobs from the shell:

Scaffold a starter notebook. Drops a pre-wired .ipynb in the current directory so you don't start from a blank cell:

cadence-cli new teacher --name "Week 3: Fibonacci"    # writes ./teacher-setup.ipynb
cadence-cli new student --name "Week 3: Fibonacci"    # writes ./student.ipynb
cadence-cli new teacher --out path/to/setup.ipynb --force   # custom location, overwrite

The teacher scaffold has %load_ext cadence%cadence_login%cadence_create_lesson → a YAML registration block → %cadence_self_test in order. The student scaffold has a placeholder %cadence_session line and one example check(...). Both are tiny on purpose — they're a launching pad, not a tutorial. The longer-form particle-physics demos live at cadence-dash.com/demo.

Manage locally-cached teacher credentials — useful when the server-side lesson has been deleted but the local YAML is stale, or when you suspect a token leak:

cadence-cli lessons list                              # every cached lesson + course, tokens masked
cadence-cli lessons forget "Week 3: Fibonacci"        # drop a stale row (local only)
cadence-cli lessons forget "Week 3: Fibonacci" --yes  # skip the confirmation prompt
cadence-cli lessons rotate "Week 3: Fibonacci"        # mint a new teacher_token
cadence-cli lessons rotate "Spring 2026" --also-join-code   # full revocation

forget only touches the local YAML. rotate calls the backend and updates the cache in place.


Configuration

Files

Path What it holds
~/.cadence/lessons.yaml Cached teacher tokens + join codes for lessons and courses (mode 0600)
~/.cadence/credentials.yaml Teacher JWT from %cadence_login (mode 0600)
~/.cadence/terms.yaml Recorded ToS acceptance from %cadence_accept_terms

Environment variables

Variable Default Purpose
CADENCE_API_URL https://api.cadence-dash.com Backend API base URL. Set to http://localhost:8000 for local dev.
CADENCE_DASHBOARD_URL (falls back to CADENCE_WEB_URL, then http://localhost:3000) Where the teacher dashboard lives. Used to build the URLs printed by create-lesson.
CADENCE_WEB_URL https://cadence-dash.com Public web URL — used for legal-page links.

For self-hosted setups, set CADENCE_API_URL + CADENCE_DASHBOARD_URL to your own deployment.


Troubleshooting

"API not available" / SSL errors on first magic. The package defaults to the hosted backend at api.cadence-dash.com. If you're running locally, point it at your own instance:

export CADENCE_API_URL=http://localhost:8000
export CADENCE_DASHBOARD_URL=http://localhost:3000

Extension didn't load. Confirm the install with:

jupyter server extension list           # Notebook 7 / JupyterLab

Then re-run %load_ext cadence in the kernel.

Stale cached lesson. If you docker compose down -v'd your local backend, your ~/.cadence/lessons.yaml will reference lessons that no longer exist. Drop them with cadence-cli lessons forget "<name>".

Teacher token leaked. %cadence_rotate_token mints a new one and invalidates the old. Add --also-join-code if you also need to lock out existing students.


Other primitives

A few legacy helpers are still exposed for the original code-submission flow:

  • CadenceAPI — direct API client (from cadence import CadenceAPI)
  • ProblemNotebook / create_problem_notebook — pre-formatted problem-notebook templates with embedded metadata and test cases

These predate the live-progress flow and aren't on the happy path; use them only if you're integrating with an external grader.


Links

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cadence_edu-0.1.6.tar.gz (55.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cadence_edu-0.1.6-py3-none-any.whl (53.6 kB view details)

Uploaded Python 3

File details

Details for the file cadence_edu-0.1.6.tar.gz.

File metadata

  • Download URL: cadence_edu-0.1.6.tar.gz
  • Upload date:
  • Size: 55.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for cadence_edu-0.1.6.tar.gz
Algorithm Hash digest
SHA256 5d285c565289b835a8721097fbb2652218887ff8d2326a7d5a19af222a0c6cb4
MD5 402e2e6ce4c1ed981c405a0cf6fe35bc
BLAKE2b-256 2ebb20c3683f99533130f522b96257ef04fd60abd12bec07f6d00f5e6e0d3fc6

See more details on using hashes here.

File details

Details for the file cadence_edu-0.1.6-py3-none-any.whl.

File metadata

  • Download URL: cadence_edu-0.1.6-py3-none-any.whl
  • Upload date:
  • Size: 53.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for cadence_edu-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 3dd4fd1599641a668ab30c75dd0f41ff8e0bc42f8aada5cc34d23b4cb67a2549
MD5 c09918a157ef60d5aaff4f2d54d31130
BLAKE2b-256 1a8f96bd227b543f309211cfec17b60636771a1cab3e57ac05de11dfaf9bfc67

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page