Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .DS_Store
Binary file not shown.
Binary file added .coverage
Binary file not shown.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run]
omit = tests/*
4 changes: 4 additions & 0 deletions CACHEDIR.TAG
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by Python virtualenv.
# For information about cache directory tags, see:
# https://bford.info/cachedir/
4 changes: 2 additions & 2 deletions competitions.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"competitions": [
{
"name": "Spring Festival",
"date": "2020-03-27 10:00:00",
"date": "2026-03-27 10:00:00",
"numberOfPlaces": "25"
},
{
"name": "Fall Classic",
"date": "2020-10-22 13:30:00",
"date": "2026-10-22 13:30:00",
"numberOfPlaces": "13"
}
]
Expand Down
7 changes: 7 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[pytest]
filterwarnings =
ignore:ast\.Str is deprecated and will be removed in Python 3\.14; use ast\.Constant instead:DeprecationWarning
ignore:Constant\.__init__ got an unexpected keyword argument 's'\. Support for arbitrary keyword arguments is deprecated and will be removed in Python 3\.15\.:DeprecationWarning
ignore:Attribute s is deprecated and will be removed in Python 3\.14; use value instead:DeprecationWarning
ignore:Constant\.__init__ missing 1 required positional argument.*:DeprecationWarning
ignore:datetime\.datetime\.utcfromtimestamp\(\) is deprecated and scheduled for removal in a future version\..*:DeprecationWarning
11 changes: 11 additions & 0 deletions pyvenv.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
home = /Library/Frameworks/Python.framework/Versions/3.13/bin
implementation = CPython
version_info = 3.13.3.final.0
version = 3.13.3
executable = /Library/Frameworks/Python.framework/Versions/3.13/bin/python3.13
command = /Library/Frameworks/Python.framework/Versions/3.13/bin/python3.13 -m virtualenv /Users/quentintellier/Documents/2 - Python Projects/P11_2 - GÜDLFT/Python_Testing
virtualenv = 21.2.4
include-system-site-packages = false
base-prefix = /Library/Frameworks/Python.framework/Versions/3.13
base-exec-prefix = /Library/Frameworks/Python.framework/Versions/3.13
base-executable = /Library/Frameworks/Python.framework/Versions/3.13/bin/python3.13
251 changes: 192 additions & 59 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,192 @@
import json
from flask import Flask,render_template,request,redirect,flash,url_for


def loadClubs():
with open('clubs.json') as c:
listOfClubs = json.load(c)['clubs']
return listOfClubs


def loadCompetitions():
with open('competitions.json') as comps:
listOfCompetitions = json.load(comps)['competitions']
return listOfCompetitions


app = Flask(__name__)
app.secret_key = 'something_special'

competitions = loadCompetitions()
clubs = loadClubs()

@app.route('/')
def index():
return render_template('index.html')

@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)


@app.route('/book/<competition>/<club>')
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]
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)


@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['numberOfPlaces'] = int(competition['numberOfPlaces'])-placesRequired
flash('Great-booking complete!')
return render_template('welcome.html', club=club, competitions=competitions)


# TODO: Add route for points display


@app.route('/logout')
def logout():
return redirect(url_for('index'))
from flask import Flask,render_template,request,redirect,flash,url_for,current_app,session
from utils import (
loadClubs,
loadCompetitions,
getClubByEmail,
getCompetitionByName,
getClubByName,
getClubPoints,
getCompetitionPlaces,
isCompetitionBookable,
isBookingValid,
updateClubPoints,
updateCompetitionPlaces,
getBookingKey,
requireLogin,
logoutAndRedirect,
buildCompetitionsView,
clearSessionKeepingFlashes,
)

def create_app(config=None, clubs=None, competitions=None):
app = Flask(__name__)
app.secret_key = 'something_special'
if config:
app.config.update(config)

app.config['COMPETITIONS'] = competitions if competitions is not None else loadCompetitions()
app.config['CLUBS'] = clubs if clubs is not None else loadClubs()
app.config.setdefault('BOOKINGS_BY_CLUB_COMPETITION', {})

@app.route('/')
def index():
clearSessionKeepingFlashes()
return render_template('index.html')

@app.route('/dashboard')
def dashboard():
club = requireLogin()
if club is None:
return logoutAndRedirect()
available_competitions = current_app.config['COMPETITIONS']
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

@app.route('/pointsBoard')
def pointsBoard():
available_clubs = current_app.config['CLUBS']

if available_clubs is None:
flash("Error loading clubs data.")
return redirect(url_for('index'))

return render_template('points_board.html', clubs=available_clubs)

@app.route('/showSummary',methods=['POST'])
def showSummary():
available_clubs = current_app.config['CLUBS']
available_competitions = current_app.config['COMPETITIONS']

if available_clubs is None or available_competitions is None:
flash("Error loading clubs or competitions data.")
return logoutAndRedirect()

club = getClubByEmail(request.form['email'], available_clubs)
if club:
session['club_email'] = club['email']
session['club_name'] = club['name']
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

flash("Unfortunately, the email you entered was not found.")
return logoutAndRedirect()


@app.route('/book/<competition>/<club>')
def book(competition,club):
available_clubs = current_app.config['CLUBS']
available_competitions = current_app.config['COMPETITIONS']
logged_club = requireLogin()

if logged_club is None:
return logoutAndRedirect()

if available_clubs is None or available_competitions is None:
flash("Error loading clubs or competitions data.")
return logoutAndRedirect()

found_competition = getCompetitionByName(competition, available_competitions)
found_club = getClubByName(club, available_clubs)

if found_club is None or found_club['name'] != logged_club['name']:
flash("Invalid booking URL. Please check the club name.")
return render_template(
'welcome.html',
club=logged_club,
competitions=buildCompetitionsView(available_competitions),
)

if found_competition is None:
flash("Invalid booking URL. Please check the competition name.")
return render_template(
'welcome.html',
club=found_club,
competitions=buildCompetitionsView(available_competitions),
)

if not isCompetitionBookable(found_competition):
flash("This competition is no longer open for booking.")
return render_template(
'welcome.html',
club=found_club,
competitions=buildCompetitionsView(available_competitions),
)



return render_template('booking.html', club=found_club, competition=found_competition)

@app.route('/purchasePlaces',methods=['POST'])
def purchasePlaces():
available_clubs = current_app.config['CLUBS']
available_competitions = current_app.config['COMPETITIONS']
logged_club = requireLogin()

if logged_club is None:
return logoutAndRedirect()

if available_clubs is None or available_competitions is None:
flash("Error loading clubs or competitions data.")
return logoutAndRedirect()

competition = getCompetitionByName(request.form['competition'], available_competitions)
club = logged_club
placesRequired = int(request.form['places'])
booking_key = getBookingKey(club['name'], request.form['competition'])
places_already_booked = current_app.config['BOOKINGS_BY_CLUB_COMPETITION'].get(booking_key, 0)

if competition is None or club is None:
flash("Invalid booking request. Please check the club and competition names.")
return logoutAndRedirect()

if not isCompetitionBookable(competition):
flash("This competition is no longer open for booking.")
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

validation_errors = isBookingValid(
getClubPoints(club),
getCompetitionPlaces(competition),
placesRequired,
placesAlreadyBooked=places_already_booked,
)

if validation_errors:
for error in validation_errors:
flash(error)
#return render_template('booking.html', club=club, competition=competition)
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

# Update club points and competition places
updateClubPoints(club, placesRequired)
updateCompetitionPlaces(competition, placesRequired)
current_app.config['BOOKINGS_BY_CLUB_COMPETITION'][booking_key] = places_already_booked + placesRequired
flash(f'Booking complete: {placesRequired} places purchased.')
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

@app.route('/logout')
def logout():
flash("You have been logged out.")
return logoutAndRedirect()

return app


app = create_app()
25 changes: 22 additions & 3 deletions templates/booking.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,32 @@
<title>Booking for {{competition['name']}} || GUDLFT</title>
</head>
<body>
<h1> GUDLFT Booking</h1>
{% if session.get('club_name') %}
<p>Logged in as {{ session.get('club_name') }}.</p>
{% endif %}
<nav>
<ul>
<li><a href="{{ url_for('pointsBoard') }}">Points Board</a></li>
<li><a href="{{url_for('logout')}}">Logout</a></li>
</ul>
</nav>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h2>{{competition['name']}}</h2>
Places available: {{competition['numberOfPlaces']}}
{% set max_places = [12, club['points']|int, competition['numberOfPlaces']|int] | min %}
<form action="/purchasePlaces" method="post">
<input type="hidden" name="club" value="{{club['name']}}">
<input type="hidden" name="competition" value="{{competition['name']}}">
<label for="places">How many places?</label><input type="number" name="places" id=""/>
<button type="submit">Book</button>
<label for="places">How many places?</label><input type="number" name="places" id="places" min="1" max="{{ max_places }}" step="1" required/>
<button type="submit" {% if max_places < 1 %}disabled{% endif %}>Book</button>
</form>
</body>
</html>
17 changes: 17 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@
</head>
<body>
<h1>Welcome to the GUDLFT Registration Portal!</h1>
<nav>
<ul>
<li><a href="{{ url_for('pointsBoard') }}">Points Board</a></li>
</ul>
</nav>
{% if session.get('club_name') %}
<p>Logged in as {{ session.get('club_name') }}.</p>
{% endif %}
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
Please enter your secretary email to continue:
<form action="showSummary" method="post">
<label for="email">Email:</label>
Expand Down
36 changes: 36 additions & 0 deletions templates/points_board.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Public Points Board | GUDLFT Registration</title>
</head>
<body>
<h1>Public Points Board</h1>
<nav>
<ul>
{% if session.get('club_name') %}
<li><a href="{{ url_for('dashboard') }}">Back to Dashboard</a></li>
<li><a href="{{url_for('logout')}}">Logout</a></li>
{% else %}
<li><a href="{{ url_for('index') }}">Back to Login</a></li>
{% endif %}
</ul>
</nav>
<table>
<thead>
<tr>
<th>Club</th>
<th>Available Points</th>
</tr>
</thead>
<tbody>
{% for club in clubs %}
<tr>
<td>{{ club['name'] }}</td>
<td>{{ club['points'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
Loading