Software Journal
Web Development Updated Sep 1, 2026 8 min read

Inside Complexity_Quiz: How Flask + Google Gemini AI Generate Dynamic Python Iteration Quizzes

Deep dive into Complexity_Quiz, an open-source Flask app that uses Google Gemini to dynamically generate Python code complexity challenges. Learn its tech stack, prompt engineering, Flask routes, and how frontend and AI work together.complexity-quiz-flask-gemini-ai-dynamic-python-quiz-explained

Portrait of Benmalek Zohir

Benmalek Zohir

Contributor

Share
Inside Complexity_Quiz: How Flask + Google Gemini AI Generate Dynamic Python Iteration Quizzes

Inside Complexity_Quiz: How Flask + Google Gemini AI Generate Dynamic Python Iteration Quizzes

Source Code: azario0/Complexity_Quiz on GitHub

What if your quiz questions were never the same twice? That’s the core idea behind Complexity_Quiz — a web-based Python quiz application that doesn’t use a static database of questions. Instead, it leverages Google’s Generative AI (Gemini) to generate a fresh Python code snippet on every request and challenges you to count its total iterations.

In this article, we’ll break down the entire project file-by-file: the languages used, the methods that power it, and how everything works together from browser to AI and back.


1. Project Overview

Complexity_Quiz is an educational tool built to test understanding of loops, recursion, and iteration counting. The workflow is simple:

  1. User selects a complexity level 1 to 3 on the home page.
  2. The app asks Gemini to generate a Python function for that level.
  3. The user analyzes the code and picks the correct total iteration count from 3 multiple-choice options.
  4. Instant feedback is shown via Flask’s flash messaging system.

No hard-coded questions. Every quiz is AI-generated.

Key Features:

  • Dynamic Quiz Generation via Gemini API
  • 3 Variable Complexity Levels
  • Clean Interactive UI
  • Instant Feedback & Validation
  • Lightweight Flask Backend

2. Tech Stack & Languages Used

The project intentionally stays minimal and lightweight:

LayerTechnologyPurpose
BackendPython 3.7+Core logic
Web FrameworkFlaskRouting, templating, server
AI EngineGoogle Generative AI SDK (google-generativeai) + Gemini 1.5 FlashCode & quiz generation
FrontendHTML5, CSS3Structure & styling
TemplatingJinja2 (via Flask)Dynamic HTML rendering ({{ quiz.code }})
PrototypingJupyter Notebook (notebook.ipynb)Prompt engineering & testing
Env Managementpython-dotenv (recommended)Secure API key storage

Project Structure

Complexity_Quiz/
├── quiz_app/
│   ├── app.py                  # <-- Flask app & all backend logic
│   ├── templates/
│   │   ├── index.html          # <-- Home / complexity selector
│   │   └── quiz.html           # <-- Quiz question page
│   └── static/css/style.css    # <-- Styling
├── notebook.ipynb              # <-- Prompt prototyping playground
├── README.md
└── LICENSE (MIT)

Explore it live: quiz_app/app.py, notebook.ipynb


3. Methods Explained - A Deep Dive Into app.py

The entire backend lives in quiz_app/app.py (~100 lines). Let’s break down each method.

3.1. Configuration & Setup — quiz_app/app.py:1-11

from flask import Flask, render_template, request, flash, redirect, url_for
import google.generativeai as genai
import json
import os

genai.configure(api_key="YOUR_KEY") # Replace with your API key
app = Flask(__name__)
app.secret_key = os.urandom(24)
  • Flask is initialized and a random secret_key is generated for session & flash messaging.
  • google.generativeai is configured with a Gemini API key. The README correctly recommends moving this to a .env file using [python-dotenv](https://pypi.org/project/python-dotenv/) and os.getenv("GEMINI_API_KEY") for security.

3.2. The Brain: prompt_engineering(complexity: int) — quiz_app/app.py:13-41

This is the most critical function. It’s a prompt engineering technique that forces the LLM to behave deterministically.

It returns an f-string prompt that instructs Gemini:

  1. Act as an expert Python teacher.
  2. Generate code exactly matching the complexity level passed in.
  3. Calculate the total number of iterations when executed.
  4. Create 3 multiple-choice integers (1 correct, 2 plausible distractors).
  5. Return ONLY strict JSON with this schema:
{
  "code": "Python code as a string",
  "choices": [int, int, int],
  "correct_choice": int
}

Why this works: By explicitly defining the JSON schema, forbidding extra text, and repeating the constraint Focus only on the requested complexity level: {complexity}, the prompt minimizes hallucinations and makes the response parsable with json.loads().

The prototype version in notebook.ipynb:105-152 is even more detailed, including few-shot examples:

Complexity 1: Single for i in range(num): -> 3 iterations
Complexity 2: Loop + recursion loop_function(num-1) -> 6 iterations
Complexity 3: Mutual recursion loop_function <-> loop_function_2 -> 5 iterations

These examples teach Gemini what “complexity” means without fine-tuning.

3.3. Route 1: index() — quiz_app/app.py:43-57

@app.route('/', methods=['GET', 'POST'])
def index():

3.4. Route 2: quiz(complexity) — quiz_app/app.py:59-76

This is where the AI magic happens:

@app.route('/quiz/<int:complexity>')
def quiz(complexity):
    model_name = "gemini-1.5-flash"
    prompt = prompt_engineering(complexity)
    response = genai.GenerativeModel(model_name).generate_content(prompt)
    cleaned_response = response.text.strip().replace("```json", "").replace("```", "").strip()
    quiz_data = json.loads(cleaned_response)
    return render_template('quiz.html', quiz=quiz_data, complexity=complexity)

Step-by-step:

  1. Selects the model: gemini-1.5-flash (fast, cheap, ideal for low-latency quizzes). The notebook tests gemini-2.5-flash.
  2. Calls generate_content(prompt) via the Gemini API.
  3. Cleans the response: Gemini often wraps JSON in ```json ... ``` markdown. The code strips this to get valid JSON — a crucial real-world LLM handling trick.
  4. Parses with json.loads() and passes quiz_data to templates/quiz.html via Jinja2.
  5. Wrapped in try/except — on failure, flashes the error and redirects home.

3.5. Route 3: submit() — quiz_app/app.py:78-96

@app.route('/submit', methods=['POST'])
def submit():
    user_answer = int(request.form['choice'])
    correct_answer = int(request.form['correct_choice'])
    if user_answer == correct_answer:
        flash('Correct! Well done.', 'success')
    else:
        flash(f'Incorrect. The correct answer was {correct_answer}.', 'danger')
    return redirect(url_for('quiz', complexity=complexity))
  • The quiz page hides correct_choice and complexity in <input type="hidden"> fields. This is simple but not tamper-proof (a user could inspect HTML). A more secure approach would use Flask session.
  • It compares integers and uses categorized flashes ('success', 'danger', 'warning') which the CSS styles differently. Then it redirects to generate a new question at the same complexity level.

4. Frontend: How the UI Works

templates/index.html:1-31 & templates/quiz.html:1-38

Both templates are pure HTML5 + Jinja2 templating.

  • index.html: A centered form with action="/" method="post" and <input type="number" min="1" max="3">. Displays flash errors with {% with messages = get_flashed_messages() %}.
  • quiz.html: Displays the code inside <pre><code>{{ quiz.code }}</code></pre> (preserves formatting). Loops over choices with {% for choice in quiz.choices %} to generate 3 buttons. Each button submits name="choice" value="{{ choice }}" to /submit. Hidden fields preserve state.

static/css/style.css:1-102

Minimal, modern CSS3:

  • Flexbox centering: display: flex; justify-content: center; align-items: center; min-height: 100vh;
  • Card UI: .container with box-shadow and border-radius: 8px
  • Code block: pre { background-color: #f5f5f5; white-space: pre-wrap; } for readability
  • Flash styling: .flash-success (green), .flash-danger (red), .flash-warning (yellow) mapped directly to Flask categories.

No JavaScript needed — full functionality via HTML forms + Flask.


5. How It All Works Together (The Full Lifecycle)

Here’s the request journey:

[Browser: index.html] --POST complexity=2--> [Flask: index() validates] --redirect--> [Flask: quiz(2)]
      |
      |---> [prompt_engineering(2) builds prompt] ---> [Google Gemini API: gemini-1.5-flash]
      |                                                        |
      |<--- JSON {code, choices, correct_choice} <-------------|
      |
      |---> [json.loads + render_template('quiz.html')] ---> [Browser: shows code + 3 buttons]
      |
[Browser: clicks choice=8] --POST to /submit--> [Flask: submit() compares] --flash message--> [redirect to /quiz/2 for new question]

The beauty is stateless AI generation: The app holds no question bank. The notebook (notebook.ipynb:83-161) was the lab where this prompt was iterated before being copied (almost identically) into app.py. The only difference is the notebook includes explicit examples in the prompt for better grounding — a good upgrade to port to app.py.


6. Strengths, Limits & How to Improve It

Strengths:

Current Limitations & Easy Fixes:

  1. Hardcoded API Key (app.py:8): Fix via load_dotenv() as the README suggests. See Google AI Studio to get your key.
  2. Client-side answer storage: Move correct_choice to session instead of hidden input.
  3. No requirements.txt: You’ll need Flask, google-generativeai, python-dotenv.
  4. No iteration verification: The app trusts Gemini’s correct_choice. A robust version would execute the generated code in a sandboxed exec() and count iterations programmatically to verify.

Ideas to Extend:

  • Add Complexity 4-5 (nested loops, Big-O explanations)
  • Add a timer & scoring system
  • Use Streaming for faster perceived generation
  • Deploy to Render or Railway with environment variables

7. Conclusion

Complexity_Quiz proves you don’t need a massive codebase to build an intelligent educational app. With just ~100 lines of Python, a bit of HTML/CSS, and clever prompt engineering, it turns a static Flask app into a dynamic, AI-powered tutor.

It’s a perfect starter project for anyone learning Flask, experimenting with the Gemini API, or teaching code complexity. Fork it, add dotenv security, deploy it, and you have a portfolio-ready AI web app.

Ready to try it? Clone it:

git clone https://github.com/azario0/Complexity_Quiz.git
cd Complexity_Quiz
pip install Flask google-generativeai python-dotenv
# Add GEMINI_API_KEY to .env, then
flask --app quiz_app/app run --debug

And visit http://127.0.0.1:5000.

Licensed under MIT License by Zohir Benmalek.

Share
Portrait of Benmalek Zohir

Written by

Benmalek Zohir

Founder, AI Engineer & Full Stack Developer

Benmalek Zohir is an AI Engineer, Full Stack Developer, and technology enthusiast focused on artificial intelligence, software development, and emerging technologies. He is the founder of SoftwareJournal.blog, where he shares practical insights, software discoveries, AI tools, and the latest developments in technology.

The Software Journal Dispatch

One excellent engineering read, every week.

A concise digest of our best new essays on architecture, tooling, databases, and the craft of software. No spam, no noise — unsubscribe anytime.