Django-based instructor interface for AI-assisted grading workflows on Canvas assignments. Uses canvas-instructor-tools for Canvas API interactions and integrates with OpenAI-compatible AI providers (Promptly, OpenAI, etc.).
- Canvas Integration: Sync assignments, download submissions (files + URL content), and post grades/comments back
- AI Feedback Generation: Generate draft feedback using OpenAI-compatible providers with rubric-aware scoring
- Smart Sampling: Head+tail file truncation preserves beginning and end of long files (configurable limits)
- Batch Processing: Background job queue for bulk draft generation across all students
- Cohort Summary: Async generation of assignment-level analysis from student feedback
- Approval Workflow: Require instructor review before posting any grade/comment to Canvas
- Diagnostics: Track file sampling, truncation, and prompt token usage per draft
- Unsubmitted Handling: Automatically skip students with no submission
- Multi-pass Generation: Optional detailed analysis and refinement passes
- Draft History: Review all previous AI-generated drafts with metadata (provider, model, mode)
- Rubric Support: Define scoring criteria with point scales; AI applies rubric in structured table format
-
Create and activate a virtual environment in this directory.
-
Install dependencies:
pip install django pip install -e ../canvas-instructor-tools
-
Run migrations:
python manage.py migrate
-
Start the app:
python manage.py runserver
-
Open the gradebook at
http://127.0.0.1:8000/.
CANVAS_API_URL: Canvas base URLCANVAS_API_KEY: Canvas API token
FEEDBACK_AI_API_KEY: Promptly API key (sk-...)FEEDBACK_AI_BASE_URL: defaults tohttps://promptlyapi.com/v1FEEDBACK_AI_MODEL: defaults todefault(Promptly accepts but may ignore)FEEDBACK_AI_TEMPERATURE: optional, defaults to0.2OPENAI_API_KEY: fallback ifFEEDBACK_AI_API_KEYis not set
FEEDBACK_MAX_PROMPT_FILES: max files to include (default:8)FEEDBACK_MAX_PROMPT_FILE_CHARS: max chars per file (default:24000)FEEDBACK_MAX_PROMPT_TOTAL_CHARS: total char budget (default:52000)
FEEDBACK_SECRET_KEY: Django secret key overrideFEEDBACK_DEBUG:true/falseFEEDBACK_ALLOWED_HOSTS: comma-separated hostsFEEDBACK_CSRF_TRUSTED_ORIGINS: for HTTPS deploymentsFEEDBACK_BIND_PORT: Docker bind port (default:18080)
POSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DB
- Sync an assignment by Canvas
course_idandassignment_id - Open a submission and generate AI draft feedback
- Review diagnostics (files included, truncation status)
- Edit score/feedback as needed
- Approve and post to Canvas
- Configure rubric and additional instructions on assignment page
- Click "Batch Download & Generate Drafts" (optional: enable multi-pass or refinement)
- Background worker processes all submissions
- Monitor progress with live polling UI
- Review and approve individual submissions
- Generate student feedback drafts first
- Click "Generate Cohort Summary (Background)"
- AI analyzes all feedback to identify common patterns, strengths, and mistakes
- Summary appears automatically when generation completes
Run the worker process to handle batch and cohort summary jobs:
python manage.py process_batch_jobsFor production, run as a systemd service or Docker container.
Target deployment path on VPS: /opt/feedback
This repository includes:
Dockerfilefor the Django app (gunicorn)docker-compose.prod.ymlwith app + PostgreSQLdeploy/bootstrap-vps.shfirst-run VPS bootstrap helper (safe if Docker already exists)- GitHub Actions workflows:
.github/workflows/ci.yml.github/workflows/deploy.yml
Use the sample config at deploy/nginx/feedback.perdrizet.org.conf and point it to
127.0.0.1:18080 (or your configured FEEDBACK_BIND_PORT).
The deploy workflow injects all runtime configuration from GitHub Secrets and writes
/opt/feedback/.env automatically at deploy time (no manual .env management required).
Required repository secrets:
VPS_HOSTVPS_USERVPS_SSH_KEYGHCR_USERNAMEGHCR_PATFEEDBACK_SECRET_KEYFEEDBACK_ALLOWED_HOSTS(for example:feedback.perdrizet.org)FEEDBACK_CSRF_TRUSTED_ORIGINS(for example:https://feedback.perdrizet.org)FEEDBACK_AI_API_KEYFEEDBACK_AI_BASE_URL(for Promptly:https://promptlyapi.com/v1)FEEDBACK_AI_MODEL(for Promptly:default)FEEDBACK_AI_TEMPERATURE(for example:0.2)CANVAS_API_URLCANVAS_API_KEYPOSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_DBFEEDBACK_BIND_PORT(for example:18080)
Deployments run on push to main and via manual dispatch in GitHub Actions.
Deploy runs docker compose up -d, then checks http://127.0.0.1:${FEEDBACK_BIND_PORT}/healthz/ in a retry loop.
- In the current single-instance setup, this is a health gate, not a traffic switch.
- It verifies the updated app actually boots and serves requests.
- If unhealthy, the workflow fails and prints app logs for debugging.
For true no-downtime traffic switching, you would run a blue/green pattern with two app stacks and change the nginx upstream only after the new stack is healthy.