A minimalist MCQ quiz web application
Project description
SnekQuiz ๐
A minimalist multiple-choice quiz web application built with FastAPI, HTMX, and Jinja2 templates.
[!NOTE] This project was vibe-coded with Claude Opus 4.6 ๐คโจ
Features
- Role-based auth โ admins and quiz-takers via HTTP Basic authentication
- Quiz catalogue โ home page lists all quizzes, highlighting completed vs new
- Paginated questions โ one question at a time with instant feedback via HTMX
- Results page โ score summary with per-question review (safe to refresh)
- Admin portal โ upload quizzes, view aggregated results, delete quizzes
- Quiz API โ
POST /api/quizzes(admin),GET /api/quizzes,DELETE /api/quizzes/{id}(admin) - SQLite storage โ lightweight, zero-config persistence via aiosqlite
- YAML config โ app settings parsed into Pydantic models, logging via
dictConfig
Quick Start
# Install dependencies (requires Python 3.13+ and uv)
uv sync
# Run the app
uv run snekquiz
# or run via the CLI entry-point
snekquiz
# or via python -m
python -m snekquiz
# or via uvicorn directly
uvicorn snekquiz:create_app --factory
The server starts at http://localhost:8000. Log in with one of the default
accounts configured in src/snekquiz/config/app.yaml:
Admins can also take quizzes. The admin portal is at http://localhost:8000/admin.
Uploading a Quiz
Via the admin portal at /admin/upload, or via the API:
curl -u admin:admin \
-H "Content-Type: application/json" \
-d @quiz.json \
http://localhost:8000/api/quizzes
See the example quiz format below.
Example quiz.json
{
"quiz_name": "Cities quiz",
"questions": [
{
"id": 1,
"question_type": "single_answer",
"question_text": "What is the capital of France?",
"options": [
{"id": "A", "text": "Paris"},
{"id": "B", "text": "London"},
{"id": "C", "text": "Berlin"},
{"id": "D", "text": "Madrid"}
],
"correct_answers": ["A"],
"explanation": "Paris is the capital and largest city of France."
},
{
"id": 2,
"question_type": "multiple_answer",
"question_text": "Which of the following are cities in Europe?",
"options": [
{"id": "A", "text": "Paris"},
{"id": "B", "text": "London"},
{"id": "C", "text": "Singapore"},
{"id": "D", "text": "Perth"}
],
"correct_answers": ["A", "B", "D"],
"explanation": "Paris (France), London (England), Perth (Scotland)."
}
]
}
Project Structure
src/snekquiz/
โโโ __init__.py # App factory & CLI entry-point
โโโ auth.py # Authentication including LDAP layer
โโโ database.py # SQLite database layer (aiosqlite)
โโโ models.py # Pydantic models (config + quiz data)
โโโ routes.py # Web & API routes
โโโ config/
โ โโโ app.yaml # Application settings
โ โโโ logging.yaml # Python logging dictConfig
โโโ templates/
โ โโโ base.html # Base layout
โ โโโ home.html # Quiz catalogue
โ โโโ complete.html # Quiz completion interstitial
โ โโโ question.html # Question page
โ โโโ results.html # Results / score page
โ โโโ admin/
โ โ โโโ dashboard.html # Admin overview with aggregated stats
โ โ โโโ quiz_detail.html # Per-quiz attempt drilldown
โ โ โโโ upload.html # Quiz upload form
โ โโโ partials/
โ โโโ answer_feedback.html # HTMX fragment for answer feedback
โโโ static/
โโโ style.css # Minimalist CSS theme
Configuration
App config (src/snekquiz/config/app.yaml) provides default config if no path is provided.
A user specified config file can supplied with:
- The
APP_CONFIGenvironment variable is read to load a config file, egexport APP_CONFIG=/path/to/config.yaml. - Providing
-cor--app-configarguments
Logging config (src/snekquiz/config/logging.yaml) provides default logging config.
A user specified config file can supplied with:
- The
LOG_CONFIGenvironment variable - The
-lor--log-configarguments
When running with uvicorn you must use the environment variable approach.
Development
# Lint & format
uv run ruff check --fix .
uv run ruff format .
# Type check
uv run ty check
# Pre-commit hooks (using prek)
prek install
prek run --all-files
Theme
Colours: #ffffff ยท #000000 ยท #002554 ยท #099d91 ยท #00cc99
License
See LICENSE.
Built with vibes and Claude Opus 4.6.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snekquiz-0.1.3.tar.gz.
File metadata
- Download URL: snekquiz-0.1.3.tar.gz
- Upload date:
- Size: 20.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a773d7ebf54f07f3faec50cb4e776574b52e9e03a7f26ae9fd6a7edc581a9548
|
|
| MD5 |
0316659cdd33e091bce1fefa10e201cd
|
|
| BLAKE2b-256 |
e831e646950069b82294e4316842758cafeb6e0c6f0cf23c3fa6a164e25cd88e
|
File details
Details for the file snekquiz-0.1.3-py3-none-any.whl.
File metadata
- Download URL: snekquiz-0.1.3-py3-none-any.whl
- Upload date:
- Size: 27.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea7bc8c24d4e7f37a9c3089b32acdf32b6065617026642c3f00eaa29f20a9da2
|
|
| MD5 |
c1bd846f1eee8f62547ede2c0d5b794c
|
|
| BLAKE2b-256 |
daf5a8070f95b0030ee5455248658cd49d12009c3b27624aea0386df75f8155b
|