Add decided badge to idea cards
Tour Voting App
Tour Voting App is a full-stack web application for planning tours, proposing ideas, and voting on those ideas. It consists of:
- Backend API built with FastAPI
- Data persistence using per-tour JSON files in a configurable directory, with concurrency safety via file locks
- Frontend: Single-page HTML/JavaScript application that consumes the API
- Helper scripts for local development or Supervisor-based deployment
Table of Contents
- Features
- Tech Stack
- Prerequisites
- Installation
- Configuration
- Development
- Running in Production
- Frontend Usage
- API Reference
- Models
- Endpoints
- Directory Structure
- Contributing
- License
Features
- Create and manage tours with metadata (name, start/end dates, description)
- Add multiple ideas per tour, each with its own name, description, and optional time window
- Vote on ideas by recording unique voter names
- No external database: JSON file–based persistence
- Concurrency-safe via file locking
- Minimal static HTML/JavaScript frontend for user interaction
- Helper scripts for local development or Supervisor deployment
Tech Stack
- Backend: Python 3.10+, FastAPI, Uvicorn, Pydantic, filelock
- Frontend: HTML, CSS, JavaScript (Fetch API)
- Process Control (optional): Supervisor
- Data storage: JSON files, one per tour
Prerequisites
- Python 3.10 or higher
- pip (Python package installer)
- (Optional) Supervisor for process management
Installation
-
Clone the repository git clone https://github.com/your-organization/tour-voting-app.git cd tour-voting-app
-
Create virtual environment and install dependencies python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt
If
requirements.txtis not provided, install directly: pip install fastapi uvicorn pydantic filelock
Configuration
Configure via environment variables or command-line flags:
- DATA_DIR: directory to store tour JSON files (default:
./data) - HOST: address to bind the API (default:
0.0.0.0) - PORT: port for the API (default:
8000)
Example using environment variables:
export DATA_DIR=./data
export HOST=0.0.0.0
export PORT=8000
Or via flags:
python server.py --data-dir ./data --host 0.0.0.0 --port 8000
Development
-
Activate your virtual environment.
-
Start the server in reload mode:
uvicorn server:app --reload --host 0.0.0.0 --port 8000 -
Open
index.htmlin your browser (or serve it via a local HTTP server to avoid CORS issues).
Running in Production
Use the provided service.sh script and Supervisor config:
service.sh
#!/usr/bin/env bash
cd /path/to/tour-voting-app || exit 1
exec uvicorn server:app \
--host 0.0.0.0 \
--port 3002 \
--loop uvloop \
--workers 2 \
--access-log \
--log-level info
tour.ini (Supervisor)
[program:tour_voting_app]
command=/path/to/service.sh
directory=/path/to/tour-voting-app
autostart=true
autorestart=true
stderr_logfile=/var/log/tour_voting_app.err.log
stdout_logfile=/var/log/tour_voting_app.out.log
Frontend Usage
The frontend is a single index.html file that interacts with the API. By default, API_URL is set to http://localhost:8000/tour/v1. To point it at a remote server, edit the API_URL constant near the top of index.html.
API Reference
Base path: /tour/v1
Models
Tour
{
"id": "string",
"name": "string",
"description": "string",
"startDate": "ISO-8601 timestamp",
"endDate": "ISO-8601 timestamp",
"createdAt": "ISO-8601 timestamp",
"ideas": [ Idea ]
}
Idea
{
"id": "string",
"name": "string",
"description": "string",
"startTime": "ISO-8601 timestamp | null",
"endTime": "ISO-8601 timestamp | null",
"voters": [ "string" ]
}
Endpoints
1. List Tours
-
Method: GET
-
URL:
/tours -
Response:
200 OK[ { Tour }, ... ]
2. Create a Tour
-
Method: POST
-
URL:
/tours -
Request Body:
{ "name": "Europe Explorer", "description": "A tour across Europe", "startDate": "2025-07-01T00:00:00Z", "endDate": "2025-07-15T00:00:00Z" } -
Response:
201 Created{ Tour }
3. Get Tour Details
-
Method: GET
-
URL:
/tours/{tour_id} -
Path Parameters:
tour_id(string): ID of the tour
-
Response:
200 OK{ Tour } -
Error:
404 Not Foundif tour does not exist.
4. Add an Idea to a Tour
-
Method: POST
-
URL:
/tours/{tour_id}/ideas -
Path Parameters:
tour_id(string): ID of the tour
-
Request Body:
{ "name": "Visit the Louvre", "description": "Guided museum tour", "startTime": "2025-07-03T10:00:00Z", "endTime": "2025-07-03T14:00:00Z" } -
Response:
201 Created{ Idea } -
Error:
404 Not Foundif tour does not exist.
5. Vote for an Idea
-
Method: POST
-
URL:
/tours/{tour_id}/ideas/{idea_id}/vote -
Path Parameters:
tour_id(string): ID of the touridea_id(string): ID of the idea
-
Request Body:
{ "voterName": "alice@example.com" } -
Response:
200 OK{ Idea } // Updated idea with new voter -
Errors:
404 Not Foundif tour or idea does not exist.400 Bad Requestif voter name is missing or already voted.
Directory Structure
tour-voting-app/
├── server.py # FastAPI application
├── index.html # Static frontend
├── service.sh # Startup helper script
├── tour.ini # Supervisor config
├── data/ # JSON files for tours (runtime)
├── requirements.txt # Python dependencies (optional)
└── README.md # Project documentation
Contributing
- Fork the repository.
- Create a feature branch: git checkout -b feature/my-feature
- Commit your changes: git commit -m "Add my feature"
- Push to your branch: git push origin feature/my-feature
- Open a pull request.
License
This project is licensed under the WTFPL License.
.gitignore
Byte-compiled files
pycache/ *.py[cod] *$py.class
Virtual environments
.env/ .venv/ env/ venv/
Distribution
build/ dist/ *.egg-info/ *.egg
Installer logs
pip-log.txt pip-delete-this-directory.txt
Python coverage
htmlcov/ .tox/ .coverage .pytest_cache/ .nox/
Logs
*.log
IDEs
.vscode/ .idea/
macOS
.DS_Store
Linux
*~
Data files and locks
data/ *.json *.lock