commit f83a061e396d0c1acc2bee1997a3194ff5f412a0 Author: Feiko Wielsma Date: Wed Nov 26 17:11:44 2025 +0100 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7c52a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +credentials.json \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3e8dd74 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.9-slim + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Expose the port Flask runs on +EXPOSE 5000 + +# Run with Gunicorn (Production Server) +CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"] \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..952497f --- /dev/null +++ b/app.py @@ -0,0 +1,83 @@ +import os +import yaml +import gspread +from flask import Flask, render_template, request, redirect, url_for, abort +from datetime import datetime + +app = Flask(__name__) + +# Load Configuration +def load_config(): + with open('events.yaml', 'r') as f: + return yaml.safe_load(f) + +# Connect to Google Sheets +def get_google_sheet(sheet_id): + gc = gspread.service_account(filename='credentials.json') + try: + sh = gc.open_by_key(sheet_id) + return sh.sheet1 # Assumes data goes into the first tab + except Exception as e: + print(f"Error connecting to Google Sheet: {e}") + return None + +@app.route('/') +def home(): + # List all available active events + config = load_config() + events = config.get('events', {}) + return render_template('home.html', events=events) + +# Dynamic Route for any event defined in YAML +@app.route('/', methods=['GET', 'POST']) +def event_form(event_slug): + config = load_config() + events = config.get('events', {}) + + # Check if event exists in config + if event_slug not in events: + abort(404) + + event_data = events[event_slug] + + if request.method == 'POST': + # 1. Collect Form Data + form_data = [ + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), # Timestamp + request.form.get('klasse'), + request.form.get('zeilnummer'), + request.form.get('bootnaam'), + request.form.get('boottype'), + # Checkboxes (Join them or separate columns? Let's join for simplicity) + ", ".join([k for k in ['genua', 'rolfok', 'spinaker', 'halfwinder', 'genaker', 'dacron'] if k in request.form]), + request.form.get('schroef'), + request.form.get('naam'), + request.form.get('straat'), + request.form.get('postcode'), + request.form.get('plaats'), + request.form.get('land'), + request.form.get('telefoonmobiel'), + request.form.get('email'), + request.form.get('startlicentienummer'), + request.form.get('vereniging'), + request.form.get('opmerkingen') + ] + + # 2. Push to Google Sheet + sheet = get_google_sheet(event_data['sheet_id']) + if sheet: + sheet.append_row(form_data) + return redirect(url_for('success', event_slug=event_slug)) + else: + return "Error: Could not connect to Google Sheet. Check server logs." + + return render_template('form.html', event=event_data, slug=event_slug) + +@app.route('//success') +def success(event_slug): + config = load_config() + event_data = config['events'].get(event_slug) + return render_template('success.html', event=event_data) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..872493c --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,16 @@ +version: '3.8' +services: + sailing-forms: + build: . + container_name: sailing_forms + restart: unless-stopped + volumes: + # Mount the config file so you can edit it without rebuilding + - ./events.yaml:/app/events.yaml + # Mount credentials securely + - ./credentials.json:/app/credentials.json + + +networks: + npm_network: + external: true \ No newline at end of file diff --git a/events.yaml b/events.yaml new file mode 100644 index 0000000..b68d804 --- /dev/null +++ b/events.yaml @@ -0,0 +1,18 @@ +# Define as many events as you want here. +# The key (e.g., 'zomeravond') becomes the URL: domain.com/zomeravond + +events: + zomeravond: + title: "Inschrijving Zomeravondregatta" + sheet_id: "1k-eTke2GGmcMtq2-acvWPvjgfDeUHBsR0_Bd_tAbLx0" + description: "De gezelligste avondwedstrijd van het jaar." + + papklokken: + title: "Papklokkenrace 2025" + sheet_id: "1k-eTke2GGmcMtq2-acvWPvjgfDeUHBsR0_Bd_tAbLx0" + description: "Sluit het seizoen af in stijl." + + winterwedstrijd: + title: "Winter Bokaal" + sheet_id: "1k-eTke2GGmcMtq2-acvWPvjgfDeUHBsR0_Bd_tAbLx0" + description: "Alleen voor de echte bikkels." \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..867cbe9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +Flask==3.0.0 +gspread==5.12.0 +PyYAML==6.0.1 +gunicorn==21.2.0 \ No newline at end of file diff --git a/templates/form.html b/templates/form.html new file mode 100644 index 0000000..e89921a --- /dev/null +++ b/templates/form.html @@ -0,0 +1,140 @@ + + + + + + {{ event.title }} + + + + + +
+
+

{{ event.title }}

+

{{ event.description }}

+ +
+ +

De Boot

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +

Zeilvoering

+
+ {% for sail in ['Genua', 'Rolfok', 'Spinaker', 'Halfwinder', 'Genaker', 'Alleen dacron'] %} +
+
+ + +
+
+ {% endfor %} +
+ + +

Motor / Schroef

+
+ +
+ + +

Schipper / Eigenaar

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + +
+ + +
+ Voorwaarden:
+ Deelnemers nemen deel aan dit evenement voor geheel eigen risico. + De organiserende vereniging kan nimmer aansprakelijk gesteld worden. + (Zie wedstrijdreglement voor volledige tekst). +
+ +
+ + +
+ +
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/templates/participants.html b/templates/participants.html new file mode 100644 index 0000000..1902b0e --- /dev/null +++ b/templates/participants.html @@ -0,0 +1,43 @@ + + + + + Deelnemerslijst + + + +
+

Huidige Deelnemers

+ ← Terug naar inschrijving + + + + + + + + + + + + + + {% for boat in boats %} + + + + + + + + + {% else %} + + + + {% endfor %} + +
KlasseZeilnummerBootnaamTypeSchipperVereniging
{{ boat.klasse }}{{ boat.zeilnummer }}{{ boat.bootnaam }}{{ boat.boottype }}{{ boat.naam }}{{ boat.vereniging }}
Nog geen inschrijvingen.
+
+ + \ No newline at end of file diff --git a/templates/success.html b/templates/success.html new file mode 100644 index 0000000..7994e9e --- /dev/null +++ b/templates/success.html @@ -0,0 +1,20 @@ + + + + + + Bedankt! + + + +
+
+

Bedankt!

+

Je inschrijving voor {{ event.title }} is ontvangen.

+

De gegevens zijn opgeslagen.

+
+ Bekijk deelnemers (Google Sheets) +
+
+ + \ No newline at end of file