# Sailing Forms A self-hosted Flask-based custom signup application for managing sailing event registrations. It provides dynamically generated registration forms based on a YAML configuration and automatically saves submitted data to Google Sheets. ## Features - **Dynamic Event Forms**: Simply define new events in `events.yaml` without touching the code. Each event gets a clean URL based on its slug (e.g., `domain.com/zomeravond`). - **Google Sheets Integration**: Automatically appends participant details to specific Google Sheet tabs in real-time. - **Public Participant Lists**: Securely displays public-safe participant data (e.g., class, sail number, boat name) at the bottom of the registration page, while keeping sensitive info (email, phone) private. - **CSRF Protection**: Form submissions are protected against Cross-Site Request Forgery. - **Dockerized Base**: Includes a `Dockerfile` and `docker-compose.yaml` utilizing `gunicorn` for simple and reliable production deployments. ## Prerequisites - **Python 3.9+** (if running locally) - **Docker and Docker Compose** (if deploying via containers) - **Google Service Account**: You need a `credentials.json` file authorized for the Google Sheets API. ## Setup & Configuration ### 1. Credentials Place your Google Service Account credentials inside a file named `credentials.json` in the root of the project. This is required for the application to access the Google Sheets via the `gspread` library. ### 2. Environment Variables Create a `.env` file in the root directory and add a secure, random string to be used as your Flask session secret key. This is required for CSRF protection and session management. ```env SECRET_KEY=your_very_secret_random_string_here ``` ### 3. Events Configuration (`events.yaml`) Define your sailing events in `events.yaml`. You can set a global `master_sheet_id` or specific `sheet_id` for each event. The `tab_name` specifies which worksheet inside the Google Sheet the data should append to. ```yaml master_sheet_id: "your_spreadsheet_id_here" # From the Google Sheets URL events: zomeravond: # The URL slug title: "Zomeravondregatta 2026" tab_name: "Zomeravond 2026" # Exact tab name in Google Sheets description: "De gezelligste avondwedstrijd van het jaar." ``` ## Running the Application ### Option A: Using Docker (Recommended for Production) The easiest way to run the service is using Docker Compose. The `docker-compose.yaml` is configured to mount your configuration and credentials without rebuilding the container. ```bash docker-compose up -d --build ``` The application will run on port `5000` (mapped to `localhost:5000`). Make sure your `credentials.json`, `.env` and `events.yaml` files are in the same directory as the `docker-compose.yaml` file so they are correctly mounted into the container as volumes. ### Option B: Local Development If you're making changes to the codebase and need to run it directly: 1. Create a virtual environment and activate it: ```bash python -m venv .venv source .venv/bin/activate ``` 2. Install the required dependencies: ```bash pip install -r requirements.txt ``` 3. Run the development server: ```bash python app.py ``` ## Automated Testing The project uses `pytest` for unit and E2E testing (located in the `tests/` directory). To run the tests locally: ```bash pytest ``` *Note: The testing suite automatically bypasses the need for a real `SECRET_KEY` and passes `TESTING_NO_APPEND=1` to prevent test submissions from cluttering the live Google Sheets.* ## Tech Stack Overview - **Backend Context:** Flask (Web Framework), Gunicorn (Production WSGI server) - **Google Sheets Integration:** `gspread` - **Data Configuration:** `PyYAML` - **Frontend Context:** Rendered via standard Jinja2 `templates/` (HTML)