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
210 changes: 151 additions & 59 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,151 @@
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
from datetime import datetime
from utils import (
loadClubs,
loadCompetitions,
getClubByEmail,
getCompetitionByName,
getClubByName,
getClubPoints,
getCompetitionPlaces,
isCompetitionBookable,
isBookingValid,
)

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

def buildCompetitionsView(competitions):
now = datetime.now()
competitions_view = []

for competition in competitions:
competition_view = dict(competition)
competition_view['canBook'] = isCompetitionBookable(competition, now=now)
competitions_view.append(competition_view)

return competitions_view

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

@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 redirect(url_for('index'))

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

flash("Unfortunately, the email you entered was not found.")
return redirect(url_for('index'))


@app.route('/book/<competition>/<club>')
def book(competition,club):
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 redirect(url_for('index'))

found_competition = getCompetitionByName(competition, available_competitions)
found_club = getClubByName(club, available_clubs)
#on peut forcer via l'URL une compétition dans le passé ou une compétition sans places disponibles, mais on ne peut pas forcer une compétition ou un club qui n'existent pas

if found_club is None:
flash("Invalid booking URL. Please check the club name.")
return redirect(url_for('index'))

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']

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

competition = getCompetitionByName(request.form['competition'], available_competitions)
club = getClubByName(request.form['club'], available_clubs)
placesRequired = int(request.form['places'])

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

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

if validation_errors:
for error in validation_errors:
flash(error)
return render_template('booking.html', club=club, competition=competition)

competition['numberOfPlaces'] = str(getCompetitionPlaces(competition) - placesRequired)
flash(f'Booking complete: {placesRequired} places purchased.')
return render_template(
'welcome.html',
club=club,
competitions=buildCompetitionsView(available_competitions),
)

# TODO: Add route for points display


@app.route('/logout')
def logout():
return redirect(url_for('index'))

return app


app = create_app()
15 changes: 13 additions & 2 deletions templates/booking.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@
<title>Booking for {{competition['name']}} || GUDLFT</title>
</head>
<body>
<h1> GUDLFT Booking</h1>
{% 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>
9 changes: 9 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
</head>
<body>
<h1>Welcome to the GUDLFT Registration Portal!</h1>
{% 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
4 changes: 2 additions & 2 deletions templates/welcome.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>Summary | GUDLFT Registration</title>
</head>
<body>
<h2>Welcome, {{club['email']}} </h2><a href="{{url_for('logout')}}">Logout</a>
<h2>Welcome, {{club['name']}} </h2><a href="{{url_for('logout')}}">Logout</a>

{% with messages = get_flashed_messages()%}
{% if messages %}
Expand All @@ -23,7 +23,7 @@ <h3>Competitions:</h3>
{{comp['name']}}<br />
Date: {{comp['date']}}</br>
Number of Places: {{comp['numberOfPlaces']}}
{%if comp['numberOfPlaces']|int >0%}
{%if comp['canBook'] %}
<a href="{{ url_for('book',competition=comp['name'],club=club['name']) }}">Book Places</a>
{%endif%}
</li>
Expand Down
Loading