Skip to main content

A simple, elegant Python framework for creating and running interactive quizzes

Project description

Quizy

Overview

A lightweight, API-first Python quiz framework for creating interactive quizzes and assessments. Quizy provides a clean, extensible interface without prescriptive UI constraints, allowing you to build quiz applications that fit your specific needs.

  • Async/Await Support: Non-blocking quiz execution with execute_async()
  • Live Timer Display: Real-time countdown with visual feedback
  • Partial Credit System: Award points for partially correct answers
  • Enhanced MatchingQuestion: Answer shuffling and optional partial credit
  • Improved CLI: Interactive formatting with progress indicators
  • Better Time Management: Per-question and quiz-level time limits with countdown

Important: Unified API

As of v0.3.0, all enhanced features are integrated directly into the core API. There are no separate "improved" modules:

# Single unified import
from quizy import Quiz, MatchingQuestion, QuizCLI, TimerDisplay

# All features are available by default
quiz = Quiz(title="My Quiz", time_limit=60)

1. Async Quiz Execution

Execute quizzes asynchronously for non-blocking operations:

from quizy import Quiz, MultipleChoiceQuestion, QuizCLI

quiz = Quiz(title="Async Quiz")
# ... add questions ...

# Run asynchronously
result = await quiz.execute_async(
    answer_provider=async_answer_provider,
    question_callback=async_question_callback
)

2. Enhanced MatchingQuestion

New options for better user experience:

from quizy import MatchingQuestion

question = MatchingQuestion(
    text="Match items",
    pairs={"A": "Apple", "B": "Banana", "C": "Cherry"},
    shuffle_answers=True,        # NEW: Randomize answer options (default: True)
    allow_partial_credit=False,  # NEW: Award partial points (default: False)
    time_limit=15                # Time limit in seconds
)

Partial Credit Calculation:

  • Full match: 100% credit
  • Partial match: correct_matches / total_pairs
  • Wrong selections: 0% credit

3. Partial Credit for MultipleSelectQuestion

Award partial credit for incomplete correct answers:

from quizy import MultipleSelectQuestion

question = MultipleSelectQuestion(
    text="Select all correct answers",
    options=["A", "B", "C", "D"],
    correct_answers=["A", "B"],
    allow_partial_credit=True,  # NEW
    shuffle_options=True
)

4. Live Timer Display

Display countdown timers with visual feedback:

from quizy import TimerDisplay

# Create a timer for a question
timer = TimerDisplay(duration=15)  # 15 seconds

# Check remaining time
remaining = timer.get_remaining()
time_string = timer.format_time(remaining)  # "00:05"

# Visual warning
warning = timer.get_warning_symbol()  # Returns ⚠️, ⏱️, or ✓

Time Warning Indicators:

  • - Plenty of time (>30s remaining)
  • ⏱️ - Getting short (10-30s remaining)
  • ⚠️ - Running out (≤10s remaining)
  • - Time expired

5. Enhanced Results with Metrics

New metrics in QuizResult:

result = quiz.execute(answer_provider=provide_answer)

# New properties
print(f"Score: {result.score_percentage:.1f}%")           # Includes partial credit
print(f"Partial Answers: {result.partial_answers}")       # Count of partial credit
print(f"Avg Time/Question: {result.average_time_per_question:.1f}s")

# New in QuestionResult
for qr in result.question_results:
    print(f"Score: {qr.score}")      # 0.0 to 1.0 (0.5 for partial)
    print(f"Is Partial: {qr.is_partial}")

6. Question Shuffling

Shuffle options for variety and fairness:

from quizy import MultipleChoiceQuestion

# Shuffle for this question
q1 = MultipleChoiceQuestion(
    text="What is 2+2?",
    options=["3", "4", "5"],
    correct_answer="4",
    shuffle_options=True
)

# Access shuffled display options
for opt in q1.display_options:
    print(opt)

7. Quiz Randomization

Randomize quiz question order:

quiz = Quiz(
    title="My Quiz",
    randomize_order=True,  # Shuffle question order
    shuffle_options=True,  # Shuffle all question options
    show_progress=True     # Show progress indicator
)

Enhanced CLI Features

Interactive Formatting

The QuizCLI now provides:

  • Color-coded output: Green for correct, red for incorrect, yellow for warnings
  • Visual status icons: ✓, ✗, ◐, ⏱, ⊘ for different result types
  • Progress indicators: Question numbering with remaining time
  • Better prompts: Context-aware input requests

Example Output

============================================================
Question 1/5 | ⏱️  15s
============================================================

What is the capital of France?

  1. London
  2. Berlin
  3. Paris
  4. Madrid

⏱️  Time remaining: 00:12
Your answer (1-4): 3

============================================================
  Results: Quiz Title
============================================================

Overall Performance:
  Total Questions:     5
  Correct Answers:     4
  Partial Answers:     1
  Score:               90.0%
  Time Taken:          00:34
  Avg Time/Question:   6.8s

Running Interactive Quiz

from quizy import QuizCLI

# With timer display
QuizCLI.run_interactive(quiz, show_timer=True)

# Async version
await QuizCLI.run_interactive_async(quiz, show_timer=True)

Showing Detailed Results

# Display breakdown for each question
QuizCLI.display_detailed_results(result)

API Compatibility

Unified Single API

v0.3.0 uses a single unified API. All enhanced features are directly integrated:

# Single import source
from quizy import (
    Quiz,
    MultipleChoiceQuestion,
    MatchingQuestion,
    QuizCLI,
    TimerDisplay
)

# All features available by default
quiz = Quiz(title="My Quiz", time_limit=60)

Performance Considerations

Async vs Sync

Use execute() when:

  • Simple synchronous answer providers
  • No blocking I/O operations
  • Standard quiz application

Use execute_async() when:

  • Answer provider makes network calls
  • Integrating with web servers (FastAPI, Django)
  • Real-time quiz systems
  • Complex async workflows

Timer Overhead

Timer display has minimal overhead:

  • Time checking: ~0.1ms per check
  • Display formatting: ~0.5ms per update
  • No separate threads needed (can be added for UI updates)

Migration Guide

From v0.2 to v0.3

  1. Questions still work the same:

    # v0.2
    q = MultipleChoiceQuestion(text="?", options=[...], correct_answer="...")
    
    # v0.3 - same, but can add new features
    q = MultipleChoiceQuestion(
        text="?",
        options=[...],
        correct_answer="...",
        shuffle_options=True  # NEW option
    )
    
  2. Add timer support gradually:

    # Add time_limit to existing questions
    q.time_limit = 15  # or in constructor
    

Examples

Complete Example: Timed Quiz with Async

import asyncio
from quizy import (
    Quiz,
    MultipleChoiceQuestion,
    MatchingQuestion,
    QuizCLI
)

async def main():
    quiz = Quiz(
        title="Python Fundamentals",
        time_limit=120,  # 2 minutes total
        allow_skip=False,
        shuffle_options=True,
        randomize_order=True
    )
    
    # Add timed questions
    quiz.add_question(MultipleChoiceQuestion(
        text="What does OOP stand for?",
        options=["Object Oriented Programming", "Other Object Patterns", "Outer Object Procedure"],
        correct_answer="Object Oriented Programming",
        time_limit=10
    ))
    
    quiz.add_question(MatchingQuestion(
        text="Match methods to descriptions",
        pairs={
            "__init__": "Constructor",
            "__str__": "String representation",
            "__len__": "Length"
        },
        shuffle_answers=True,
        allow_partial_credit=False,  # Disable by default
        time_limit=20
    ))
    
    # Run async
    result = await quiz.execute_async(
        answer_provider=async_get_answer,
        question_callback=async_display_question
    )
    
    QuizCLI.display_result(result)

async def async_get_answer(question, idx):
    # Your async answer logic here
    return "answer"

async def async_display_question(question, idx, total):
    # Your async display logic here
    pass

# Run
asyncio.run(main())

Known Limitations

  1. Terminal-based timer: Countdown display works best in interactive terminals
  2. Async input: Currently uses blocking input() - can be enhanced with aioinput
  3. Option shuffling: Once shuffled, cannot be re-shuffled in same instance
  4. Partial credit: Some question types (Short Text) don't support partial credit

8. AI-Powered Question Generation

Generate quiz questions automatically using OpenAI's GPT models:

import asyncio
from quizy import Quiz
from quizy.ai_generator import AIQuestionGenerator
from quizy.core import QuestionType

async def main():
    # Initialize generator
    generator = AIQuestionGenerator(api_key="your-openai-key")

    # Generate a complete quiz
    questions = await generator.generate_quiz(
        topic="Python Programming",
        num_questions=5,
        question_types=[
            QuestionType.MULTIPLE_CHOICE,
            QuestionType.TRUE_FALSE,
            QuestionType.MULTIPLE_SELECT,
            QuestionType.MULTIPLE_CHOICE,
            QuestionType.SHORT_TEXT,
        ],
        difficulty="medium"
    )

    # Create quiz from AI-generated questions
    quiz = Quiz(title="AI-Generated Python Quiz")
    for question in questions:
        quiz.add_question(question)

    result = await quiz.execute_async(answer_provider=get_answer)

asyncio.run(main())

Setup

  1. Install the package with AI support:

    pip install quizy openai
    
  2. Set your OpenAI API key:

    export OPENAI_API_KEY="sk-..."
    

    Or pass it directly:

    generator = AIQuestionGenerator(api_key="sk-...")
    

Supported Question Types

  • MULTIPLE_CHOICE: Traditional multiple choice with single correct answer
  • TRUE_FALSE: Binary true/false questions
  • SHORT_TEXT: Free-form text answers
  • MULTIPLE_SELECT: Select all correct answers from a list
  • MATCHING: Match items to descriptions

Difficulty Levels

  • easy: Basic knowledge recall
  • medium: Requires understanding and application
  • hard: Requires analysis and synthesis

Asynchronous Generation

Generate multiple questions concurrently for better performance:

import asyncio
from quizy.ai_generator import AIQuestionGenerator
from quizy.core import QuestionType

async def create_quiz():
    generator = AIQuestionGenerator()
    
    # Generate 10 questions concurrently
    questions = await generator.generate_quiz(
        topic="Web Development",
        num_questions=10,
        difficulty="medium",
        question_types=[QuestionType.MULTIPLE_CHOICE] * 10
    )
    
    return questions

questions = asyncio.run(create_quiz())

Generation with Context

Provide reading material or specific context for question generation:

import asyncio
from quizy.ai_generator import AIQuestionGenerator

async def main():
    generator = AIQuestionGenerator()
    
    context = """
    Photosynthesis is the process by which plants use sunlight to synthesize 
    carbohydrates from carbon dioxide and water. It occurs in two stages: 
    light-dependent reactions and the Calvin cycle...
    """
    
    questions = await generator.generate_quiz(
        topic="Photosynthesis",
        num_questions=5,
        context=context,
        difficulty="medium"
    )

asyncio.run(main())

Complete Example

import asyncio
from quizy import Quiz, QuizCLI
from quizy.ai_generator import AIQuestionGenerator
from quizy.core import QuestionType

async def create_and_run_ai_quiz():
    # Initialize generator
    generator = AIQuestionGenerator()
    
    # Generate quiz asynchronously
    print("Generating quiz questions...")
    questions = await generator.generate_quiz(
        topic="Python Async Programming",
        num_questions=5,
        difficulty="hard",
        question_types=[
            QuestionType.MULTIPLE_CHOICE,
            QuestionType.TRUE_FALSE,
            QuestionType.MULTIPLE_SELECT,
            QuestionType.MULTIPLE_CHOICE,
            QuestionType.SHORT_TEXT,
        ]
    )
    
    # Create and run quiz
    quiz = Quiz(
        title="AI-Generated Python Async Quiz",
        time_limit=300,  # 5 minutes
        show_progress=True
    )
    
    for q in questions:
        quiz.add_question(q)
    
    # Run with CLI
    QuizCLI.run_interactive(quiz, show_timer=True)

# Run
asyncio.run(create_and_run_ai_quiz())

Future Enhancements

Planned for v0.5+:

  • Web-based UI components
  • Database integration for result persistence
  • Advanced analytics and reporting
  • Real-time leaderboards
  • Support for more AI providers (Claude, Gemini, etc.)
  • More partial credit options
  • Fine-tuning for domain-specific questions

Support & Contributions

For issues, feature requests, or contributions:

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

quizy-0.4.3.tar.gz (31.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

quizy-0.4.3-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file quizy-0.4.3.tar.gz.

File metadata

  • Download URL: quizy-0.4.3.tar.gz
  • Upload date:
  • Size: 31.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for quizy-0.4.3.tar.gz
Algorithm Hash digest
SHA256 3a6f89ee0d91153ba7cb62f54e848cadb8e6ff1dcd0e21b17a0f4d559291b009
MD5 7c1e450e453de2c2605bc30d0c3992cb
BLAKE2b-256 0cc14db43aa4277c1a426026dafac5e735e0d422930ab4e275fa9cdc0e8575d6

See more details on using hashes here.

File details

Details for the file quizy-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: quizy-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for quizy-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 cc7ce8276274a1b2189a31fa18302e6cfdd7b4e089938d7ced4d9e25fcde44bc
MD5 8aeac18f8ec7565974be5040836142f7
BLAKE2b-256 df801185e5f654fdac0b59165be57d20f7e04435c463053f1a9a37c0f8fdee48

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page