1
0
Benjamin Mako Hill 7845167ec5 Phase 4: student views, opt-outs, and class schedule
Student page with a standing summary, a class-comparison histogram
(zero-dependency HTML/CSS) with a plain-language fewer/same/more
sentence, opt-out management with withdrawal of future dates, and a
call history that respects the per-course assessment-visibility
setting and never shows pending or skipped calls. Opt-outs validate
against a new per-course class-day schedule (range generator plus
individual add/remove for holidays), since Canvas has no structured
meeting-day data; courses without a schedule fall back to a free date
picker. Dev mode seeds a Tue/Thu pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:44:49 -07:00

coldcall-lti

A Canvas external tool (LTI 1.3) for managing cold calls in case-based classes: it selects students to call using weighted randomness, records what happened with each call, lets students report planned absences, and reports participation data back to both instructor and students. It replaces a manual workflow built on exported rosters, Google Forms, and local scripts.

How it works

The tool is a Flask application that Canvas launches over LTI 1.3. A single URL serves everyone: Canvas identifies the person and course on each launch, so instructors get the call-list and reporting views while students get the absence form and their own history. The roster comes from Canvas through the Names and Role Provisioning Service, which means adds and drops are picked up automatically rather than reconciled by hand.

Selection uses the same weighting as the manual system it replaces: each answered call divides a student's weight by the course's weight factor (default 2), so students who have answered more questions become progressively less likely to be called. Each course can instead use "cycle" mode, which shuffles the roster and calls everyone exactly once. These, along with whether students can see their own assessments, are per-course settings.

Layout

  • coldcall_lti/models.py — SQLAlchemy models: courses (with their settings), students, enrollments, opt-outs, and calls, plus the query helpers that feed selection.
  • coldcall_lti/selection.py — the weighted and cycle selection logic. Kept free of database and web dependencies so it can be tested and reasoned about on its own.
  • coldcall_lti/__init__.py — the Flask application factory.
  • migrations/ — alembic migrations. The schema avoids database-specific types so the same migrations run on SQLite (the default) and MariaDB/MySQL; switching is a matter of changing COLDCALL_DATABASE_URL.
  • tests/ — pytest suite covering selection behavior and the model helpers.

Setup

Development uses a virtualenv that shares the system's Debian-packaged libraries (Flask, SQLAlchemy, alembic, pytest) and adds the one PyPI-only dependency, the maintained pylti1p3next fork of PyLTI1p3:

python3 -m venv --system-site-packages .venv
.venv/bin/pip install pylti1p3next
.venv/bin/pip install -e .

Create the database and run the tests:

.venv/bin/python -m alembic upgrade head
.venv/bin/python -m pytest tests/

Configuration is by environment variable: COLDCALL_DATABASE_URL (any SQLAlchemy URL; defaults to an SQLite file under instance/), COLDCALL_SECRET_KEY for Flask sessions, COLDCALL_LTI_CONFIG (path to the LTI platform configuration), and COLDCALL_DEV_MODE=1 to enable the fake-launch pages.

Connecting to Canvas

The tool speaks LTI 1.3, which requires a Developer Key created by a Canvas account admin. The key points Canvas at three endpoints here: /lti/login (OIDC initiation), /lti/launch (the launch target), and /lti/jwks (this tool's public keys). The platform side is described in a JSON file — copy lti_config.example.json to instance/lti_config.json and fill in the client id and deployment id from the Developer Key. Generate the tool's keypair alongside it:

openssl genrsa -out instance/private.key 4096
openssl rsa -in instance/private.key -pubout -out instance/public.key

On each instructor launch the tool refreshes the course roster from Canvas through the Names and Role Provisioning Service, so enrollment changes appear without any manual step.

Developing without Canvas

Because a Developer Key takes institutional approval to get, the app has a fake-launch mode for local development:

COLDCALL_DEV_MODE=1 .venv/bin/flask --app coldcall_lti run --debug

Then open http://localhost:5000/dev and launch as the fake instructor or any of the fake students. This sets up exactly the session state a real launch would, and the fake roster flows through the same sync code as real NRPS data, so everything past the launch behaves identically. Dev mode also relaxes the cookie settings that Canvas's iframe embedding requires in production (SameSite=None; Secure), which would otherwise break plain-http localhost use.

Description
an LTI app to manage cold calling students in a weighted random way (e.g., from within Canvas LMS)
Readme 150 KiB
Languages
Python 82.3%
HTML 17.4%
Mako 0.3%