1. ποΈ Architecture
flowchart LR DS["OpenTDB API"] -->|"import: base64 decode + map"| CPT["qw_quiz / qw_question CPTs"] CPT -->|"GET /quizwizz/v1/quiz/{id} (with answers)"| PLAY["Alpine player in browser"] PLAY -->|"local grading"| PLAY PLAY -->|"window.print()"| PRINT["printable sheet + key"]
flowchart TD DS[DATASETS] --> CPT[CPT quiz+question] CPT --> REST[REST] REST --> AP[Alpine player browser] AP --> PS[print / share] BT[batch translate] --> CPT CW[π§ chat wizard NLβdraft] --> CPT OR[OpenRouter server-side, key hidden] --> CW API[Abilities API + Data Views] -.-> BT API -.-> CW
Plain: facts live in WordPress β browser plays them β AI only fills the two arrows (translate, wizard).
2. ποΈ Data Model
| π¦ Entity | Kind | Key fields |
|---|---|---|
quiz | CPT | title, mode(practice/timed/shuffle), form(A/B/C), lang, question refs |
question | CPT | stem, type, choices[], correct_idx, explanation, source |
| taxonomies | tax | subject Β· difficulty Β· language Β· tag |
ποΈ External Datasets & Scale Analysis
QuizWizz is designed to scale beyond the bundled OpenTDB pool by leveraging two massive academic and trivia benchmarks for future expansion:
π Global-MMLU (The Academic Benchmark)
- Scope & Size: ~14,000 highly-curated samples spanning 42 languages and 57 academic subjects (physics, law, history, etc.).
- Time-Relevance: Highly static. Because it tests universal academic concepts, the factual validity does not decay over time.
- Storage Footprint: Micro (~30 MB). The dataset is purely structured text (stem, choices, correct index), making it incredibly lightweight to store and query.
- Scrapeability/Access: Trivial. It is officially hosted and maintained on Hugging Face (
CohereLabs/Global-MMLU), bypassing any need for complex web scraping.
π§© TriviaQA (The Heavyweight Open-QA Standard)
- Scope & Size: ~95,000 base Q&A pairs authored by trivia enthusiasts. This scales to ~650,000 triples when combined with its reading comprehension evidence documents.
- Time-Relevance: Semi-static. While most historical/factual trivia remains accurate, questions regarding βcurrentβ events from its 2017 release date experience slow decay.
- Storage Footprint: The Q&A pairs alone are lightweight (~150 MB). However, the full context corpus (including Wikipedia/web evidence docs) expands from a 2.6 GB compressed download to ~18 GB uncompressed on disk.
- Scrapeability/Access: Trivial. Natively integrated into Hugging Face (
mandarjoshi/trivia_qa) with cleantrain,validation, andtestsplits.
3. π£οΈ Routes
| Method | Endpoint | π Auth | Does |
|---|---|---|---|
| GET | /models | logged-in | OpenRouter list, cached 1h β picker |
| POST | /generate | edit_posts + rate-limit | π§ NL β quiz draft (SSE stream) |
| POST | /translate | manage_options | batch translate via WP-Cron |
| GET | /quiz/{id} | public | quiz JSON for play |
| GET | /wp/v2/settings | manage_options | React settings (core) |
4. π¨ UI/UX
| π₯οΈ Surface | What | Built with |
|---|---|---|
| Admin list | filter/sort quizzes & questions | Data Views |
| Settings | API key Β· default model+prompt Β· languages | React + /wp/v2/settings |
| Frontend | quiz block β card Β· progress Β· timer Β· results | Block v3 + Alpine |
| π§ Wizard | floating bubble (β) β model+prompt dropdowns β NL box β insert draft | Alpine + /generate |
| Finish | Print + Share buttons | native APIs |
5. π OpenRouter
| Knob | Setting |
|---|---|
| Picker | GET /api/v1/models?order=newest β cache 1h β dropdown (model & prompt user-selectable) |
| Chat/Translate | OpenAI-compatible /chat/completions, stream:true (+ non-stream fallback) |
| Reliability | response_format=json_schema β clean question objects |
| Cost | :floor variant Β· max_tokens cap Β· BYOK Β· provider spend-limit |
| π Key | server-side only (wp-config/env) β never client JS |
6. π Security
cap_check β nonce β sanitize β ESCAPE Β· $wpdb->prepare('%s %d %i') Β· defined('ABSPATH')||exit Β· sanitize HTML before mPDF Β· 2FA on wp.org acct.
7. π Builder Checklists
QuizWizz Builder π§ β the config that makes a quiz: category Γ count Γ difficulty Γ mode{π₯οΈonline|π¨οΈprint}. Checklist below is priority-tagged: β
MVP Β· π Phase-2 Β· π§ YAGNI-cut (donβt build until a real story forces it).
1. β Builder Form
Acceptance: pick category(ies) + count + difficulty + mode β a valid, buildable quiz, every time.
| β | Feature | Pri | Must behave like |
|---|---|---|---|
| β | Category select (multi + βAll/Randomβ) | β | shows live count per category |
| β | Count stepper/input + presets (5/10/20) | β | clamped to available pool |
| β | Difficulty easy/med/hard/mixed | β | mixed = even split |
| β | Mode toggle π₯οΈ/π¨οΈ | β | switches output path |
| β | Language picker (enabled langs only) | β | drives translated bank |
| β | Live availability counter | β | β32 questions matchβ |
| β | Validation + disabled CTA + empty-match msg | β | βloosen filtersβ hint |
| β | Loading/skeleton on generate | β | no dead clicks |
| β | Keyboard + ARIA + mobile | β | fully operable |
| β | Shareable URL state ?cat&count&diff&mode&lang | π | |
| β | Remember last config Β· Timer setting Β· Form A/B/C selector | π | |
| β | βSurprise meβ Β· skins Β· per-question timer Β· drag-reorder | π§ |
2. π₯οΈ Online Play
| β | Feature | Pri |
|---|---|---|
| β | Render Q cards (MCQ/T-F) + shuffle Qs & options | β |
| β | Progress X/N Β· select+lock answer | β |
| β | Results: score Β· % Β· per-Q review + explanation | β |
| β | Retry / new quiz Β· Print results Β· Share (Web Share API) | β |
| β | Timer Β· save attempt (logged-in) Β· immediate-vs-end feedback toggle | π |
| β | Leaderboards Β· streaks Β· achievements Β· multiplayer | π§ |
3. π¨οΈ Print Mode
| β | Feature | Pri |
|---|---|---|
| β | Clean question sheet (no site chrome) | β |
| β | Separate answer key (from explanation field) | β |
| β | Header: title + name/date blanks + instructions | β |
| β | @media print/@page: page-breaks + repeat headers | β |
| β | window.print() path | β |
| β | mPDF download (saved/email/non-Latin) Β· Form A/B/C shuffle Β· QR version-id Β· bubble sheet | π |
| β | OMR auto-grade Β· multi-column Β· per-theme fonts | π§ |
4. βοΈ Admin Backend
| β | Feature | Pri |
|---|---|---|
| β | Question bank CRUD via native WP 7.0 Data Views extending edit.php?post_type=qw_question | β |
| β | Import OpenTDB (ready MCQ) + map categories via wp quizwizz import_sqlite CLI command | β |
| β | Choose which categories/difficulties/modes/languages are exposed in builder | β |
| β | Builder limits: default/max count, allowed presets, guest-vs-logged-in | β |
| β | Settings (React): OpenRouter key (server-side) + default model+prompt | β |
| β | Quality gate: review/approve imported Qs (kills bad data) | β |
| β | Batch translate (OpenRouter + WP-Cron / local SQLite fallback): per-lang status via qw_lang native column in DataViews Β· spot-check flag Β· cost estimate | β |
| β | π§ Wizard config: hourly model picker Β· rate limit Β· enable/disable | π |
| β | Attempts log + per-category stats (Data Views) + CSV export | π |
| β | TriviaQA/Global-MMLU import Β· dedup Β· bulk tag Β· print branding | π |
| β | A/B configs Β· full analytics dashboard Β· gamification admin | π§ |
5. π‘οΈ Security Checklists
β every route: cap β nonce β sanitize β escape Β· β clamp count to pool server-side (never trust client) Β· β cache pool-counts + model-list(1h) + built/translated sets Β· β i18n + a11y + mobile Β· β Plugin Check green before ship.
6. π· Media Assets
(Assets should be placed in projects/quizwizz/assets/ and embedded here as they are generated during development.)
8. π Frontend
The quiz builder and pre-print editor heavily leverage Alpine.js and SortableJS to provide a reactive, zero-build frontend without the overhead of React.
ποΈ Architecture
- Data Hydration: Initial quiz and question metadata are loaded via
wp_localize_script()directly into the globalqwCraftDataobject. This eliminates the need for initial AJAX fetching and prevents UI jitter on page load. - State Management: The
<div x-data="qwPdfEditor()">element acts as the single source of truth for the frontend state. It manages variables likeactiveTab,quizSearch,questionCount,pdfTheme, anddifficulty. - Progressive Enhancement: Forms initially render with hidden native
<input>fields bonded to Alpine models (x-model). When the form is submitted natively (viaadmin-post.php), the backend receives a standard POST payload, ensuring gracefully degraded operations. - Visual Pre-print Editor:
- When a user clicks βEdit/Rerollβ or views a draft, a REST API endpoint (
/wp-json/quizwizz/v1/craft-draft) validates the current selections and returns a JSON array ofquestions. - Alpine binds this array using
x-for, rendering interactive cards. - SortableJS is initialized via an
$nextTick()Alpine hook on the list container to enable drag-and-drop reordering. - Undo/Redo: The editor uses standard
pushState()logic maintaininghistoryandfuturestacks for deep-cloned question arrays.
- When a user clicks βEdit/Rerollβ or views a draft, a REST API endpoint (
π§Ή Codebase Standards
- No external CDNs. All frontend libraries (Alpine.js
3.15.12, SortableJS1.15.2) are packaged natively insideassets/js/. - Vanilla JS elimination: Event listeners like
DOMContentLoadedare stripped and replaced natively byx-initor standard Alpine directives (@click,@change). - CSS Utility Scaling: Extensive use of scoped
qw-prefixed utility classes (from Tailwind processed CSS) maintains a minimal stylesheet footprint without bleeding into the broader WordPress theme.