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
- Simple auth โ HTTP Basic. Admins can do admin things, users can take quizzes
- Quiz list โ See what's available, what you've finished, what's left to do
- One question at a time โ HTMX gives you instant feedback without page reloads
- Results โ Your score, what you got wrong, what the right answers were
- Admin stuff โ Upload quizzes (JSON), see how everyone's doing, delete quizzes when needed
- API โ POST new quizzes, GET quiz lists, DELETE when you messed up
- SQLite โ Because sometimes simple is better. No database server required
- YAML config โ Tweak settings without touching code
Quick Start
# Install dependencies (requires Python 3.13+ and uv)
uv sync
# Run the app (default: http://0.0.0.0:8001)
uv run snekquiz
# or run via the CLI entry-point
snekquiz
# or via python -m
python -m snekquiz
# Run with custom host/port, SSL, auto-reload for development
snekquiz --host localhost --port 8000 --ssl_keyfile key.pem --ssl_certfile cert.pem --reload
# Run with custom config files
snekquiz --config /path/to/app.yaml --log-config /path/to/logging.yaml
# or via uvicorn directly. NB this will expect `uvicorn` CLI args only
uvicorn snekquiz:create_app --factory --host 0.0.0.0 --port 8001
The server starts at http://0.0.0.0:8001 by default. 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://0.0.0.0:8001/admin.
Adding Quizzes
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:8001/api/quizzes
Quiz format is pretty straightforward:
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 be supplied with:
- The
APP_CONFIGenvironment variable:export APP_CONFIG=/path/to/config.yaml - The
--config(or-c) CLI argument:snekquiz --config /path/to/config.yaml
Logging config (src/snekquiz/config/logging.yaml) provides default logging config.
A user specified config file can be supplied with:
- The
LOG_CONFIGenvironment variable:export LOG_CONFIG=/path/to/logging.yaml - The
--log-config(or-l) CLI argument:snekquiz --log-config /path/to/logging.yaml
When running with uvicorn directly, you must use the environment variable approach.
CLI Arguments
snekquiz --help
Available options:
--config,-c: Path to app config YAML (default: bundled config/app.yaml)--log-config,-l: Path to logging config YAML (default: bundled config/logging.yaml)--host: Host to bind to (default: 0.0.0.0)--port: Port to bind to (default: 8001)--reload: Enable auto-reload for development--ssl_keyfile: Path to SSL key file--ssl_certfile: Path to SSL certificate file
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
White, black, navy, teal, and more teal: #ffffff ยท #000000 ยท #002554 ยท #099d91 ยท #00cc99
License
See LICENSE.
Made with questionable judgment 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.4.2.tar.gz.
File metadata
- Download URL: snekquiz-0.4.2.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c8d019e9220410fcf5a6192e468f51aebc215073ec94aa8741f9629b530bfea
|
|
| MD5 |
75cad0ae8faf5ec12c585da4077a8ee5
|
|
| BLAKE2b-256 |
d390142a06e5d7eccc4855a002133ce326da6eb1112bb2cf2a2cc46751979a4a
|
Provenance
The following attestation bundles were made for snekquiz-0.4.2.tar.gz:
Publisher:
python-publish.yaml on ac3673/SnekQuiz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snekquiz-0.4.2.tar.gz -
Subject digest:
3c8d019e9220410fcf5a6192e468f51aebc215073ec94aa8741f9629b530bfea - Sigstore transparency entry: 1572840306
- Sigstore integration time:
-
Permalink:
ac3673/SnekQuiz@503572ba379db9ecc6c35a9b13f382ee4b76323e -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/ac3673
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yaml@503572ba379db9ecc6c35a9b13f382ee4b76323e -
Trigger Event:
release
-
Statement type:
File details
Details for the file snekquiz-0.4.2-py3-none-any.whl.
File metadata
- Download URL: snekquiz-0.4.2-py3-none-any.whl
- Upload date:
- Size: 28.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
153a7c0b49e04e3f510ea7a5b57abd0aee9ae3f11d954d007d639002d78c58cc
|
|
| MD5 |
33c626544289b5069b07db8c10313d24
|
|
| BLAKE2b-256 |
ec3c8398223a0995eb40413172ca700b7364fab7fa0a1cae0f40e9d5733f2d4c
|
Provenance
The following attestation bundles were made for snekquiz-0.4.2-py3-none-any.whl:
Publisher:
python-publish.yaml on ac3673/SnekQuiz
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
snekquiz-0.4.2-py3-none-any.whl -
Subject digest:
153a7c0b49e04e3f510ea7a5b57abd0aee9ae3f11d954d007d639002d78c58cc - Sigstore transparency entry: 1572840324
- Sigstore integration time:
-
Permalink:
ac3673/SnekQuiz@503572ba379db9ecc6c35a9b13f382ee4b76323e -
Branch / Tag:
refs/tags/v0.4.2 - Owner: https://github.com/ac3673
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yaml@503572ba379db9ecc6c35a9b13f382ee4b76323e -
Trigger Event:
release
-
Statement type: