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
2 changes: 2 additions & 0 deletions .flaskenv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FLASK_APP=server
FLASK_ENV=development
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ bin
include
lib
.Python
tests/
.envrc
__pycache__
__pycache__
.venv
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
click==7.1.2
colorama==0.4.6
Flask==1.1.2
iniconfig==2.3.0
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
packaging==25.0
pluggy==1.6.0
Pygments==2.19.2
pytest==8.4.2
python-dotenv==1.1.1
Werkzeug==1.0.1
34 changes: 30 additions & 4 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from flask import Flask,render_template,request,redirect,flash,url_for
from datetime import datetime


def loadClubs():
Expand All @@ -26,28 +27,53 @@ 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)
email = request.form.get('email', '')
email = email.strip()
club = next((c for c in clubs if c.get('email', '').strip() == email), None)
if not email or not club:
flash("Sorry, that email was not found, please try again.")
return render_template('index.html')
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]

competition_date = datetime.strptime(foundCompetition['date'], "%Y-%m-%d %H:%M:%S")
if competition_date < datetime.now():
flash("This competition has already taken place, booking is not allowed.")
return render_template('welcome.html', club=foundClub, competitions=competitions)

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():
MAX_BOOKING = 12
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!')
club_points = int(club['points'])

if placesRequired > club_points:
flash("Cannot book more places than club points.")
return render_template('welcome.html', club=club, competitions=competitions)


if placesRequired > MAX_BOOKING:
flash(f"Cannot book more than {MAX_BOOKING} places for this competition.")
return render_template('welcome.html', club=club, competitions=competitions)

competition['numberOfPlaces'] = int(competition['numberOfPlaces']) - placesRequired
club['points'] = club_points - placesRequired
flash('Great - booking complete!')
return render_template('welcome.html', club=club, competitions=competitions)


Expand Down
9 changes: 9 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
<body>
<h1>Welcome to the GUDLFT Registration Portal!</h1>
Please enter your secretary email to continue:
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes" style="color: red;">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form action="showSummary" method="post">
<label for="email">Email:</label>
<input type="email" name="email" id=""/>
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import sys
import pytest


"""Pytest configuration file to set up the testing environment.
This file adds the project root directory to sys.path to ensure that
"""

ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
if ROOT_DIR not in sys.path:
sys.path.insert(0, ROOT_DIR)

from server import app

@pytest.fixture
def client():
"""Flask test client fixture.
Provides a test client for the Flask application defined in server.py.
"""
app.config['TESTING'] = True
with app.test_client() as client:
yield client
47 changes: 47 additions & 0 deletions tests/unit/test_book_more_than_12_places.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import server

"""
Unit test file to check that clubs cannot book more than 12 places.

Test 1: Club A has 13 points and competition 1 has 25 places.
- Book 12 places
- status code 200
- message: "Great - booking complete!"
- competition places decreased

Test 2: Club A has 13 points and competition 1 has 25 places.
- Book 13 places
- status code 200
- message contains "Cannot book more than"
- competition places unchanged
"""


def test_book_12_places_allowed(client):
server.clubs = [{"name": "Club A", "points": "13"}]
server.competitions = [{"name": "Comp 1", "numberOfPlaces": "25"}]

response = client.post('/purchasePlaces', data={
'competition': 'Comp 1',
'club': 'Club A',
'places': '12'
})

assert response.status_code == 200
assert b"Great - booking complete!" in response.data
assert int(server.competitions[0]['numberOfPlaces']) == 13


def test_cannot_book_more_than_12_places(client):
server.clubs = [{"name": "Club A", "points": "13"}]
server.competitions = [{"name": "Comp 1", "numberOfPlaces": "25"}]

response = client.post('/purchasePlaces', data={
'competition': 'Comp 1',
'club': 'Club A',
'places': '13'
})

assert response.status_code == 200
assert b"Cannot book more than" in response.data
assert int(server.competitions[0]['numberOfPlaces']) == 25
34 changes: 34 additions & 0 deletions tests/unit/test_book_past_competition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import server
from datetime import datetime, timedelta

"""
Unit test file to check that booking is not allowed for past competitions

Test 1: A competition in the future -> user can access booking page
- status code 200
- page contains "How many places?"
Test 2: A competition in the past -> booking is refused.
- status code 200
- message contains "This competition has already taken place"
"""

def test_can_book_future_competition(client):
future_date = (datetime.now() + timedelta(days=5)).strftime("%Y-%m-%d %H:%M:%S")
server.competitions = [{"name": "Future Comp", "date": future_date, "numberOfPlaces": "10"}]
server.clubs = [{"name": "Club A", "email": "a@a.com", "points": "10"}]

response = client.get('/book/Future Comp/Club A')

assert response.status_code == 200
assert b"How many places?" in response.data


def test_cannot_book_past_competition(client):
past_date = (datetime.now() - timedelta(days=5)).strftime("%Y-%m-%d %H:%M:%S")
server.competitions = [{"name": "Old Comp", "date": past_date, "numberOfPlaces": "10"}]
server.clubs = [{"name": "Club A", "email": "a@a.com", "points": "10"}]

response = client.get('/book/Old Comp/Club A')

assert response.status_code == 200
assert b"This competition has already taken place" in response.data
44 changes: 44 additions & 0 deletions tests/unit/test_book_place_with_enough_point.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import server

"""
Unit test file for the place booking feature.
The purpose is to verify that booking places is only possible with sufficient points.

Test 1: the club has 4 points and wants to book 4 places — booking accepted :
- status code 200
- confirmation message
- remaining places updated correctly.
Test 2: the club has 4 points and wants to book 5 places — booking refused :
- status code 200
- error message
- remaining places unchanged
"""


def test_book_places_with_enough_points(client):
server.clubs = [{"name": "Club A", "points": "4"}]
server.competitions = [{"name": "Comp 1", "numberOfPlaces": "5"}]

response = client.post('/purchasePlaces', data={
'competition': 'Comp 1',
'club': 'Club A',
'places': '4'
})

assert response.status_code == 200
assert b"Great - booking complete!" in response.data
assert int(server.competitions[0]['numberOfPlaces']) == 1

def test_book_places_without_enough_points(client):
server.clubs = [{"name": "Club B", "points": "4"}]
server.competitions = [{"name": "Comp 2", "numberOfPlaces": "5"}]

response = client.post('/purchasePlaces', data={
'competition': 'Comp 2',
'club': 'Club B',
'places': '5'
})

assert response.status_code == 200
assert b"Cannot book more places than club points." in response.data
assert int(server.competitions[0]['numberOfPlaces']) == 5
27 changes: 27 additions & 0 deletions tests/unit/test_booking_decrease_club_points.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import server

"""
Unit test file to verify that club points are correctly updated after booking.

Test 1: Club has 10 points, books 3 places, points should decrease by 3.
- status code 200
- success message "Great - booking complete!"
- club points decrease by 3

"""


def test_club_points_decrease_after_booking(client):
server.clubs = [{"name": "Club A", "points": "10"}]
server.competitions = [{"name": "Comp 1", "numberOfPlaces": "20"}]

response = client.post('/purchasePlaces', data={
'competition': 'Comp 1',
'club': 'Club A',
'places': '3'
})

assert response.status_code == 200
assert b"Great - booking complete!" in response.data
assert int(server.clubs[0]['points']) == 7

27 changes: 27 additions & 0 deletions tests/unit/test_check_email_show_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pytest


"""Unit tests for the /showSummary route in server.py.
test1 : Valid email should return 200 and welcome message
test2 : Unknown email should return 200 and error message
test3 : Invalid email (empty or whitespace) should return 200 and error message

"""


def test_show_summary_with_valid_email(client):
response = client.post('/showSummary', data={'email': 'john@simplylift.co'})
assert response.status_code == 200
assert b'Welcome' in response.data


def test_show_summary_with_unknown_email(client):
response = client.post('/showSummary', data={'email': 'unknown@example.com'})
assert response.status_code == 200
assert b"Sorry, that email was not found" in response.data


def test_show_summary_with_invalid_email(client):
response = client.post('/showSummary', data={'email': ' '})
assert response.status_code == 200
assert b"Sorry, that email was not found" in response.data