Build production-style quiz platforms with persistent storage, scoring logic, and admin capabilities. Use when creating educational quizzes, knowledge assessments, trivia games, certification tests, or any interactive Q&A system with user authentication and score tracking.
Build a complete quiz system with user authentication, persistent storage, weighted scoring, admin tools, and a polished player experience.
Authentication Layer → Quiz Engine → Scoring System → Results Dashboard
| | | |
User Profiles Question Bank Score History Leaderboard
| | | |
Session Mgmt Randomization Weighted Logic Analytics
# User model
user = {
"id": "unique-id",
"username": "player1",
"email": "[email protected]",
"password_hash": "bcrypt-hash",
"role": "player", # "player" or "admin"
"created_at": "2026-03-27",
"stats": {
"quizzes_taken": 0,
"total_score": 0,
"best_score": 0,
"average_score": 0
}
}
question = {
"id": "q-001",
"text": "What is the primary purpose of a CPU?",
"options": [
{"id": "a", "text": "Process instructions and manage data flow"},
{"id": "b", "text": "Store permanent data"},
{"id": "c", "text": "Render graphics"},
{"id": "d", "text": "Connect to the internet"}
],
"correct": "a",
"difficulty": "medium", # easy, medium, hard
"category": "Technology",
"points": 10, # base points (modified by difficulty)
"time_limit": 30, # seconds
"explanation": "CPUs execute program instructions and coordinate data between components."
}
Organize questions by topic and difficulty:
categories = {
"Technology": {"easy": 15, "medium": 20, "hard": 10},
"Science": {"easy": 12, "medium": 18, "hard": 8},
"Business": {"easy": 10, "medium": 15, "hard": 5}
}
quiz_config = {
"total_questions": 10,
"difficulty_mix": {
"easy": 3, # 30%
"medium": 5, # 50%
"hard": 2 # 20%
},
"time_per_question": 30, # seconds
"categories": ["Technology", "Science"], # filter or "all"
"shuffle_options": True,
"prevent_repeats": True # no duplicate questions in a session
}
import random
def select_questions(bank: list, config: dict, user_history: list) -> list:
"""Select randomized question set avoiding repeats."""
available = [q for q in bank if q["id"] not in user_history]
selected = []
for difficulty, count in config["difficulty_mix"].items():
pool = [q for q in available
if q["difficulty"] == difficulty
and q["category"] in config["categories"]]
if len(pool) < count:
# Fall back to any available at this difficulty
pool = [q for q in available if q["difficulty"] == difficulty]
chosen = random.sample(pool, min(count, len(pool)))
selected.extend(chosen)
for q in chosen:
available.remove(q)
random.shuffle(selected)
return selected
def score_question(question: dict, answer: str, time_taken: float) -> dict:
"""Score a single question with time bonus."""
base_points = question["points"]
difficulty_multiplier = {"easy": 1.0, "medium": 1.5, "hard": 2.0}
multiplier = difficulty_multiplier.get(question["difficulty"], 1.0)
correct = answer == question["correct"]
if not correct:
return {"correct": False, "points": 0, "time_taken": time_taken}
# Time bonus: faster answers get up to 50% bonus
time_limit = question.get("time_limit", 30)
time_ratio = max(0, 1 - (time_taken / time_limit))
time_bonus = base_points * 0.5 * time_ratio
total = round((base_points * multiplier) + time_bonus)
return {
"correct": True,
"points": total,
"base_points": base_points,
"time_bonus": round(time_bonus),
"time_taken": round(time_taken, 1)
}
# Admin endpoints / functions
def add_question(question: dict) -> bool:
"""Validate and add a question to the bank."""
# Validation
assert question.get("text"), "Question text required"
assert len(question.get("options", [])) >= 2, "Minimum 2 options"
assert question.get("correct") in [o["id"] for o in question["options"]], "Correct answer must match an option"
assert question.get("difficulty") in ("easy", "medium", "hard"), "Invalid difficulty"
# Save to storage
return save_question(question)
def edit_question(question_id: str, updates: dict) -> bool:
"""Update existing question with validation."""
existing = get_question(question_id)
if not existing:
return False
merged = {**existing, **updates}
return save_question(merged)
def delete_question(question_id: str) -> bool:
"""Remove question from bank."""
return remove_question(question_id)
admin_stats = {
"total_questions": 150,
"by_difficulty": {"easy": 50, "medium": 70, "hard": 30},
"by_category": {"Technology": 45, "Science": 40, "Business": 35, "General": 30},
"most_missed": [ # questions with lowest correct rate
{"id": "q-042", "text": "...", "correct_rate": 0.23},
],
"average_completion_time": 245, # seconds per quiz
"total_attempts": 1250,
"unique_players": 340
}
quiz_result = {
"user_id": "user-123",
"quiz_id": "quiz-456",
"date": "2026-03-27",
"score": 145,
"max_possible": 200,
"percentage": 72.5,
"questions": 10,
"correct": 7,
"incorrect": 3,
"average_time": 18.3, # seconds per question
"fastest_answer": 4.2,
"by_difficulty": {
"easy": {"correct": 3, "total": 3},
"medium": {"correct": 3, "total": 5},
"hard": {"correct": 1, "total": 2}
}
}
def get_leaderboard(period: str = "all", limit: int = 10) -> list:
"""Get top scores for a time period."""
# period: "daily", "weekly", "monthly", "all"
scores = fetch_scores(period=period)
ranked = sorted(scores, key=lambda x: x["score"], reverse=True)
return ranked[:limit]
Handle these scenarios: