Skip to content

Rusty-nails/ab-testing-webapp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A/B Testing Web App

A Flask web application that demonstrates A/B testing with session stickiness and click tracking. When a user logs in, they are randomly assigned to either Version A or Version B of the home page. Their button clicks are logged to a JSON file along with the IP address, timestamp, and which version they saw.

What the App Does

  1. A static intro page introduces the project and links to the login feature
  2. The login form takes any username (no real password required for this demo)
  3. On login, a random 50/50 coin flip assigns the user to Version A or Version B
  4. The assignment is stored in a session cookie so the user keeps seeing the same version on every visit
  5. Each home page has tracked buttons that log clicks to data/clicks.json
  6. A /stats page shows all the captured click data

Tech Stack

  • Python 3.8+ with Flask for the backend
  • HTML and CSS for the frontend
  • Vanilla JavaScript (fetch) for the click tracking calls
  • JSON file for storing click data (no database setup needed)

Project Structure

ab-testing-webapp/
├── app.py                  # Main Flask application
├── requirements.txt        # Python dependencies
├── README.md               # This file
├── data/
│   └── clicks.json         # Click event storage
├── templates/
│   ├── intro.html          # Static intro page
│   ├── login.html          # Login form
│   ├── home_a.html         # Version A home page
│   ├── home_b.html         # Version B home page
│   └── stats.html          # View captured click data
└── static/
    ├── style_main.css      # Styles for intro, login, stats
    ├── style_a.css         # Version A styling (light theme, serif body, blue accent, top-nav)
    └── style_b.css         # Version B styling (dark mode, sans-serif, amber accent, sidebar nav)

How to Set Up and Run

1. Clone the repository

git clone <your-repo-url>
cd ab-testing-webapp

2. Create a virtual environment (optional but recommended)

python -m venv venv
source venv/bin/activate    # On macOS/Linux
venv\Scripts\activate       # On Windows

3. Install dependencies

pip install -r requirements.txt

4. Run the app

python app.py

5. Open in your browser

Go to http://127.0.0.1:5000

How to Test the A/B Logic

  1. Open http://127.0.0.1:5000 and click "Go to Login"
  2. Enter any username and submit
  3. You will be randomly placed on Version A (light theme, serif body, top navigation) or Version B (dark mode, amber accent, sidebar navigation)
  4. Click some buttons and watch the status update
  5. Visit http://127.0.0.1:5000/stats to see your tracked clicks
  6. To get reassigned for testing, click "Log out" or clear your browser cookies, then log in again
  7. Try opening the app in a different browser or in private/incognito mode to land on the other version

How the A/B Distribution Works

In app.py, the assign_version() function uses Python's random.random() to flip a virtual coin:

def assign_version():
    return "A" if random.random() < 0.5 else "B"

The result is stored in session["version"]. Flask's session is signed with the secret key and sent as a cookie to the browser. On every request, Flask reads the cookie and pulls out the version, so the user keeps seeing the same one. This is how stickiness works without a database.

What Gets Tracked

Every button click triggers a POST to /track with the button's structural position ID and visible label. The server then logs:

  • username (whatever was entered at login)
  • version (A or B)
  • position (structural ID like nav_1, nav_2, nav_3, cta_primary, cta_secondary)
  • label (the visible button text, e.g. "Start Now" or "View Profile")
  • ip_address (request.remote_addr from Flask)
  • timestamp (ISO format date and time)

These all go into data/clicks.json as a list of objects.

Why position IDs and not just button names

Both versions of the home page have the same five buttons in the same structural roles (3 nav buttons + 2 CTA buttons). The position field captures the role (cta_primary, etc.) so you can compare, for example, "primary CTA clicks on Version A" vs "primary CTA clicks on Version B" directly — even if the button labels were different. The label field captures what the user actually saw, so you keep the human-readable context.

Routes

Route Method What It Does
/ GET Static intro page
/login GET Show login form
/login POST Process login and assign A/B version
/home GET Render Version A or B based on session
/track POST Log a click event to the JSON file
/stats GET View all collected click data
/logout GET Clear session and return to intro

Notes

  • The secret_key in app.py is set to a placeholder. For a real deployment you would put it in an environment variable.
  • The JSON file approach is fine for a demo. For production you would swap this for SQLite or a real database.
  • The login does not check passwords. This is by design for the demo. The focus is the A/B logic.

Author: CSP Student-M252

About

CST-640- Week-6

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors