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

πŸ“¦ EntityKindKey fields
quizCPTtitle, mode(practice/timed/shuffle), form(A/B/C), lang, question refs
questionCPTstem, type, choices[], correct_idx, explanation, source
taxonomiestaxsubject Β· 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 clean train, validation, and test splits.

3. πŸ›£οΈ Routes

MethodEndpointπŸ” AuthDoes
GET/modelslogged-inOpenRouter list, cached 1h β†’ picker
POST/generateedit_posts + rate-limitπŸ§™ NL β†’ quiz draft (SSE stream)
POST/translatemanage_optionsbatch translate via WP-Cron
GET/quiz/{id}publicquiz JSON for play
GET/wp/v2/settingsmanage_optionsReact settings (core)

4. 🎨 UI/UX

πŸ–₯️ SurfaceWhatBuilt with
Admin listfilter/sort quizzes & questionsData Views
SettingsAPI key Β· default model+prompt Β· languagesReact + /wp/v2/settings
Frontendquiz block β†’ card Β· progress Β· timer Β· resultsBlock v3 + Alpine
πŸ§™ Wizardfloating bubble (β†˜) β†’ model+prompt dropdowns β†’ NL box β†’ insert draftAlpine + /generate
FinishPrint + Share buttonsnative APIs

5. πŸ”Œ OpenRouter

KnobSetting
PickerGET /api/v1/models?order=newest β†’ cache 1h β†’ dropdown (model & prompt user-selectable)
Chat/TranslateOpenAI-compatible /chat/completions, stream:true (+ non-stream fallback)
Reliabilityresponse_format=json_schema β†’ clean question objects
Cost:floor variant Β· max_tokens cap Β· BYOK Β· provider spend-limit
πŸ”‘ Keyserver-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.

☐FeaturePriMust 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

☐FeaturePri
☐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

☐FeaturePri
☐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

☐FeaturePri
☐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 global qwCraftData object. 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 like activeTab, quizSearch, questionCount, pdfTheme, and difficulty.
  • Progressive Enhancement: Forms initially render with hidden native <input> fields bonded to Alpine models (x-model). When the form is submitted natively (via admin-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 of questions.
    • 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 maintaining history and future stacks for deep-cloned question arrays.

🧹 Codebase Standards

  • No external CDNs. All frontend libraries (Alpine.js 3.15.12, SortableJS 1.15.2) are packaged natively inside assets/js/.
  • Vanilla JS elimination: Event listeners like DOMContentLoaded are stripped and replaced natively by x-init or 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.