Skip to content

About

Rubric maker for use in education. Geared towards use in EFL Classrooms

Topics

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

2,720 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rubric Maker

CI CodeQL Lighthouse CI Coverage License Live demo

React TypeScript Vite Supabase PWA WCAG

Rubric Maker landing page

A comprehensive rubric creation and grading tool built with React and TypeScript — self-hostable with full functionality, and offline-capable (with reduced capabilities) when no backend is configured. Designed for educators who need to design complex rubrics, grade students efficiently, and analyse performance — including language proficiency tracking aligned to the Common European Framework of Reference (CEFR).

🚀 Try it now — the live demo runs entirely in your browser. No account, no install, no backend.


Contents


Quick start

Pick the path that fits:

You want to… Do this
Try it immediately Open the live demo — data stays in your browser
Run it locally (no backend) The three commands below; everything works in localStorage
Run the full stack (Docker + Supabase sync) Jump to the Docker quick start

Run locally (3 commands):

git clone https://github.com/NesiciCoding/RubricMaker.git
cd RubricMaker
npm install
npm run dev

Open http://localhost:5173. No backend or account is required — without one, all data lives in the browser's local storage.


How to read this README

This document is the landing page, not the manual. Everything below is organised by audience — start where you belong and follow the links for depth.

Reader Start with
Teachers & educators — you want to use the app Features, then the user documentation wiki
Students — you use the student portal Student Portal wiki page
School IT / admins — you deploy and operate it Deployment → Installation wiki
Developers — you build or contribute to the code Development → Tech stack → Developer reference → Contributing

Sections marked 🧑‍💻 in the contents are written for developers — if you're here to use Rubric Maker, you can skip them entirely.


Features

The full, illustrated feature documentation lives in the Features wiki page (with screenshots). Here is the one-glance overview:

Area What you can do
Classes & cohorts Classes carry one or more school years and Dutch VO tracks, so a mixed group (groep 7 & 8, or VMBO-TL/HAVO together) is a single class; cohort chips filter the combined roster, and each student can be pinned to one of their class's own years and tracks — CEFR targets, mastery targets and cohort calibration then follow that tag.
Rubric builder Flexible criteria and performance levels; three scoring modes (total points, weighted percentage, single-point); tiered rubrics that put several tiers (school year + optional VO track, e.g. Mavo/Havo/VWO) side by side with shared levels, a statement checklist per level × tier, requirements and a points-based verdict, each student graded in their own tier, comparative grading paired within a tier or across tiers, import of tiered sheets from Word or PDF, and PDF/DOCX exports and previews in the layout of the paper sheet; sub-items as scored components or as an evidence checklist you tick while grading (with a best-fit level suggestion), point ranges and score modifiers; link criteria to CCSS/NGSS/Dutch kerndoelen standards, CEFR Can-Do descriptors, IB Learner Profile, Bloom's Taxonomy or the SLO kerndoelen for English (primary and secondary, with a per-kerndoel Statistics chart); grammar tagging; automatic versioning with restore.
Folders Separate folder trees on the Rubrics, Tests, Essays and Flashcards lists, nesting up to four levels deep. The folder bar shows one level with a breadcrumb (“New folder” becomes “New subfolder” inside a folder). Drag a card onto a folder tile to file it; rubric and test cards and folder tiles also have a “Move to…” menu, and filed rubric and test cards show their folder path. Deleting a folder keeps its items — they move back into the full list — and moves its subfolders up one level.
Tests & quizzes Nineteen question types (multiple-choice, multiple-response, true/false, short answer, open, fill-the-gap, word-bank fill-in with draggable tiles, dictation (deterministic word-level scoring), key word transformation (word limit, 2 marked parts), word-formation gaps with derived-form suggestions, error correction (click-to-mark fragments with accepted corrections; on Supabase-hosted assignments the corrections are withheld from students, while offline share-code tests include the answer key), sentence builder (shuffled word tiles, several accepted orders), grid of statements × shared options, matching, ordering, categorize, hot text, audio-response, …); rich-text prompts with inline images (drag the corner handles to resize) and click-to-insert gap pills; deterministic cloze-gap generators (fixed-ratio, C-test, part-of-speech, above-CEFR-level, academic vocabulary); a passage-level check against the class's target CEFR level with one-click pre-teaching decks; generate matching/multiple-choice/cloze/categorize questions straight from a flashcard deck; listening controls (play limits logged as proctor events, browser-voice stimuli, practice-mode transcripts, per-option audio) and speaking prep time with a cue card; rubric-scored writing/speaking tasks (per-criterion scoring mapped to question points, CEFR per criterion) with teacher-only writing insights; opt-in tolerant answer matching (punctuation, contractions, British/American spelling, minor slips) and distractor suggestions; per-question standards/CEFR links; class assignments with unique share links, due dates, optional Safe Exam Browser, and grade scales; per-student read-aloud accommodation for passages, prompts and options in every test mode, including graded ones; chip/card-based visual editors for error correction, key word transformation, word-bank fill-in and sentence builder (raw syntax stays one click away via "Edit as text"); and a live A4 preview pane in the test builder with an answer-key toggle, point values, print, and an editable header, instructions and footer.
Score sheet & test quality Spreadsheet-style grid (students × questions) to type or paste the points earned per question, for any test or a quick-created score-only paper test whose columns can be linked to standards, CEFR and grammar descriptors; absent students; per-test grading norm — percentage → grade scale, Dutch N-term (CvTE rules), pass mark/caesura, or a points → grade table with a column per school year/track whose cells hold 1–10 grades or labels such as IO/B/E (with a per-column "sufficient from" label) (picked from each student's year/track, or every column shown and labelled when it can't be); norm grades flow into the results page and Magister/SOMtoday exports; analysis tab with mean/SD/median, Cronbach's α (KR-20), SEM, and — for numeric 1–10 norms — grade distribution and pass rate (a percentage distribution under a grade scale) and per-question P-value, Rit, Rir, upper/lower-27% discrimination and α-if-deleted with flags.
Practice & placement Practice mode (listening, reading, grammar) that never affects graded results; three adaptive placement engines — multistage routing, staircase (A2→C2 ladder), and a generator that pulls questions live from your question bank (filtered by CEFR range, skill — the five CEFR skills plus grammar — and tag, and drawing whole reading/listening section bundles as well as standalone questions) with Elo ordering — each producing a provisional CEFR estimate.
Grading Click/level/slider scoring in a card or compact grid layout; keyboard shortcuts (1–5, A+1, …); score modifiers with a reason; voice-graded comments; a taggable comment bank with smart suggestions; scan handwritten answers with the device camera or an imported photo/PDF and OCR them on-device (Tesseract.js) straight into feedback; scan a paper exam's filled-in CITO-style answer sheet back in — drop in a whole stack of photos or a multi-page PDF, assign pages to students by QR code or printed name, read multiple-choice/true-false bubbles by mark detection and short-answer/numeric/open text by per-question OCR across every page, then review and save every student in one action; group grading, comparative side-by-side grading (it loads and updates each student's existing grade rather than adding another, flags students who already have duplicate grade records so the extras can be moved to the archive, and keeps a per-rubric, per-student comparison cap that persists across reloads and devices so a class can't be compared forever), peer review with reviewer analytics, and self-assessment; co-grading with an independent second marking and a moderation queue for disputes.
Essays Standalone essay assignments with prompts, rubric links, word/time limits and live monitoring; a distraction-free student focus mode; rich-text TipTap editor; anonymous submission codes; OCR (Tesseract.js) and DOCX parsing for uploaded documents; inline anchored comments on submissions; deterministic (no-AI) writing statistics in the grading view — sentence-length variance, transition-word frequency, Flesch-Kincaid — plus a CEFR-J grammar-range panel (a range indicator, not a grade) driven by a UD dependency parse (udpipe-wasm; the CC BY-NC-SA model is downloaded separately, see public/models/README.md).
Flashcards Anki-style decks scheduled with the FSRS spaced-repetition algorithm; vocabulary decks with phonetic/part-of-speech fields and grammar decks that feed grammar practice; import from CSV/XLSX/DOCX/TXT; assign to classes and inspect per-student learning insights; students can create their own decks and share them read-only with a teacher.
CEFR & language assessment Per-student and whole-class proficiency overviews (Reading, Writing, Speaking, Listening) benchmarked against track/year expected ranges; structured speaking sessions; rule-based learning paths and intervention flags; a grammar mastery profile merging test, flashcard and writing evidence; optional Cambridge English exam mapping.
Vocabulary profiling A rule-based (no-AI) CEFR vocabulary profiler over analysed documents: per-class/student level distribution, off-list share and academic-word (AWL/NAWL) coverage; a "right for my class?" target-level verdict on any text; export word lists by band or seed a flashcard deck from them.
Student portal Unique shareable link per student (no login needed): grades, teacher comments and files, a combined to-do list, essays and tests, flashcard study, self-assessment, portal search, messaging with the teacher, and an interface-language picker (EN/NL/FR/DE/ES).
Analytics Home dashboard opens with a Student Snapshot — one row per student with CEFR level, Grammar mastery, Vocabulary mastery, and a needs-attention flag, class-filterable by chips and colored on a configurable scale; statistics dashboard with grade distributions, per-criterion breakdowns, multi-class comparison with an insights panel, progress trends that combine rubric grades and graded test results, a Customise panel to choose which cards are shown, and an advanced query builder (filter, group and aggregate every grade — e.g. students averaging below a pass or whose scores dropped — with saved queries and CSV export); an activity dashboard (rubric × test × essay × class grid); vocabulary profile dashboards; per-student profiles with portfolios; rule-based writing trends (weak or declining rubric criteria across graded essays) per student and per class; overdue tracking.
Export PDF reports (individual or class); Word (.docx) styled by rubric-table and style templates; CSV with ready-made Magister/SOMtoday gradebook presets; aggregated period reports; per-student report cards; essay exports (Markdown/DOCX/PDF); calendar (.ics) exports; CITO-style printable exam export (question booklet, answer sheet, attachment, grading sheet) as PDF or Word.
Collaboration A school marketplace to publish/clone rubrics, tests, decks and bank items; live sharing of rubrics and tests (and whole folders) with co-teachers or the whole school, view-only or editable, with a per-teacher default and a teacher-filterable admin view; class sharing with co-teachers (view-only or view & grade) from a class's options menu on the Students page — "view & grade" co-teachers grade the class with its rubrics and tests, change any grade or test result in it, and assign the owner's tests, while the class's student roster (name, class, track, school year) stays editable by the owner only; live test/essay monitoring with proctoring flags; batch grading-task assignment to colleagues; student messaging and a notification center.
Accessibility WCAG 2.1 AA with AAA (7:1) colour contrast, gated by axe-core in CI; dyslexia-friendly reading mode; six theme bundles; in-app Joyride tours; global search (Ctrl/Cmd+K) with filter tokens.

Offline-first, cloud-ready: the core grading workflow (rubrics, grading, statistics, flashcards) works fully offline with data in localStorage. Connecting an optional Supabase backend unlocks everything that needs a shared server — cross-device sync, multi-teacher collaboration, the student portal, messaging, notifications and the marketplace. If Supabase is configured but unavailable, the app continues with the reduced-capability localStorage fallback rather than failing.


Screenshots

Grading interface CEFR proficiency overview
Interactive grading — click levels, slider scoring, comment bank, keyboard shortcuts Per-student and whole-class CEFR overview, benchmarked by track and year
Student portal Statistics dashboard
The student portal — grades, to-dos, essays, flashcards, and messaging Statistics dashboard with grade distributions, per-criterion breakdowns, and multi-class comparison

More illustrated walkthroughs — including manual captures of live-service features — are on the wiki.


Documentation

User documentation (wiki): Home · Getting started · Features · Tests & Practice · Flashcards · Student Portal · Supabase Sync · FAQ

Deployment & operations (repo docs/):


Development

To run the project locally:

  1. Install dependencies:

    npm install
  2. Start the development server:

    npm run dev
  3. Open http://localhost:5173 in your browser.

Other useful commands:

npm run check        # Full pre-push gate: typecheck + lint + format + unit tests
npm run typecheck    # TypeScript check (run before commits)
npm run lint         # ESLint
npm run test         # Vitest unit tests
npm run coverage     # Coverage report
npm run e2e          # Playwright end-to-end tests (starts its own dev server, see below)

# Supabase local dev (optional)
npm run db:start     # Start local Supabase stack
npm run db:reset     # Reset and re-apply all migrations

# Backend load testing (k6) against a local Supabase stack (needs `npm run db:start`).
# Reads Supabase keys automatically; profiles: smoke | load | stress | spike | soak.
npm run loadtest              # default 'load' profile (~50 VUs)
npm run loadtest:stress       # climb VUs to find the breaking point

End-to-end tests and their dev server

Playwright starts and stops its own dev server, on a port derived from the checkout path: 5173 for the first checkout, a stable neighbour for any extra worktree, so two suites can run side by side without sharing a server. It never attaches to a server it did not start unless you ask it to.

Variable Effect
E2E_PORT Use this port instead of the derived one
E2E_BASE_URL Test an already-running origin (a preview deployment, a remote stack) and start no server
E2E_REUSE_SERVER 1 attaches to a server already on the port — only if it serves this checkout

Every dev server advertises the checkout it serves in an x-rm-checkout header, and the suite refuses to run when the stamp doesn't match. That is deliberate: a fixed port meant a second worktree's dev server could answer instead, and the whole run then passed against different code.

New to the codebase? See the Development Guide and Architecture wiki pages.


Tech stack

Layer Choice
UI React 19 + TypeScript, built with Vite 8
Styling Plain CSS + Radix UI primitives
Rich-text editing TipTap (ProseMirror)
Backend & sync Supabase (optional — Postgres, auth, storage, realtime, edge functions)
Spaced repetition ts-fsrs (FSRS algorithm)
Unit testing Vitest
End-to-end testing Playwright
CI / CD GitHub Actions (CI, CodeQL, Lighthouse, e2e, deployments)

Deployment

RubricMaker works in two modes:

  • Offline-only — data lives in the browser's local storage. No server needed. Works on GitHub Pages, SharePoint, or any static host.
  • With database sync — add an optional Supabase backend for multi-device sync, email login, and rubric sharing between teachers. Hosted on your own infrastructure.

Deploying for a school? Read the Installation wiki page first — it walks through the choices end to end.

Option A: Docker (full stack)

The easiest way to run the full stack. Requires Docker.

Your own laptop or school LAN:

./scripts/generate-docker-secrets.sh   # creates .env with random secrets (needs openssl)
docker-compose up -d --build

Open http://localhost:8080. To make it accessible to other teachers on the network, set SITE_URL=http://<your-ip>:8080 in .env first.

docker-compose.yml no longer ships default secrets: it refuses to start until POSTGRES_PASSWORD, JWT_SECRET, ANON_KEY, SERVICE_ROLE_KEY and JWT_JWKS are set. Upgrading an existing installation: keep your current .env and run the script once. It only fills values that are empty (for an old .env that is JWT_JWKS, derived from your existing JWT_SECRET) and never changes the rest. If your .env still contains the old public demo values the script warns about it; rotating them on an existing database needs extra steps (the Postgres roles keep the password they were created with), so do not expose such an installation to the internet. POSTGRES_PASSWORD is applied to every database role (including the auth, REST and storage roles, via docker/db/roles.sql) only when the database volume is first created. To change it later — or to repair an older installation whose auth/REST/storage services cannot log in — update .env, then run docker compose up -d db, docker compose exec -e PGHOST=127.0.0.1 db psql -U supabase_admin -d postgres -f /docker-entrypoint-initdb.d/init-scripts/99-roles.sql and docker compose up -d --force-recreate.

VPS with a domain name (HTTPS):

./scripts/generate-docker-secrets.sh   # random JWT_SECRET, POSTGRES_PASSWORD and API keys
# Edit .env:
#   DOMAIN=rubricmaker.school.nl
#   SITE_URL=https://rubricmaker.school.nl
docker-compose --profile https up -d --build

Caddy obtains a free Let's Encrypt certificate automatically. Open ports 80 and 443 on your firewall.

Enabling email login (OTP):

Without SMTP, teachers log in anonymously. To allow email-linked accounts:

# In .env:
MAILER_AUTOCONFIRM=false
SMTP_HOST=smtp.office365.com   # or smtp.gmail.com, smtp-relay.brevo.com
SMTP_USER=rubricmaker@school.nl
SMTP_PASS=your-app-password

docker-compose up -d --force-recreate auth

Teachers receive an 8-digit sign-in code by email. The bundled GoTrue config sends code-only Dutch + English templates (public/email-templates/, one per GoTrue email type) with no clickable confirmation link — some email security scanners (e.g. Microsoft Safe Links) automatically open links in incoming mail, which would consume the one-time token before the teacher can enter the code, causing "Token has expired or is invalid" errors. On the official Supabase stack, use deploy/supabase/docker-compose.override.yml — see docs/SELF_HOSTING_OPS.md → Email.

Student login without email: many schools' spam filters block or delay Supabase's default OTP sender, leaving students unable to sign in. As an alternative, a teacher can generate a password for any student with an email on file (Students page → key icon on that student's row) and share it with them directly — the student then signs in at the landing page with "Student login (password)". This depends on the set-student-password edge function, which requires a functions runtime in front of an API gateway — this repo's own docker-compose.yml does not include one, so this feature (and the DB-backed essay submission flow, which relies on submit-essay/get-essay-assignment the same way) only works when Supabase is provided by the official self-hosted Supabase Docker stack (which ships a functions container behind Kong) or Supabase Cloud. On the official stack, deploy by copying the function's index.ts straight into that stack's volumes/functions/set-student-password/index.ts — there's no separate "deploy" step; the edge-runtime serves it as soon as the file is in place.

Backup and restore:

./scripts/backup.sh              # saves to ./backups/YYYYMMDD_HHMMSS/
./scripts/restore.sh backups/20260515_120000

Backups hold data only; the restore target must be a running stack migrated to at least the same version (docker-compose up -d runs the migrations). The restore runs in one transaction and stops on the first error. See docs/SELF_HOSTING_OPS.md.

Off-site backups and restore drills: a backup on the same server as the database is lost with it. Configure .env.backup (from .env.backup.example) with any S3-compatible bucket at a different provider (EU: Hetzner, Scaleway, OVHcloud, IONOS; or Cloudflare R2, Backblaze B2, Wasabi, …), then schedule ./scripts/scheduled-backup.sh nightly: it runs backup.sh, uploads the result encrypted on the server (rclone crypt, so the provider never sees student data), verifies the upload, prunes old copies and pings an optional heartbeat URL. ./scripts/restore-drill.sh (monthly) restores the newest off-site copy into a throwaway Compose project and checks every table's row count and every uploaded file against the dump. ./scripts/offsite-backup.sh pull latest fetches a copy for a real restore. See docs/SELF_HOSTING_OPS.md.

Moving from Supabase Cloud: ./scripts/export-cloud.sh (on your laptop) dumps users, rows and every Storage file; ./scripts/import-cloud.sh <export-dir> (on the server) loads them, compares file counts per bucket, and checks that each attachment, export template, essay, recording and scan row finds its file. See docs/SELF_HOSTING_OPS.md.

Updating to a new version:

git pull
docker-compose up -d --build    # rebuilds the app image, restarts services
# Migrations run automatically on next startup

Option B: Static hosting (offline mode only)

No database sync — all data stays in the browser. Works on any static host.

Build:

npm run build   # output in dist/

Deploy the dist/ folder to GitHub Pages, Vercel, Netlify, or any web server.

SharePoint:

  1. Run npm run build
  2. In dist/, rename index.html → index.aspx
  3. Upload the entire dist/ folder to a SharePoint Document Library
  4. Click index.aspx to launch

For Standards Integration on SharePoint, add the SharePoint domain to your Common Standards Project API key's allowed origins.

Operational guides

The deep operational detail is in the self-hosting docs. Highlights:

  • Nightly attachment cleanup (recommended): attachment files and their DB rows are deleted automatically when they age past the owner's school retention period (default: 7 years). Handwriting scans are removed by the same job once they are older than the current academic year. Schedule the bundled script with crontab -e: 0 2 * * * cd /path/to/rubricmaker && ./scripts/delete-old-attachments.sh >> /var/log/rubricmaker-cleanup.log 2>&1 || logger -t rubricmaker-cleanup 'attachment cleanup FAILED - see /var/log/rubricmaker-cleanup.log'. The script uses the Storage HTTP API (it does not delete rows directly from storage.objects, which Supabase blocks) and treats a 404 from storage as success so orphaned DB rows are always removed. It reaches storage through SITE_URL (default http://localhost:8080, the compose stack's port) and exits non-zero when any storage delete fails. Because the example sends its output to a log, cron itself mails nothing — the || logger alert is what surfaces a failed run (drop the redirect instead if you would rather have cron mail the output). It validates every attachment id and path before using it, so keep your copy of the script up to date. On Supabase Cloud, schedule the delete-old-attachments edge function instead (Supabase Dashboard → Edge Functions).
  • Nightly cloud backup (recommended for Supabase Cloud / the official self-hosted stack): scripts/backup.sh (and the off-site scripts/scheduled-backup.sh built on it) only works against this repo's own docker-compose.yml (it runs pg_dump inside the Compose db service and derives the uploads volume from the Compose project name, refusing to run if that volume does not exist). For Cloud or a separately self-hosted Supabase, use the nightly-backup edge function instead: it dumps each teacher/admin's rows via public.export_owner_backup() and uploads a JSON snapshot to the private backups Storage bucket (7 most recent per user), scheduled via Supabase Dashboard → Edge Functions or a pg_cron + pg_net job. Note this is a disaster-recovery snapshot of raw table rows — restoring means re-inserting the rows directly (e.g. via psql), not importing through the app's own Settings → Backup & Restore; and it's metadata only, so back up the essays/recordings/attachments buckets separately if you need the files themselves recoverable. Its snapshots also stay in the same Storage as the database, so they are not an off-site copy.
  • Teacher email digest (optional, pg_cron-driven): the scheduled-digest edge function emails opted-in teachers nightly about pending moderation disputes, overdue grading, or unread student messages (same three sources as the in-app Notification Center). It needs a functions runtime plus one-time per-deployment settings (app.settings.project_url / app.settings.service_role_key via ALTER DATABASE …) before it can send anything — see docs/SELF_HOSTING_OPS.md for the full pg_cron/pg_net setup.
  • Stress-test logging (optional): set VITE_STRESS_TEST_LOGGING=true in .env (requires migrations 035_client_logs.sql and 072_client_logs_observability.sql, included with db:reset/docker-compose db_migrate) to log user actions, sync results, JS errors, pageviews and Web Vitals (LCP/FCP/FID/CLS + page load) to a client_logs table — never free-text content. Unset and rebuild after the stress-test window.
  • Observability (optional): a standalone Loki + Prometheus + Alloy + Grafana stack (docker-compose -f docker-compose.observability.yml --env-file .env.observability up -d, Grafana on http://localhost:3001) filters web-server and container logs, collects host, per-container and Postgres metrics (disk, memory, connections, cache hit ratio), can query client_logs and the whole Supabase schema via a Postgres datasource, and alerts by email (or a chat webhook) on a filling disk, exhausted Postgres connections, failed scheduled jobs and 5xx bursts. Scheduled jobs also ping optional dead-man's-switch heartbeat URLs, and an external uptime check covers the server itself; see Grafana dashboards → Alerting. Set GRAFANA_ADMIN_PASSWORD to a strong value in .env.observability — the stack refuses to start without it (no default credentials). See Observability on a HestiaCP subdomain and Grafana dashboards for remote HTTPS access and what each dashboard shows.
  • Backend load testing (optional): the k6 harness (npm run loadtest, see k6/README.md) drives configurable virtual-user load at the Supabase backend. Run it against a local stack for regression smoke, or against a VM sized to a school's server to find its concurrency ceiling — docs/LOAD_TESTING_STAGING.md covers the staging-VM workflow and per-school sizing.

Developer reference

🧑‍💻 This section is for contributors — teachers and admins can stop here.

Routes

Path Page
/ Dashboard
/rubrics Rubric list
/rubrics/new New rubric
/rubrics/:id Rubric builder
/rubrics/:rubricId/grade/:studentId Grade a student
/rubrics/:rubricId/peer-review/:studentId Peer review view
/peer-analytics/:rubricId Peer review analytics (consistency, feedback heatmap, reviewer trends)
/rubrics/:rubricId/self-assess/:studentId Student self-assessment
/essays Essay list
/essays/new New essay
/essays/:teacherKey Essay builder (prompt, rubric link, assign students, import submissions)
/essays/:assignmentId/monitor Live essay monitor (presence, live word counts, draft preview)
/speaking/:rubricId/:studentId Speaking session
/grade-comparative/:classId/:rubricId Comparative grading
/marketplace School marketplace for rubrics, tests, flashcard decks, and question bank items (browse, publish, clone, upvote)
/tests Test list
/tests/new New test
/tests/:id Test builder
/tests/:testId/results/:studentTestId Test results, manual grading, and class-average adjustment
/tests/:testId/monitor Live test monitor (presence, response grid, proctoring flags)
/tests/:id/scores Score sheet: type in points per question per student, per-test grading norm, and test quality analysis (P-value, Rit/Rir, Cronbach's α)
/students Students list
/students/:id Student profile
/students/:id/cefr-overview Per-student CEFR overview
/students/:id/learning-path Per-student learning path — rule-based rubric recommendations and intervention flags
/cefr-overview Whole-class CEFR overview
/vocabulary Vocabulary Insights hub (CEFR vocabulary distribution per class/student, cross-deck flashcard progress, pasted/uploaded text screening, CSV export)
/flashcards Flashcard deck list
/flashcards/:id Flashcard deck editor — cards, CSV/XLSX/DOCX import, class assignment, per-student insights
/news-flashes News flashes — curate articles/books/videos to share with students
/portal/:studentId Student portal (public) — grades, to-do list, self-assessment, pending co-grading moderation notices, learning-path/grammar recommendations
/portal/:studentId/flashcards/:deckId Student flashcard study session (spaced repetition)
/feedback/:code Student feedback view (public, no login — decodes a shared grade-summary link)
/preview/:code Rubric preview (public, no login — decodes a shared rubric link, no student data)
/essay/:code Essay writing session (public, no login — decodes a shared essay-assignment link)
/test/:code Take a test (public, no login — answer questions, optional timer, submit)
/attachments Attachment manager
/comments Comment bank
/question-bank Question bank — reusable test questions, insertable into any test
/statistics Statistics dashboard (by-rubric, by-student, multi-class compare with insights, advanced query builder)
/activity-dashboard Activity Dashboard — rubric/test/essay × class grid with link/assign/reorder actions, pending grading-task list
/moderation Moderation queue — disputed co-graded submissions, per-criterion delta, keep/accept resolution
/messages Messages inbox — reply to or start a thread with a portal-authenticated student
/notifications Notification Center — full, filterable history of overdue grading, unread messages, and pending moderation reviews, with cross-device dismissals
/export Export page
/settings Settings
/admin Admin panel (admin role only)
/privacy Privacy statement

Key utility modules

File Purpose
src/utils/gradeCalc.ts Score aggregation and weighted scoring engine
src/utils/gradeSnapshot.ts Grade-scale comparison and carrying a grade's entries over to a newer rubric version (regrade on latest)
src/utils/subItems.ts Sub-item rules shared by scoring, grading and exports: points vs checklist mode, ranges and legacy ticks, the score breakdown, best-fit level from ticks, list pasting
src/utils/tieredRubric.ts Tiered rubrics (one column per school year/track, shared bands as levels): band presets and verdict cutoffs, validation, deriving levels from bands, a student's default tier, the final verdict with requirement caps and overrides, and checks on imported configs
src/utils/tieredSheet.ts A tiered rubric as its paper sheet (requirements, tier columns, bands with statements; a grade's tier, ticks, chosen bands and verdict), rendered by tieredSheetHtml.ts (PDF/print), tieredSheetDocx.ts (DOCX) and TieredSheetView (previews)
src/utils/import/sheetImport.ts Recognises tiered rubric sheets in DOCX (mammoth HTML, merged cells resolved) and PDF (text positions): tier columns, band codes, statements, requirements, criterion names, track/year mapping and broken-ligature repair
src/utils/rubricWeights.ts Even criterion-weight split for the Rubric Builder, and the check that keeps an untouched split at 100% as criteria are added or removed
src/utils/csvDownload.ts Shared CSV download (UTF-8 BOM so Excel keeps accented and non-Latin names), ISO YYYY-MM-DD dates, and collision-free column labels for repeated or blank criterion titles
src/utils/studentValidation.ts Add-student checks (loose email format, same name already in the class) and the students CSV export rows (name, email, class, student number)
src/utils/settingsDefaults.ts DEFAULT_SETTINGS and withSettingsDefaults, which fills fields an older backup or cloud settings row lacks (including inside defaultFormat) wherever settings enter state
src/utils/rubricClone.ts Copies a rubric for Duplicate/Differentiate: fresh nested ids (vocabulary links remapped), no list position, sharing or attachments
src/utils/testClone.ts Copies a test for Duplicate: fresh question/option ids, no list position or sharing
src/utils/materialSharing.ts Resolves a rubric/test/folder's effective sharing (own → folder chain → teacher default → private, mirroring migration 095) and groups colleagues' shared material per teacher
src/utils/hydratedGradeMerge.ts Lays edits made before a saved grade finished loading over that saved record, so untouched scores survive
src/utils/attachmentLimits.ts Per-file attachment size limits (2 MB in local mode, 50 MB with a database) and file-size formatting
src/utils/comparativeMatchups.ts Comparative-grading pairing logic: per-student matchup counts and next-pair selection against the persisted, per-rubric matchup history (Rubric.comparativeMatchupLimit)
src/utils/folders.ts Folder helpers for all four lists: ALL_FOLDERS, MAX_FOLDER_DEPTH, ordered folders per level (childFolders), paths (folderPath/folderPathLabel, folderDepth), descendantFolderIds, countInFolderTree, canNestFolder, nextFolderOrder per parent
src/utils/cefrStudentAggregator.ts CEFR level computation across assessments
src/utils/cefrVocabularyProfiler.ts CEFR vocabulary profiler over a validated 64k-word index (src/data/cefrLevels.ts): level distribution, estimated level, off-list share, academic coverage; builds/reads the persisted per-document profile
src/utils/academicWordList.ts Academic Word List (AWL) + NAWL membership and coverage over a token list
src/utils/flashcardOverview.ts Cross-deck flashcard progress for the Vocabulary Insights hub: sums computeDeckInsights over each deck's assigned students
src/utils/textLevelVerdict.ts Target-level "right for my class?" verdict: coverage %, above-target words, and a suitable/slightly-above/too-hard label, from a text or an aggregated distribution
src/utils/vocabProfileAggregator.ts Per-student and per-class CEFR vocabulary aggregation (level distribution, off-list and AWL/NAWL shares) and the vocabulary-list CSV export
src/utils/placementRouting.ts Section-routing engine for staged placement tests (threshold scoring, next-section resolution, cycle detection)
src/utils/placementResult.ts Deterministic provisional CEFR estimate from a completed placement test's section path
src/utils/placementStaircase.ts Staircase (adaptive-ladder) placement engine: level progression, next-question resolution, and item-level Elo self-calibration/rating bands per CEFR level
src/utils/placementGenerator.ts Types/predicate for the Generator placement engine, which pulls questions live from the question bank at runtime via the next-placement-question edge function instead of a pre-authored pool
src/utils/eloProgressAggregator.ts Builds a student's Elo-scaled CEFR progression across placement-test attempts, for the Statistics placement-progress chart
src/utils/learningGoalsAggregator.ts Learning goal progress tracking
src/utils/docxExport.ts DOCX generation via docx library
src/utils/docxTemplateExport.ts Blank-rubric Word export styled by a rubric table template (level headers + header shading read from the template's XML)
src/utils/exportDataPrep.ts Shared filename sanitization, points-range formatting, and HTML-comment stripping used by the DOCX/PDF export paths
src/utils/docxStyleTemplate.ts Extracts heading/body font from an uploaded .docx for essay/period-report style templates
src/utils/pdfExport.ts PDF report generation
src/utils/testExamContent.ts Format-agnostic content/geometry for the CITO-style exam export: section grouping/numbering, partial-credit ladder derivation, hot-text passage rendering, and answer-sheet bubble-grid/fiducial-marker geometry
src/utils/testExamExportHtml.ts / testExamExportDocx.ts Renders the exam booklet, attachment, answer sheet, and grading sheet — and the test builder's live A4 preview (buildTestPreviewSections/buildTestPreviewHtml) — as PDF (print-to-PDF HTML) and DOCX, from testExamContent.ts
src/utils/textExtraction.ts OCR (Tesseract) + DOCX parsing (Mammoth); also DOCX→HTML conversion for the read-only grading document view, and recognizeImage (rich OCR result: text + per-word confidence) for the handwriting-scan flow
src/utils/preprocessScan.ts Pure RGBA scan-preprocessing primitives (grayscale, contrast stretch, Otsu threshold, projection-profile deskew, bilinear resize/upscale)
src/utils/documentCrop.ts Corner ordering and bilinear perspective-unwarp for cropping a document quad out of a scan photo
src/utils/scanImport.ts Turns picked photos / scanned PDFs into OCR-ready image blobs (pdf.js page rasterization with memory guardrails)
src/utils/ocrConfig.ts / ocrLanguage.ts Tesseract engine config (OEM/PSM from a capture hint, user-words/patterns) and UI-locale→Tesseract language routing
src/utils/ocrReview.ts Per-word low-confidence annotation and confidence banding for the scan review UI
src/utils/scanRecord.ts Builds the persisted Scan record on review confirm and the discard-after-OCR decision
src/utils/scanRetention.ts One-academic-year retention: finds and sweeps scans past the cap (client counterpart to the server get_overdue_scans())
src/services/scanStore.ts Scan image blobs in the shared rm_media IndexedDB store, namespaced by a scan_ id prefix
src/services/database/ScanSync.ts Cloud sync for scans (blob upload + metadata upsert to the scans bucket / scan_metadata table, signed-URL read), mirroring RecordingSync
src/utils/examScanRegions.ts Maps a CITO exam answer sheet's answerSheetGeometry() mm boxes onto scanned-page pixel regions, and detects marked multiple-choice/true-false bubbles by ink coverage (OMR, not OCR)
src/utils/examScanAnswers.ts Reads one printed page of a scanned answer sheet into draft TestAnswers (bubble detection or per-region OCR) and merges a student's pages in printed question order
src/utils/examScanBatch.ts Batch page→student grouping, unassigned-page detection and scanned-vs-printed page-count checks
src/utils/examScanQr.ts Decodes and validates the QR in an answer sheet's scan-marker band (test, student, printed page count) for automatic page assignment
src/utils/examScanStudentMatch.ts OCR name-region fallback that matches a printed candidate name against the roster when no QR is readable
src/utils/examScanVocabulary.ts Seeds the OCR user_words dictionary from a test's short-answer model answers
src/utils/rgbaImageCanvas.ts Canvas glue between a captured photo (Blob/data URL) and the pure RgbaImage pixel pipeline
src/utils/essayShareCode.ts Shareable codes for essay access (no auth needed)
src/utils/pinHash.ts PIN hashing for student self-assessment locks
src/utils/clozeParse.ts Cloze/hot-text rendering helpers; re-exports the gap and fragment parsers from supabase/functions/_shared/testScoring.ts
src/utils/questionTypeKey.ts i18n keys for a question type's label and help popover (every hyphen in the type becomes _)
src/utils/userRole.ts resolveUserRole: the one role the Settings badge, sidebar and route guards act on (legacy user → teacher, unset → local owner/admin)
src/hooks/useActiveClassCohorts.ts Cohort-chip selection kept two-way in sync with the topbar class selector (settings.activeClassId; one class or unset = all)
src/utils/pendingGrading.ts Overdue grading: handed-in tests and essays still ungraded past the threshold, grouped per test/essay (mirrored server-side by get_overdue_grading_count, migration 086)
src/utils/clozeGenerators.ts Deterministic cloze-gap generators for the test builder: fixed-ratio, C-test, part-of-speech (compromise), above-CEFR-level and academic (AWL/NAWL) vocabulary
src/components/Tests/LimitedAudio.tsx Listening stimulus player with a play limit (file or browser voice) and the per-option audio button
src/utils/rubricTaskScoring.ts Maps a rubric-scored test answer's per-criterion entries onto the question's points (reuses gradeCalc)
src/utils/wordFormation.ts Word-formation authoring aid: derived forms of a stem word (affix candidates that exist in the CEFR index plus compromise inflections) and per-gap stem parsing
src/utils/distractorSuggestions.ts Deterministic distractor suggestions (compromise word forms, EFL confusables, same-level words from the CEFR index) for the test editor
src/utils/deckToQuestions.ts Generates matching/multiple-choice/cloze/categorize test questions from a FlashcardDeck's cards
supabase/functions/_shared/testScoring.ts Single implementation of test auto-scoring and cloze/hot-text parsing, shared by the client (testCalc.ts) and the submit-test / next-placement-question edge functions
supabase/functions/_shared/placement*.ts Single implementation of MST routing and the staircase/generator CEFR ladder with Elo calibration (plus seededShuffle.ts), shared by the client and submit-test / next-placement-question
supabase/functions/_shared/attachmentPurgeGuard.ts Shared eligibility rule for the attachment retention purge (plain ids, file in the owner's own folder); mirrored by scripts/delete-old-attachments.sh
supabase/functions/_shared/studentPasswordGuard.ts / secureCompare.ts Caller/target authorization for set-student-password, and the constant-time secret comparison used by the cron-style functions
supabase/functions/_shared/smtp.ts / notificationEmails.ts Dependency-free SMTP client (STARTTLS/implicit TLS, AUTH PLAIN/LOGIN) and the Dutch + English grade, message and digest emails; _deno/mail.ts connects them to Deno sockets and the SMTP_* / APP_URL env
src/utils/audioResponseCode.ts Encodes/parses the data-URI JSON carried in an audio-response question's TestAnswer.response
src/utils/testQuestionClone.ts Regenerates a test question's id and every nested option/pair/item id, and instantiates a fresh copy of a Question Bank item (single question or reading/listening section bundle) ready to insert into a test
src/utils/testQuestionIssues.ts What keeps a test question from being answered or scored (missing options, correct answer, gaps, model answer, ...) per question type, and whether a test can be assigned; drives the test builder issue list and the Assign block
src/utils/masteryProfileAggregator.ts Merges per-student test accuracy, flashcard FSRS state, and graded-writing scores into one row per grammar item, plus a blended 0–100 grammar mastery score for the Dashboard's Student Snapshot
src/utils/masteryColorScale.ts Grammar-mastery cell color for the Dashboard Student Snapshot: teacher-configured Settings.masteryColorBands, falling back to CriterionHeatmap's red→yellow→green pctToColor
src/utils/learningPathAggregator.ts Rule-based rubric recommendations, intervention flagging, and grammar practice recommendations
src/utils/grammarLanguage.ts Picks the LanguageTool language for a document: detects English/Dutch/French/German/Spanish from function words, and uses the teacher's English spelling variant for English
src/utils/writingTrendAggregator.ts Rule-based weak/declining rubric-criterion trends across a student’s graded essays, per student and per class
src/utils/essayTextStats.ts Deterministic essay statistics (sentence-length variance, transition words, Flesch-Kincaid), mirrored from vocabkitchen-CLI analysis.py; parity cases in sync/writing-fixtures.json
src/utils/grammarProfile.ts CEFR-J grammar-construction detectors over a UD parse, ported from vocabkitchen-CLI grammar_profile.py; registry in src/data/grammarConstructions.ts
src/utils/udParse.ts CoNLL-U reader and lazy udpipe-wasm loader (model hosted separately)
src/utils/testSummaryAggregator.ts Per-question/per-skill strong-weak test breakdown
src/utils/testGradingNorm.ts Per-test grading norms: points → grade via grade scale, N-term (CvTE), caesura or per-year/track norm table (resolveTestGrade)
src/utils/testQualityMetrics.ts Classical test-theory metrics: P-value, Rit/Rir, Cronbach's α (KR-20), SEM, α-if-deleted
src/utils/scoreSheet.ts Score-sheet cell edits, paste parsing, absent marking and CSV export
src/utils/reportCardAggregator.ts Composes CEFR, learning-goals, and test-summary data into one report card
src/utils/globalSearch.ts Token-aware search (type:/class:/year:/track: filters, student+rubric grading shortcut) across rubrics, tests, students, classes, essays, flashcard decks, and news flashes
src/utils/portalSearch.ts Student-portal search over a student's own graded rubrics, work (tests/essays), and flashcard decks
src/utils/statsChartPresets.ts Recommended chart definitions for the Statistics "Custom Views" gallery
src/utils/assessmentTimeline.ts Merges rubric grades with graded assessment-mode test results for progress trends (class, class comparison, student timeline)
src/utils/statsQuery.ts Advanced Statistics query engine: flattens every grade into records, then filter → group → aggregate (avg, median, pass rate, spread, change)
src/utils/statsSections.ts Catalogue of Statistics cards the teacher can show/hide in the Customise panel
src/utils/liveStudentRubrics.ts One live grade per student and rubric for analytics: drops soft-deleted grades, archived students, deleted rubrics and duplicate records; scopes a rubric's grades to a class
src/utils/coGradingModerationQueue.ts Flags disputed co-graded submissions (delta above threshold) for the Moderation queue
src/utils/flashcardScheduler.ts Thin wrapper around ts-fsrs (FSRS spaced repetition): rating, study queue, interval preview
src/utils/flashcardImport.ts Flashcard import from CSV (papaparse), XLSX (read-excel-file), DOCX (mammoth), and plain text
src/utils/questionBankImport.ts Bulk Question Bank import from a JSON file (plain questions or section bundles, all TestQuestionTypes, CEFR/grammar/standard links, tags, optional Elo rating)
src/utils/testImport.ts Test import from a RubricMaker test JSON file (single test, list, or backup export): fresh ids for sections and questions, per-question validation warnings, and the points, norm and print layout of the file kept as-is
src/utils/flashcardInsights.ts Learner insights per deck: stage counts, due cards, focus words from FSRS state
src/utils/displayOrder.ts Shared sort/reorder helpers for manually-orderable list views
src/utils/cohortAggregator.ts Derives a cohort's student set from current + past class memberships by year/track
src/utils/gradebookExportPresets.ts Per-SIS CSV column presets (Magister, SOMtoday) for the gradebook export
src/utils/testExportPresets.ts Whole-class test results CSV (score, points, grade, status, per-question and per-skill accuracy) and Magister/SOMtoday gradebook rows
src/utils/studentTestScore.ts The score TestResultsPage shows for one submission (path-aware for staged, staircase and generator placement runs), shared with the test exports
src/utils/icsExport.ts Builds a minimal .ics calendar file from assignment deadlines
src/utils/messageThreads.ts Groups flat student/teacher Message rows into threads by student + context
src/utils/eraseStudent.ts Right to erasure: removes a student and every record keyed to them from local state (the client side of erase_student(), migration 084)
src/utils/backupSettings.ts Settings → Backup: keeps role, account email, admin PIN, school and API keys out of exports and restores, and diffs the settings a restore would change
src/utils/roleChangeError.ts Recognises the last-admin error raised by protect_role_changes() so Admin → Users and Onboarding can show a translated message
src/services/standardsApi.ts Common Standards Project API (CCSS, NGSS)
src/services/wordnetPack.ts Optional offline definitions: downloads the Open English WordNet pack (public/wordnet/, built by scripts/build-wordnet-pack.mjs, CC BY 4.0) after confirmation and stores it in IndexedDB
src/services/wordLookup.ts Definition lookup chain (Free Dictionary → offline WordNet) plus keyless MyMemory word translation, used to fill decks seeded from Text screening
src/services/freeDictionaryApi.ts Keyless word lookup (definition, phonetic, part of speech) via the Free Dictionary API (dictionaryapi.dev) for the vocabulary panel

Contributing & security

License: MIT — free to use, modify and distribute, with attribution. See the LICENSE file for details. Bundled and downloadable word-list data keeps its own licences (notably the NAWL, CC BY-SA 4.0, and Open English WordNet, CC BY 4.0) — see docs/VOCAB_WORDLISTS_PROVENANCE.md.

About

Rubric maker for use in education. Geared towards use in EFL Classrooms

Topics

Resources

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages