Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ lib
.Python
tests/
.envrc
__pycache__
__pycache__
htmlcov/
.coverage

502 changes: 502 additions & 0 deletions .venv/Scripts/Activate.ps1

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions .venv/Scripts/activate
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# This file must be used with "source bin/activate" *from bash*
# You cannot run it directly

deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi

# Call hash to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
hash -r 2> /dev/null

if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi

unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}

# unset irrelevant variables
deactivate nondestructive

# on Windows, a path can contain colons and backslashes and has to be converted:
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
# transform D:\path\to\venv to /d/path/to/venv on MSYS
# and to /cygdrive/d/path/to/venv on Cygwin
export VIRTUAL_ENV=$(cygpath "C:\Users\Lénovo P70\Desktop\projet 11\Python_Testing\.venv")
else
# use the path as-is
export VIRTUAL_ENV="C:\Users\Lénovo P70\Desktop\projet 11\Python_Testing\.venv"
fi

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/Scripts:$PATH"
export PATH

# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi

if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1="(.venv) ${PS1:-}"
export PS1
VIRTUAL_ENV_PROMPT="(.venv) "
export VIRTUAL_ENV_PROMPT
fi

# Call hash to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
hash -r 2> /dev/null
34 changes: 34 additions & 0 deletions .venv/Scripts/activate.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@echo off

rem This file is UTF-8 encoded, so we need to update the current code page while executing it
for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do (
set _OLD_CODEPAGE=%%a
)
if defined _OLD_CODEPAGE (
"%SystemRoot%\System32\chcp.com" 65001 > nul
)

set VIRTUAL_ENV=C:\Users\Lénovo P70\Desktop\projet 11\Python_Testing\.venv

if not defined PROMPT set PROMPT=$P$G

if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT%
if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%

set _OLD_VIRTUAL_PROMPT=%PROMPT%
set PROMPT=(.venv) %PROMPT%

if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%
set PYTHONHOME=

if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH%
if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH%

set PATH=%VIRTUAL_ENV%\Scripts;%PATH%
set VIRTUAL_ENV_PROMPT=(.venv)

:END
if defined _OLD_CODEPAGE (
"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul
set _OLD_CODEPAGE=
)
Binary file added .venv/Scripts/coverage-3.12.exe
Binary file not shown.
Binary file added .venv/Scripts/coverage.exe
Binary file not shown.
Binary file added .venv/Scripts/coverage3.exe
Binary file not shown.
22 changes: 22 additions & 0 deletions .venv/Scripts/deactivate.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@echo off

if defined _OLD_VIRTUAL_PROMPT (
set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
)
set _OLD_VIRTUAL_PROMPT=

if defined _OLD_VIRTUAL_PYTHONHOME (
set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%"
set _OLD_VIRTUAL_PYTHONHOME=
)

if defined _OLD_VIRTUAL_PATH (
set "PATH=%_OLD_VIRTUAL_PATH%"
)

set _OLD_VIRTUAL_PATH=

set VIRTUAL_ENV=
set VIRTUAL_ENV_PROMPT=

:END
Binary file added .venv/Scripts/flask.exe
Binary file not shown.
Binary file added .venv/Scripts/pip.exe
Binary file not shown.
Binary file added .venv/Scripts/pip3.12.exe
Binary file not shown.
Binary file added .venv/Scripts/pip3.exe
Binary file not shown.
Binary file added .venv/Scripts/py.test.exe
Binary file not shown.
Binary file added .venv/Scripts/pygmentize.exe
Binary file not shown.
Binary file added .venv/Scripts/pytest.exe
Binary file not shown.
Binary file added .venv/Scripts/python.exe
Binary file not shown.
Binary file added .venv/Scripts/pythonw.exe
Binary file not shown.
5 changes: 5 additions & 0 deletions .venv/pyvenv.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
home = C:\Users\Lénovo P70\AppData\Local\Programs\Python\Python312
include-system-site-packages = false
version = 3.12.6
executable = C:\Users\Lénovo P70\AppData\Local\Programs\Python\Python312\python.exe
command = C:\Users\Lénovo P70\AppData\Local\Programs\Python\Python312\python.exe -m venv C:\Users\Lénovo P70\Desktop\projet 11\Python_Testing\.venv
79 changes: 66 additions & 13 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ 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)
# Correction BUG 1 "email"
email = request.form.get('email')
club = next((club for club in clubs if club['email'] == email), None)

if club:
return render_template('welcome.html', club=club, competitions=competitions)
else:
flash("Sorry, that email wasn't found.")
return redirect(url_for('index'))


@app.route('/book/<competition>/<club>')
Expand All @@ -41,19 +48,65 @@ def book(competition,club):
return render_template('welcome.html', club=club, competitions=competitions)


@app.route('/purchasePlaces',methods=['POST'])
@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
try:
# Trouver la compétition et le club
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]

# Correction BUG 6: Vérification de la disponibilité des places
# Vérification du champ vide
if not request.form['places'].strip():
flash("You must specify a number of places (at least 1).")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

try:
# Validation du type de donnée (entier valide)
placesRequired = int(request.form['places'])
except ValueError:
flash("You must enter a valid integer number.")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

# Vérification valeur positive (négatif ou nul)
if placesRequired <= 0:
flash("The number of places must be a positive integer (1 or more).")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

competition_places = int(competition['numberOfPlaces'])
club_points = int(club['points'])

# Vérification capacité de la compétition
if placesRequired > competition_places:
flash(f"Error: Only {competition_places} places available.")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

# Correction BUG 4: Limite de 12 places par club
if placesRequired > 12:
flash("Error: You cannot book more than 12 places.")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

# Correction BUG 5 :Vérifier si le club a suffisamment de points
if placesRequired > club_points:
flash("Error: Your club doesn't have enough points.")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs), 200

# Réservation réussie
competition['numberOfPlaces'] = str(competition_places - placesRequired)
club['points'] = str(club_points - placesRequired)
flash("Great-booking complete!")
return render_template('welcome.html', club=club, competitions=competitions, clubs=clubs)

except IndexError:
flash("Error: Club or competition not found.")
return render_template('welcome.html', clubs=clubs, competitions=competitions), 200

# Correction BUG 2: Tableau d'affichage des points
@app.route('/pointsDisplay')
def pointsDisplay():
return render_template('points_display.html', clubs=clubs)


@app.route('/logout')
def logout():
return redirect(url_for('index'))
return redirect(url_for('index'))
16 changes: 13 additions & 3 deletions templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,19 @@
<body>
<h1>Welcome to the GUDLFT Registration Portal!</h1>
Please enter your secretary email to continue:
<form action="showSummary" method="post">
<label for="email">Email:</label>
<input type="email" name="email" id=""/>

{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li style="color:red;">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form action="{{ url_for('showSummary') }}" method="post">
<label>Email:</label>
<input type="email" name="email" id="email" required />
<button type="submit">Enter</button>
</form>
</body>
Expand Down
27 changes: 27 additions & 0 deletions templates/points_display.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Clubs Points | GUDLFT</title>
</head>
<body>
<h1>Clubs Points</h1>
<a href="{{url_for('index')}}">Back to login</a>
<table>
<thead>
<tr>
<th>Club</th>
<th>Points</th>
</tr>
</thead>
<tbody>
{% for club in clubs %}
<tr>
<td>{{ club['name'] }}</td>
<td>{{ club['points'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
84 changes: 84 additions & 0 deletions tests/integration/test_available_places_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@

import pytest
from server import app, clubs, competitions

@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client

def test_multiple_bookings_flow(client):
"""
Test d'intégration complet : Login + réservations multiples successives
"""

# 1. LOGIN
login_response = client.post('/showSummary',
data={'email': '[email protected]'},
follow_redirects=True)
assert login_response.status_code == 200
assert b'Welcome, [email protected]' in login_response.data
print("✓ Login réussi")

# 2. DONNÉES INITIALES
initial_places = int(next(c['numberOfPlaces'] for c in competitions if c['name'] == 'Spring Festival'))
initial_points = int(next(c['points'] for c in clubs if c['name'] == 'She Lifts'))

print(f"Places initiales: {initial_places}, Points initiaux: {initial_points}")

# 3. PREMIÈRE RÉSERVATION (1 place)
response1 = client.post('/purchasePlaces', data={
'competition': 'Spring Festival',
'club': 'She Lifts',
'places': '1'
}, follow_redirects=True)

# Vérification première réservation
assert response1.status_code in [200, 400], f"Code HTTP inattendu: {response1.status_code}"

if response1.status_code == 200:
# Réservation réussie
assert any(success_msg in response1.data for success_msg in [
b'Great-booking complete!', b'booking complete', b'Success'
])
print("✓ Première réservation réussie")

# 4. DEUXIÈME RÉSERVATION (1 place)
response2 = client.post('/purchasePlaces', data={
'competition': 'Spring Festival',
'club': 'She Lifts',
'places': '1'
}, follow_redirects=True)

assert response2.status_code in [200, 400]

if response2.status_code == 200:
# Deuxième réservation réussie
assert any(success_msg in response2.data for success_msg in [
b'Great-booking complete!', b'booking complete', b'Success'
])

# 5. VÉRIFICATIONS FINALES
final_places = int(next(c['numberOfPlaces'] for c in competitions if c['name'] == 'Spring Festival'))
final_points = int(next(c['points'] for c in clubs if c['name'] == 'She Lifts'))

assert final_places == initial_places - 2, f"Places: {initial_places} → {final_places}"
assert final_points == initial_points - 2, f"Points: {initial_points} → {final_points}"
print("✓ Réservations multiples réussies")

else:
# Deuxième réservation échouée
assert any(error_msg in response2.data for error_msg in [
b'Error', b'error', b'not enough', b'insufficient'
])
print("✓ Deuxième réservation échouée (normal)")

else:
# Première réservation échouée
assert any(error_msg in response1.data for error_msg in [
b'Error', b'error', b'not enough', b'insufficient'
])
print("✓ Première réservation échouée (test ignoré)")

print("✅ Test d'intégration réservations multiples - COMPLET")
Loading