WyR/project-context.md

15 KiB

Would You Rather Website - Project Context

Project Goal

Build a lightweight "Would You Rather" website hosted on a Raspberry Pi 5.

Users visit the website, answer a daily question, and immediately see aggregated results after voting.

Example:

Would you rather be blind or deaf?

  • Blind
  • Deaf

After voting:

  • Blind: 62%
  • Deaf: 38%

Total votes: 523


Current Development Strategy

Development is being done on a laptop first.

Reasons:

  • Easier coding workflow
  • Faster testing
  • Git integration from day one
  • Easier version control
  • Safer than developing directly on the Raspberry Pi

Deployment strategy:

Laptop
    ↓
Git
    ↓
GitHub
    ↓
Raspberry Pi
    ↓
git pull
    ↓
Production

Technology Stack

Backend

  • Python
  • Flask
  • Flask application factory pattern
  • Flask blueprints for public and admin routes

Database

  • SQLite
  • Local development database path: instance/questions.db

Frontend

  • HTML
  • CSS
  • Jinja2 templates

Authentication

  • Flask sessions
  • Werkzeug password hashing for admin passwords

Version Control

  • Git
  • GitHub

Future Production

  • Gunicorn
  • Systemd
  • Nginx
  • HTTPS with Let's Encrypt

Current Local Project Structure

The project has been refactored out of a single-file Flask app.

Current structure:

would-you-rather/
│
├── .venv/
├── .gitignore
├── app.py
├── run.py
├── requirements.txt
├── project-context.md
│
├── app/
│   ├── __init__.py
│   ├── admin.py
│   ├── database.py
│   ├── routes.py
│   │
│   ├── templates/
│   │   ├── public/
│   │   │   ├── index.html
│   │   │   └── results.html
│   │   │
│   │   └── admin/
│   │       ├── dashboard.html
│   │       ├── delete_confirm.html
│   │       ├── login.html
│   │       └── question_form.html
│   │
│   └── static/
│       └── style.css
│
├── scripts/
│   └── init_db.py
│
├── tests/
│   └── test_app.py
│
└── instance/
    └── questions.db

Notes:

  • app.py remains as a compatibility entrypoint.
  • run.py is the preferred local development entrypoint.
  • instance/ is ignored by Git.
  • Root-level questions.db is ignored and is no longer the active app database.

Current Application Architecture

App Factory

File:

app/__init__.py

Purpose:

  • Creates the Flask app with create_app().
  • Configures SECRET_KEY.
  • Configures SQLite database path.
  • Ensures the instance/ directory exists.
  • Registers public and admin blueprints.
  • Registers database teardown behavior.

Important config:

SECRET_KEY = os.environ.get("WYR_SECRET_KEY", "dev-only-change-me")
DATABASE = instance/questions.db

Production must set a real WYR_SECRET_KEY.


Database Helpers

File:

app/database.py

Purpose:

  • Opens SQLite connections with get_db().
  • Uses sqlite3.Row so routes/templates can access fields by name.
  • Closes request-scoped connections with close_db().
  • Creates all tables with init_db().

Public Routes

File:

app/routes.py

Routes:

/
/vote
/results

Behavior:

  • / displays the question scheduled for today's date.
  • /vote accepts POSTed qid and vote.
  • /vote only accepts votes for today's scheduled question.
  • /results displays today's vote counts, percentages, and total votes.
  • Empty states are shown if no question exists for today.
  • Duplicate voting is still blocked by hashing the request IP address.

Current duplicate prevention is temporary.

Future plan:

  • Use cookies or session identifiers instead of IP hashing.

Current Database Design

Questions Table

CREATE TABLE questions (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    question TEXT NOT NULL,
    option_a TEXT NOT NULL,
    option_b TEXT NOT NULL,
    date TEXT NOT NULL UNIQUE
);

Purpose:

Stores scheduled daily questions.


Votes Table

CREATE TABLE votes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    question_id INTEGER,
    vote TEXT NOT NULL,
    voter_hash TEXT NOT NULL
);

Purpose:

Stores user votes.

Stored values:

  • question_id
  • selected vote, currently A or B
  • voter_hash

Admins Table

CREATE TABLE admins (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL
);

Purpose:

Stores admin login accounts.

Passwords must never be stored in plain text.

Current implementation uses:

from werkzeug.security import generate_password_hash, check_password_hash

Admin Dashboard V1

Admin functionality has been implemented.

File:

app/admin.py

Routes:

/admin/login
/admin/logout
/admin/account
/admin/dashboard
/admin/question/new
/admin/question/edit/<id>
/admin/question/delete/<id>

Implemented behavior:

  • Admin login with username/password.
  • Password verification with Werkzeug password hashing.
  • Successful login stores session["admin"] = True.
  • Protected admin pages use @admin_required.
  • Unauthenticated users are redirected to /admin/login.
  • Admins can change their username and password from /admin/account.
  • Changing credentials requires the current password.
  • Dashboard lists all questions ordered by date descending.
  • Admins can create questions.
  • Admins can edit questions.
  • Admins can delete questions.
  • Delete also removes votes for that question.
  • Duplicate question dates are blocked.
  • Required fields are validated.

Dashboard V1 is intentionally simple.

No JavaScript calendar is implemented yet.


Local Development Setup

Install Dependencies

Use the virtual environment:

source .venv/bin/activate
pip install -r requirements.txt

Or run commands directly through .venv/bin/python.


Initialize the Database

Run:

.venv/bin/python scripts/init_db.py

This creates:

instance/questions.db

It also seeds:

  • one sample question
  • one local admin account

Default local admin login:

username: admin
password: admin

To override the seeded admin account:

WYR_ADMIN_USERNAME=myname WYR_ADMIN_PASSWORD=mypassword .venv/bin/python scripts/init_db.py

If the admin user already exists, the script will not overwrite that password.


Run the Site Locally

Preferred command:

.venv/bin/python run.py

Then open:

http://127.0.0.1:5000

Admin login:

http://127.0.0.1:5000/admin/login

Default local login:

admin / admin

Restart After Local Changes

If the app is running manually in a terminal:

  1. Stop it with Ctrl+C.
  2. Make changes or pull changes from Git.
  3. Restart the app.

Local restart command:

.venv/bin/python run.py

If dependencies changed:

.venv/bin/pip install -r requirements.txt

If database tables changed:

.venv/bin/python scripts/init_db.py

Typical local update flow:

git pull
.venv/bin/pip install -r requirements.txt
.venv/bin/python scripts/init_db.py
.venv/bin/python run.py

Run Tests

Run:

.venv/bin/python -m unittest discover -s tests

Current automated tests cover:

  • public empty state
  • voting flow
  • duplicate vote prevention
  • admin login success
  • admin login failure
  • admin credential changes
  • protected dashboard redirect
  • duplicate date validation when creating questions

Manual Testing Checklist

Public Site

  1. Start the app:
.venv/bin/python run.py
  1. Open:
http://127.0.0.1:5000
  1. Confirm the sample question appears if it is scheduled for today's date.

  2. Click one answer.

  3. Confirm the site redirects to /results.

  4. Confirm results show percentages and total votes.

  5. Vote again from the same browser/IP.

  6. Confirm the duplicate vote does not increase the vote count.


Admin Site

  1. Open:
http://127.0.0.1:5000/admin/login
  1. Log in with:
admin / admin
  1. Confirm the dashboard appears.

  2. Click Change Credentials.

  3. Confirm changing credentials requires the current password.

  4. Save new credentials, log out, and confirm the new login works.

  5. Click Create Question.

  6. Add today's date or a future date, question, option A, and option B.

  7. Save and confirm the new question appears in the dashboard.

  8. Edit the question and confirm changes persist.

  9. Try creating another question with the same date.

  10. Confirm this error appears:

Date already has a question.
  1. Delete a question and confirm it disappears from the dashboard.

  2. Log out.

  3. Visit /admin/dashboard while logged out and confirm it redirects to /admin/login.


Development Notes

Current Compatibility Entrypoints

Preferred local command:

.venv/bin/python run.py

Compatibility command:

.venv/bin/python app.py

Both create the app through:

from app import create_app

Git Ignore Rules

The following local-only files/directories are ignored:

.venv/
__pycache__/
*.pyc
questions.db
instance/
.env
.vscode/
.idea/

The active SQLite database is intentionally not committed.


What To Commit To Git

Safe and expected to commit:

app/
scripts/
tests/
app.py
run.py
requirements.txt
.gitignore
project-context.md

Also commit deletions/moves when files are refactored.

Current old paths that were replaced:

init_db.py
templates/
static/

These old paths should remain deleted once the new app/ package layout is committed.


What Not To Commit

Keep these local only:

.venv/
instance/
instance/questions.db
questions.db
.env
__pycache__/
.vscode/
.idea/

Reasons:

  • .venv/ is machine-specific.
  • SQLite databases contain local runtime data.
  • .env may contain secrets.
  • cache/editor files are not project source.

Even with a self-hosted private Git server, avoid committing secrets.


Future Admin Features

Dashboard Version 2

Use a proper calendar interface.

Possible library:

  • FullCalendar

Desired behavior:

June 2026

13 Question
14 Question
15 Empty
16 Question

Clicking a date:

  • opens edit view if the date has a question
  • opens create view if the date is empty

Search questions by:

  • date
  • question text

Statistics View

Admin dashboard should eventually show:

Question

Blind: 142
Deaf: 98

Total: 240

Useful for engagement analytics.


Future Public Features

Better Duplicate Vote Prevention

Replace IP hashing with:

  • cookies
  • sessions
  • or lightweight anonymous visitor IDs

Archive

Browse historical questions.


Comments

Discussion under questions.


Country Statistics

Example:

Denmark

Blind: 65%
Deaf: 35%

United States

Blind: 52%
Deaf: 48%

AI Generated Images

One image per question.

Example:

Would you rather live underwater or in space?

with generated artwork.


User Accounts

Optional.

Users can:

  • save history
  • track participation

Future AI Features

Not a priority yet.

Planned later.

Possible route:

Generate next 30 questions

Workflow:

  1. AI generates questions.
  2. Questions fill empty dates.
  3. Admin reviews.
  4. Admin approves.

Potential providers:

  • OpenAI
  • OpenRouter
  • Mistral

Future Raspberry Pi Deployment

Local Raspberry Pi testing is currently done manually on the home network.

The future goal is to run the app as a managed service with systemd, then later put it behind Gunicorn/Nginx for production.


Raspberry Pi Local Network Test Setup

Install system dependencies:

sudo apt update
sudo apt install -y git python3 python3-venv

Clone the repo:

git clone <your-self-hosted-git-url> wyr
cd wyr

Create and install into a virtual environment:

python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Set local secrets and initialize the database:

export WYR_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
export WYR_ADMIN_USERNAME="youradmin"
export WYR_ADMIN_PASSWORD="yourstrongpassword"

.venv/bin/python scripts/init_db.py

Run on the home LAN:

WYR_SECRET_KEY="$WYR_SECRET_KEY" .venv/bin/flask --app run run --host 0.0.0.0 --port 5000

Open from another device on the same network:

http://<raspberry-pi-ip>:5000

Admin:

http://<raspberry-pi-ip>:5000/admin/login

Do not port-forward this app to the public internet yet.


Restart On The Pi During Manual Testing

If running manually in a terminal:

  1. Press Ctrl+C.
  2. Pull changes.
  3. Install any dependency updates.
  4. Run database setup if needed.
  5. Start the app again.

Command sequence:

git pull
.venv/bin/pip install -r requirements.txt
.venv/bin/python scripts/init_db.py
WYR_SECRET_KEY="$WYR_SECRET_KEY" .venv/bin/flask --app run run --host 0.0.0.0 --port 5000

If the Pi reboots, manually cd back into the project and run the Flask command again.

This will become easier after adding systemd.


Future Systemd Service

Future goal:

sudo systemctl start wyr
sudo systemctl stop wyr
sudo systemctl restart wyr
sudo systemctl status wyr

Benefits:

  • app can start automatically after Pi reboot
  • easier restart command
  • logs can be viewed with journalctl
  • app can run in the background without an open terminal

Example future log command:

journalctl -u wyr -f

This is not implemented yet.


Planned deployment stack:

Internet
    ↓
Cloudflare
    ↓
Nginx
    ↓
Gunicorn
    ↓
Flask
    ↓
SQLite

Deployment Workflow

On laptop:

git add .
git commit -m "feature"
git push

On Pi:

git pull
.venv/bin/pip install -r requirements.txt
.venv/bin/python scripts/init_db.py

Then manually restart the Flask command.

Later, after the systemd service exists, the Pi restart step will become:

sudo systemctl restart wyr

Immediate Roadmap

Current Status

Local refactor and Admin Dashboard V1 are implemented.


Next Step

Add cooler layout and styling:

  • improve the public voting page design
  • improve the results page design
  • improve admin dashboard styling
  • make buttons, forms, and navigation feel more polished
  • add better spacing, typography, and responsive layout

After Styling

Polish the admin flow and public behavior:

  • improve layout
  • add clearer success messages
  • add better vote duplicate handling
  • improve the empty state when no question is scheduled for today
  • add CSRF protection before public internet exposure

After Local Pi Testing

Implement:

  • systemd service for easier Pi management
  • automatic start on reboot
  • clearer production environment variables
  • basic log inspection workflow with journalctl

After Admin Polish

Implement:

  • calendar view
  • question scheduling by date
  • admin statistics

After Calendar

Implement:

  • AI-assisted question generation

Long-Term Goal

A polished daily "Would You Rather" website that:

  • runs on a Raspberry Pi 5
  • uses a custom domain
  • supports HTTPS
  • allows browser-based question management
  • supports AI-assisted content generation
  • scales beyond the initial MVP