From bcfb32fe13777bfda7d5de867186e2eb5e51bc7c Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 10:32:40 +0200 Subject: [PATCH 1/7] Fix: Application crash on unknown email entry (IndexError) --- .gitignore | 3 ++- server.py | 10 +++++++--- templates/index.html | 12 ++++++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 2cba99d87..09821350e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ lib .Python tests/ .envrc -__pycache__ \ No newline at end of file +__pycache__ +venv/ \ No newline at end of file diff --git a/server.py b/server.py index 4084baeac..ddf6650c1 100644 --- a/server.py +++ b/server.py @@ -26,9 +26,13 @@ def index(): @app.route('/showSummary',methods=['POST']) def showSummary(): - club = [club for club in clubs if club['email'] == request.form['email']][0] - return render_template('welcome.html',club=club,competitions=competitions) - + found_clubs = [club for club in clubs if club['email'] == request.form['email']] + if found_clubs: + club = found_clubs[0] + return render_template('welcome.html',club=club,competitions=competitions) + else: + flash("Sorry, that email was not found.") + return redirect(url_for('index')) @app.route('/book//') def book(competition,club): diff --git a/templates/index.html b/templates/index.html index 926526b7d..2ee8e0e12 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,6 +6,18 @@

Welcome to the GUDLFT Registration Portal!

+ + {# Add this block to display flashed messages #} + {% with messages = get_flashed_messages()%} + {% if messages %} +
    + {% for message in messages %} +
  • {{message}}
  • + {% endfor %} +
+ {% endif%} + {%endwith%} + Please enter your secretary email to continue:
From 993590c38cfc5dacba3a3c187ae640e537fcf1b5 Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 11:11:20 +0200 Subject: [PATCH 2/7] Fix: Prevent clubs from booking more places than their available points --- server.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index ddf6650c1..c31eadc73 100644 --- a/server.py +++ b/server.py @@ -50,11 +50,17 @@ def purchasePlaces(): competition = [c for c in competitions if c['name'] == request.form['competition']][0] club = [c for c in clubs if c['name'] == request.form['club']][0] placesRequired = int(request.form['places']) - competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired - flash('Great-booking complete!') + if int(club['points']) >= placesRequired: + competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired + club['points'] = str(int(club['points']) - placesRequired) + flash('Great-booking complete!') + else: + flash(f"You do not have enough points to book {placesRequired} places. You currently have {club['points']} points.") + return render_template('welcome.html', club=club, competitions=competitions) + # TODO: Add route for points display From 8cf9b4b67dd7dce54dfe6513b63b53803dcb6598 Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 11:20:07 +0200 Subject: [PATCH 3/7] Fix: Limit club bookings to a maximum of 12 places per competition --- server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server.py b/server.py index c31eadc73..54a8f5fa8 100644 --- a/server.py +++ b/server.py @@ -50,6 +50,9 @@ def purchasePlaces(): competition = [c for c in competitions if c['name'] == request.form['competition']][0] club = [c for c in clubs if c['name'] == request.form['club']][0] placesRequired = int(request.form['places']) + if placesRequired > 12: + flash("You cannot book more than 12 places per competition.") + return render_template('welcome.html', club=club, competitions=competitions) if int(club['points']) >= placesRequired: competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired club['points'] = str(int(club['points']) - placesRequired) From a3297e4ec8b02c07c781a973a2721760cbc06c7a Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 14:44:23 +0200 Subject: [PATCH 4/7] Fix: Prevent booking places in past competitions and grey out links --- competitions.json | 5 +++++ server.py | 18 +++++++++++++----- templates/welcome.html | 13 ++++++++++--- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/competitions.json b/competitions.json index 039fc61bd..e7d056945 100644 --- a/competitions.json +++ b/competitions.json @@ -9,6 +9,11 @@ "name": "Fall Classic", "date": "2020-10-22 13:30:00", "numberOfPlaces": "13" + }, + { + "name": "Summer 2025", + "date": "2025-07-22 13:30:00", + "numberOfPlaces": "10" } ] } \ No newline at end of file diff --git a/server.py b/server.py index 54a8f5fa8..5c4d97efb 100644 --- a/server.py +++ b/server.py @@ -1,5 +1,6 @@ import json from flask import Flask,render_template,request,redirect,flash,url_for +from datetime import datetime def loadClubs(): @@ -29,7 +30,7 @@ def showSummary(): found_clubs = [club for club in clubs if club['email'] == request.form['email']] if found_clubs: club = found_clubs[0] - return render_template('welcome.html',club=club,competitions=competitions) + return render_template('welcome.html',club=club,competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) else: flash("Sorry, that email was not found.") return redirect(url_for('index')) @@ -38,21 +39,28 @@ def showSummary(): def book(competition,club): foundClub = [c for c in clubs if c['name'] == club][0] foundCompetition = [c for c in competitions if c['name'] == competition][0] + competition_date = datetime.strptime(foundCompetition['date'], '%Y-%m-%d %H:%M:%S') + if competition_date < datetime.now(): + flash("This competition has already passed. Booking is not allowed.") + return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) if foundClub and foundCompetition: return render_template('booking.html',club=foundClub,competition=foundCompetition) else: flash("Something went wrong-please try again") - return render_template('welcome.html', club=club, competitions=competitions) - + return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) @app.route('/purchasePlaces',methods=['POST']) def purchasePlaces(): competition = [c for c in competitions if c['name'] == request.form['competition']][0] club = [c for c in clubs if c['name'] == request.form['club']][0] placesRequired = int(request.form['places']) + competition_date = datetime.strptime(competition['date'], '%Y-%m-%d %H:%M:%S') + if competition_date < datetime.now(): + flash("Booking for past competitions is not allowed.") + return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) if placesRequired > 12: flash("You cannot book more than 12 places per competition.") - return render_template('welcome.html', club=club, competitions=competitions) + return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) if int(club['points']) >= placesRequired: competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired club['points'] = str(int(club['points']) - placesRequired) @@ -60,7 +68,7 @@ def purchasePlaces(): else: flash(f"You do not have enough points to book {placesRequired} places. You currently have {club['points']} points.") - return render_template('welcome.html', club=club, competitions=competitions) + return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) diff --git a/templates/welcome.html b/templates/welcome.html index ff6b261a2..5db75bf46 100644 --- a/templates/welcome.html +++ b/templates/welcome.html @@ -23,9 +23,16 @@

Competitions:

{{comp['name']}}
Date: {{comp['date']}}
Number of Places: {{comp['numberOfPlaces']}} - {%if comp['numberOfPlaces']|int >0%} - Book Places - {%endif%} + {# Compare competition date string with current_date_str #} + {% if comp['date'] > current_date_str and comp['numberOfPlaces']|int > 0 %} + Book Places + {% else %} + {% if comp['date'] <= current_date_str %} + (Competition passed) + {% else %} + (No places left) + {% endif %} + {% endif %}
{% endfor %} From 7bba7af66aac56ab8b8f85777ad51119605ea803 Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 14:55:57 +0200 Subject: [PATCH 5/7] Fix: Persist club points and competition places after booking --- clubs.json | 35 +++++++++++++++++++---------------- competitions.json | 2 +- server.py | 33 +++++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/clubs.json b/clubs.json index 1d7ad1ffe..c0c23ddb3 100644 --- a/clubs.json +++ b/clubs.json @@ -1,16 +1,19 @@ -{"clubs":[ - { - "name":"Simply Lift", - "email":"john@simplylift.co", - "points":"13" - }, - { - "name":"Iron Temple", - "email": "admin@irontemple.com", - "points":"4" - }, - { "name":"She Lifts", - "email": "kate@shelifts.co.uk", - "points":"12" - } -]} \ No newline at end of file +{ + "clubs": [ + { + "name": "Simply Lift", + "email": "john@simplylift.co", + "points": "10" + }, + { + "name": "Iron Temple", + "email": "admin@irontemple.com", + "points": "4" + }, + { + "name": "She Lifts", + "email": "kate@shelifts.co.uk", + "points": "12" + } + ] +} \ No newline at end of file diff --git a/competitions.json b/competitions.json index e7d056945..cd9897b34 100644 --- a/competitions.json +++ b/competitions.json @@ -13,7 +13,7 @@ { "name": "Summer 2025", "date": "2025-07-22 13:30:00", - "numberOfPlaces": "10" + "numberOfPlaces": 7 } ] } \ No newline at end of file diff --git a/server.py b/server.py index 5c4d97efb..045937b87 100644 --- a/server.py +++ b/server.py @@ -8,12 +8,19 @@ def loadClubs(): listOfClubs = json.load(c)['clubs'] return listOfClubs - def loadCompetitions(): with open('competitions.json') as comps: listOfCompetitions = json.load(comps)['competitions'] return listOfCompetitions +def saveClubs(clubs_data): + with open('clubs.json', 'w') as c: + json.dump({"clubs": clubs_data}, c, indent=4) + +def saveCompetitions(competitions_data): + with open('competitions.json', 'w') as comps: + json.dump({"competitions": competitions_data}, comps, indent=4) + app = Flask(__name__) app.secret_key = 'something_special' @@ -28,50 +35,64 @@ def index(): @app.route('/showSummary',methods=['POST']) def showSummary(): found_clubs = [club for club in clubs if club['email'] == request.form['email']] - if found_clubs: + + if found_clubs: club = found_clubs[0] return render_template('welcome.html',club=club,competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) else: flash("Sorry, that email was not found.") return redirect(url_for('index')) + @app.route('/book//') def book(competition,club): foundClub = [c for c in clubs if c['name'] == club][0] foundCompetition = [c for c in competitions if c['name'] == competition][0] + competition_date = datetime.strptime(foundCompetition['date'], '%Y-%m-%d %H:%M:%S') if competition_date < datetime.now(): flash("This competition has already passed. Booking is not allowed.") return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + if foundClub and foundCompetition: return render_template('booking.html',club=foundClub,competition=foundCompetition) else: flash("Something went wrong-please try again") return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + @app.route('/purchasePlaces',methods=['POST']) def purchasePlaces(): competition = [c for c in competitions if c['name'] == request.form['competition']][0] club = [c for c in clubs if c['name'] == request.form['club']][0] placesRequired = int(request.form['places']) + competition_date = datetime.strptime(competition['date'], '%Y-%m-%d %H:%M:%S') if competition_date < datetime.now(): flash("Booking for past competitions is not allowed.") return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + if placesRequired > 12: flash("You cannot book more than 12 places per competition.") return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + if int(club['points']) >= placesRequired: - competition['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired - club['points'] = str(int(club['points']) - placesRequired) - flash('Great-booking complete!') + if int(competition['numberOfPlaces']) >= placesRequired: + competition['numberOfPlaces'] = int(competition['numberOfPlaces']) - placesRequired + club['points'] = str(int(club['points']) - placesRequired) + + saveClubs(clubs) + saveCompetitions(competitions) + + flash('Great-booking complete!') + else: + flash(f"Not enough places available in this competition. Only {competition['numberOfPlaces']} places left.") else: flash(f"You do not have enough points to book {placesRequired} places. You currently have {club['points']} points.") return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) - # TODO: Add route for points display From 75c7e8f1fe09704483ce63babfc8252cf1d21f40 Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 16:10:37 +0200 Subject: [PATCH 6/7] feat: Implement club points display board and session management --- clubs.json | 2 +- competitions.json | 2 +- server.py | 76 +++++++++++++++++++++++++++++++++--------- templates/points.html | 33 ++++++++++++++++++ templates/welcome.html | 7 ++-- 5 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 templates/points.html diff --git a/clubs.json b/clubs.json index c0c23ddb3..5645c8916 100644 --- a/clubs.json +++ b/clubs.json @@ -3,7 +3,7 @@ { "name": "Simply Lift", "email": "john@simplylift.co", - "points": "10" + "points": "6" }, { "name": "Iron Temple", diff --git a/competitions.json b/competitions.json index cd9897b34..7b7af81e3 100644 --- a/competitions.json +++ b/competitions.json @@ -13,7 +13,7 @@ { "name": "Summer 2025", "date": "2025-07-22 13:30:00", - "numberOfPlaces": 7 + "numberOfPlaces": 3 } ] } \ No newline at end of file diff --git a/server.py b/server.py index 045937b87..505046c24 100644 --- a/server.py +++ b/server.py @@ -1,5 +1,5 @@ import json -from flask import Flask,render_template,request,redirect,flash,url_for +from flask import Flask,render_template,request,redirect,flash,url_for,session from datetime import datetime @@ -28,53 +28,90 @@ def saveCompetitions(competitions_data): competitions = loadCompetitions() clubs = loadClubs() + @app.route('/') def index(): + if 'club_email' in session: + club_email = session['club_email'] + found_clubs = [c for c in clubs if c['email'] == club_email] + if found_clubs: + club = found_clubs[0] + return render_template('welcome.html', club=club, competitions=competitions, + current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + else: + session.pop('club_email', None) + flash("Your club's email was not found. Please log in again.") + return render_template('index.html') return render_template('index.html') + @app.route('/showSummary',methods=['POST']) def showSummary(): - found_clubs = [club for club in clubs if club['email'] == request.form['email']] + user_email = request.form['email'] + found_clubs = [club for club in clubs if club['email'] == user_email] if found_clubs: club = found_clubs[0] - return render_template('welcome.html',club=club,competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + session['club_email'] = club['email'] + return render_template('welcome.html',club=club,competitions=competitions, + current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) else: flash("Sorry, that email was not found.") + session.pop('club_email', None) return redirect(url_for('index')) -@app.route('/book//') -def book(competition,club): - foundClub = [c for c in clubs if c['name'] == club][0] - foundCompetition = [c for c in competitions if c['name'] == competition][0] +@app.route('/book//') +def book(competition_name,club_name): + if 'club_email' not in session: + flash("You need to be logged in to book places.") + return redirect(url_for('index')) + logged_in_club_email = session['club_email'] + foundClub = [c for c in clubs if c['email'] == logged_in_club_email][0] + + foundCompetition = [c for c in competitions if c['name'] == competition_name][0] + + if foundClub['name'] != club_name: + flash("Attempted to book for a different club. Action blocked.") + return redirect(url_for('index')) + competition_date = datetime.strptime(foundCompetition['date'], '%Y-%m-%d %H:%M:%S') if competition_date < datetime.now(): flash("This competition has already passed. Booking is not allowed.") - return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + return redirect(url_for('index')) if foundClub and foundCompetition: return render_template('booking.html',club=foundClub,competition=foundCompetition) else: flash("Something went wrong-please try again") - return render_template('welcome.html', club=foundClub, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + return redirect(url_for('index')) @app.route('/purchasePlaces',methods=['POST']) def purchasePlaces(): + if 'club_email' not in session: + flash("You need to be logged in to purchase places.") + return redirect(url_for('index')) + + logged_in_club_email = session['club_email'] + club = [c for c in clubs if c['email'] == logged_in_club_email][0] + competition = [c for c in competitions if c['name'] == request.form['competition']][0] - club = [c for c in clubs if c['name'] == request.form['club']][0] placesRequired = int(request.form['places']) + if club['name'] != request.form['club']: + flash("Attempted to purchase for a different club. Action blocked.") + return redirect(url_for('index')) + competition_date = datetime.strptime(competition['date'], '%Y-%m-%d %H:%M:%S') if competition_date < datetime.now(): flash("Booking for past competitions is not allowed.") - return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + return redirect(url_for('index')) if placesRequired > 12: flash("You cannot book more than 12 places per competition.") - return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + return redirect(url_for('index')) if int(club['points']) >= placesRequired: if int(competition['numberOfPlaces']) >= placesRequired: @@ -90,12 +127,21 @@ def purchasePlaces(): else: flash(f"You do not have enough points to book {placesRequired} places. You currently have {club['points']} points.") - return render_template('welcome.html', club=club, competitions=competitions, current_date_str=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) + return redirect(url_for('index')) -# TODO: Add route for points display +@app.route('/pointsDisplay') +def pointsDisplay(): + if 'club_email' not in session: + flash("You need to be logged in to view club points.") + return redirect(url_for('index')) + + sorted_clubs = sorted(clubs, key=lambda c: int(c['points']), reverse=True) + return render_template('points.html', clubs=sorted_clubs) @app.route('/logout') def logout(): - return redirect(url_for('index')) \ No newline at end of file + session.pop('club_email', None) + flash("You have been logged out.") + return redirect(url_for('index')) diff --git a/templates/points.html b/templates/points.html new file mode 100644 index 000000000..cc34d2fb3 --- /dev/null +++ b/templates/points.html @@ -0,0 +1,33 @@ + + + + + Points Display Board | GUDLFT Registration + + +

Club Points Leaderboard

+

Back to Dashboard

+ + {% if clubs %} + + + + + + + + + {% for club_item in clubs %} + + + + + {% endfor %} + +
Club NamePoints
{{ club_item['name'] }}{{ club_item['points'] }}
+ {% else %} +

No clubs found to display points.

+ {% endif %} + + + \ No newline at end of file diff --git a/templates/welcome.html b/templates/welcome.html index 5db75bf46..795f61f55 100644 --- a/templates/welcome.html +++ b/templates/welcome.html @@ -5,7 +5,9 @@ Summary | GUDLFT Registration -

Welcome, {{club['email']}}

Logout +

Welcome, {{club['email']}}

+ Logout | + View All Club Points {% with messages = get_flashed_messages()%} {% if messages %} @@ -25,7 +27,8 @@

Competitions:

Number of Places: {{comp['numberOfPlaces']}} {# Compare competition date string with current_date_str #} {% if comp['date'] > current_date_str and comp['numberOfPlaces']|int > 0 %} - Book Places + {# --- CHANGE THIS LINE --- #} + Book Places {% else %} {% if comp['date'] <= current_date_str %} (Competition passed) From eca9691a0d51c895471d440292ebb8bb84e7972d Mon Sep 17 00:00:00 2001 From: devogs Date: Wed, 2 Jul 2025 16:22:17 +0200 Subject: [PATCH 7/7] fix: Prevent booking more places than available in competition --- clubs.json | 2 +- competitions.json | 2 +- server.py | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/clubs.json b/clubs.json index 5645c8916..922fe165e 100644 --- a/clubs.json +++ b/clubs.json @@ -8,7 +8,7 @@ { "name": "Iron Temple", "email": "admin@irontemple.com", - "points": "4" + "points": "1" }, { "name": "She Lifts", diff --git a/competitions.json b/competitions.json index 7b7af81e3..0f35b3992 100644 --- a/competitions.json +++ b/competitions.json @@ -13,7 +13,7 @@ { "name": "Summer 2025", "date": "2025-07-22 13:30:00", - "numberOfPlaces": 3 + "numberOfPlaces": 0 } ] } \ No newline at end of file diff --git a/server.py b/server.py index 505046c24..0bd8abdf4 100644 --- a/server.py +++ b/server.py @@ -113,8 +113,11 @@ def purchasePlaces(): flash("You cannot book more than 12 places per competition.") return redirect(url_for('index')) + # --- BUG Fix: Prevent overbooking and incorrect point deduction --- + # Check if club has enough points AND if competition has enough places if int(club['points']) >= placesRequired: if int(competition['numberOfPlaces']) >= placesRequired: + # ONLY proceed with updates if both conditions are met competition['numberOfPlaces'] = int(competition['numberOfPlaces']) - placesRequired club['points'] = str(int(club['points']) - placesRequired) @@ -123,9 +126,12 @@ def purchasePlaces(): flash('Great-booking complete!') else: + # Not enough places available in competition flash(f"Not enough places available in this competition. Only {competition['numberOfPlaces']} places left.") else: + # Not enough points for the club flash(f"You do not have enough points to book {placesRequired} places. You currently have {club['points']} points.") + # ------------------------------------------------------------------ return redirect(url_for('index'))