diff --git a/.gitignore b/.gitignore index c7c52a9..f7b4cec 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -credentials.json \ No newline at end of file +credentials.json +.venv/ +*.pyc +__pycache__/ +.env \ No newline at end of file diff --git a/app.py b/app.py index 3f47f55..f5b6961 100644 --- a/app.py +++ b/app.py @@ -2,11 +2,31 @@ import os import yaml import gspread import secrets +import logging +import sys from flask import Flask, render_template, request, redirect, url_for, abort, session from datetime import datetime +# Configure logging for Docker/Portainer +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[logging.StreamHandler(sys.stdout)] +) +logger = logging.getLogger(__name__) + app = Flask(__name__) -app.secret_key = os.environ.get('SECRET_KEY', secrets.token_hex(32)) + +# Fail-fast secret requirement (allow bypass for pytest testing) +secret_key = os.environ.get('SECRET_KEY') +if not secret_key: + if os.environ.get('TESTING_NO_APPEND') or 'pytest' in sys.modules: + secret_key = 'test_secret_bypassed' + else: + logger.critical("No SECRET_KEY set! Exiting. Set it in .env or Portainer Stack secrets.") + sys.exit(1) + +app.secret_key = secret_key def generate_csrf_token(): if '_csrf_token' not in session: @@ -40,7 +60,7 @@ def get_google_sheet(sheet_id, tab_name=None): # Default to first tab return sh.sheet1 except Exception as e: - print(f"Error connecting to Google Sheet (ID: {sheet_id}, Tab: {tab_name}): {e}") + logger.error(f"Error connecting to Google Sheet (ID: {sheet_id}, Tab: {tab_name}): {e}") return None # NEW: Fetch and filter participants for public display @@ -82,7 +102,7 @@ def get_public_participants(sheet_id, tab_name=None): return public_list except Exception as e: - print(f"Error fetching participants: {e}") + logger.error(f"Error fetching participants: {e}") return [] @app.route('/') @@ -146,9 +166,15 @@ def event_form(event_slug): sheet = get_google_sheet(sheet_id, tab_name) if sheet: if not os.environ.get('TESTING_NO_APPEND'): - sheet.append_row(form_data) + try: + sheet.append_row(form_data) + logger.info(f"Successfully appended registration for {form_data[7]} to {tab_name}") + except Exception as e: + logger.error(f"Failed to append row to {tab_name}: {e}") + return f"Error appending data. Try again later." return redirect(url_for('success', event_slug=event_slug)) else: + logger.error(f"Could not connect to tab '{tab_name}' during POST.") return f"Error: Could not connect to Google Sheet Tab '{tab_name}'. Check server logs." # GET Request: Fetch participants to show at bottom of form diff --git a/docker-compose.yaml b/docker-compose.yaml index 9cc027f..c9c47e2 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,6 +4,8 @@ services: build: . container_name: sailing_forms restart: unless-stopped + env_file: + - .env ports: - "5000:5000" volumes: diff --git a/tests/test_e2e.py b/tests/test_e2e.py index 747a3b8..e6b483c 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -1,29 +1,37 @@ import os import re import pytest -from playwright.sync_api import Page, expect +from playwright.sync_api import sync_playwright, expect -def test_homepage_has_title(page: Page): - page.goto("http://localhost:5000/") - expect(page).to_have_title(re.compile("Zeilwedstrijden")) +def test_homepage_has_title(): + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + page.goto("http://localhost:5000/") + expect(page).to_have_title(re.compile("Zeilwedstrijden")) + browser.close() -def test_submission_flow(page: Page): - page.goto("http://localhost:5000/zomeravond") - - # Fill required fields - page.select_option("select[name='klasse']", label="Kajuitklasse") - page.fill("input[name='zeilnummer']", "42") - page.fill("input[name='bootnaam']", "Vliegende Hollander") - page.fill("input[name='naam']", "Hendrik Test") - page.fill("input[name='telefoonmobiel']", "0612345678") - page.fill("input[name='email']", "hendrik@example.com") - - # Accept terms - page.check("input#terms") - - # Submit - page.click("button[type='submit']") - - # Expect success redirect - expect(page).to_have_url(re.compile(r".*/zomeravond/success")) - expect(page.locator("h1")).to_have_text("Bedankt!") +def test_submission_flow(): + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page() + page.goto("http://localhost:5000/zomeravond") + + # Fill required fields + page.select_option("select[name='klasse']", label="Kajuitklasse") + page.fill("input[name='zeilnummer']", "42") + page.fill("input[name='bootnaam']", "Vliegende Hollander") + page.fill("input[name='naam']", "Hendrik Test") + page.fill("input[name='telefoonmobiel']", "0612345678") + page.fill("input[name='email']", "hendrik@example.com") + + # Accept terms + page.check("input#terms") + + # Submit + page.click("button[type='submit']") + + # Expect success redirect + expect(page).to_have_url(re.compile(r".*/zomeravond/success")) + expect(page.locator("h1")).to_have_text("Bedankt!") + browser.close()