Skip to content

feat: readme and makefile #274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
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
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__
.vscode
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
PYTHON := python
PYTEST := pytest

TEST_FILES := \
tests/test_purchase_place.py \
tests/test_booking_page.py \
tests/test_show_summary.py

# Commande pour exécuter tous les tests
test:
$(PYTEST) $(TEST_FILES)

help:
@echo " test Exécute les tests Pytest"
@echo " help Affiche cette aide"

.PHONY: test install run clean help
79 changes: 42 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,56 @@
# gudlift-registration
# GÜDLFT : Projet d'école [OpenClassrooms](https://openclassrooms.com/fr) - Améliorez une application Web Python par des tests et du débogage

1. Why
GUDLFT est une application web conçue pour les organisateurs de compétitions locales et régionales. L'application permet aux secrétaires de clubs de réserver des places pour des compétitions en utilisant les points accumulés, tout en garantissant une participation équitable entre tous les clubs.


This is a proof of concept (POC) project to show a light-weight version of our competition booking platform. The aim is the keep things as light as possible, and use feedback from the users to iterate.
## Installation et exécution

2. Getting Started
### Prérequis

This project uses the following technologies:
- Python 3.9 ou version supérieur
- pip (gestionnaire de paquets Python)

* Python v3.x+
### Étapes d'installation

* [Flask](https://flask.palletsprojects.com/en/1.1.x/)
1. **Cloner le dépôt** :
```bash
git clone https://github.com/Jbguerin13/Python_Testing.git
cd gudlft/PYTHON_TESTING
```

Whereas Django does a lot of things for us out of the box, Flask allows us to add only what we need.

2. **Créer un environnement virtuel** :
Sous Windows :
```bash
python -m venv env
env\Scripts\activate
```
Sous macOS/Linux :
```bash
python3 -m venv env
source env/bin/activate
```

* [Virtual environment](https://virtualenv.pypa.io/en/stable/installation.html)
3. **Installer les dépendances** :
```bash
pip install -r requirements.txt
```

This ensures you'll be able to install the correct packages without interfering with Python on your machine.
4. **Lancer le serveur** :
```bash
export FLASK_APP=server.py
flask run
```

Before you begin, please ensure you have this installed globally.
5. **📌 Fonctionnalités** :

- **Authentification des utilisateurs** : Connexion par e-mail pour les secrétaires de clubs.
- **Système de réservation** : Utilisation des points des clubs pour réserver des places aux compétitions.
- **Gestion des points** : Affichage du solde des points et mise à jour après réservation.
- **Limites de réservation** : Un club ne peut pas réserver plus de 12 places par compétition.
- **Tableau des points** : Affichage public des points de tous les clubs pour assurer la transparence.

3. Installation

- After cloning, change into the directory and type <code>virtualenv .</code>. This will then set up a a virtual python environment within that directory.

- Next, type <code>source bin/activate</code>. You should see that your command prompt has changed to the name of the folder. This means that you can install packages in here without affecting affecting files outside. To deactivate, type <code>deactivate</code>

- Rather than hunting around for the packages you need, you can install in one step. Type <code>pip install -r requirements.txt</code>. This will install all the packages listed in the respective file. If you install a package, make sure others know by updating the requirements.txt file. An easy way to do this is <code>pip freeze > requirements.txt</code>

- Flask requires that you set an environmental variable to the python file. However you do that, you'll want to set the file to be <code>server.py</code>. Check [here](https://flask.palletsprojects.com/en/1.1.x/quickstart/#a-minimal-application) for more details

- You should now be ready to test the application. In the directory, type either <code>flask run</code> or <code>python -m flask run</code>. The app should respond with an address you should be able to go to using your browser.

4. Current Setup

The app is powered by [JSON files](https://www.tutorialspoint.com/json/json_quick_guide.htm). This is to get around having a DB until we actually need one. The main ones are:

* competitions.json - list of competitions
* clubs.json - list of clubs with relevant information. You can look here to see what email addresses the app will accept for login.

5. Testing

You are free to use whatever testing framework you like-the main thing is that you can show what tests you are using.

We also like to show how well we're testing, so there's a module called
[coverage](https://coverage.readthedocs.io/en/coverage-5.1/) you should add to your project.
6. **Lancement des tests** :
```bash
make test
```

3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
filterwarnings =
ignore::DeprecationWarning
94 changes: 68 additions & 26 deletions server.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,101 @@
import json
from flask import Flask,render_template,request,redirect,flash,url_for


#load the clubs and competitions from the json files
def loadClubs():
with open('clubs.json') as c:
listOfClubs = json.load(c)['clubs']
return listOfClubs


#load the clubs and competitions from the json files
def loadCompetitions():
with open('competitions.json') as comps:
listOfCompetitions = json.load(comps)['competitions']
return listOfCompetitions


app = Flask(__name__)
app.secret_key = 'something_special'
app = Flask(__name__) #create the Flask app
app.secret_key = 'something_special' #used to add a session flash

#load the clubs and competitions
competitions = loadCompetitions()
clubs = loadClubs()

@app.route('/')
def index():
# user can connect from a form (by email)
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)
#display welcome page after connextion
@app.route('/show_summary',methods=['POST','GET'])
def show_summary():
email = request.form.get("email") if request.method == "POST" else request.args.get("email")

if not email:
flash("Email is required. Please login.", "error")
return redirect(url_for('index'))

club = next((club for club in clubs if club['email'] == email), None)

if club:
return render_template('welcome.html', club=club, competitions=competitions)

flash("Wrong credentials, please retry", "error")
return redirect(url_for('index'))

@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)
#display the booking page
@app.route('/book/<competition>/<club>', methods=['GET', 'POST'])
def book(competition, club):
found_club = next((c for c in clubs if c['name'] == club), None)
found_competition = next((c for c in competitions if c['name'] == competition), None)

if not found_club or not found_competition:
flash("Something went wrong - Please try again", "error")
return redirect(url_for('index'))

@app.route('/purchasePlaces',methods=['POST'])
return render_template("booking.html", club=found_club, competition=found_competition)


#purchase places
@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)
competition = next((c for c in competitions if c['name'] == request.form['competition']), None)
club = next((c for c in clubs if c['name'] == request.form['club']), None)

if not competition or not club:
flash("Something went wrong - Please try again", "error")
return redirect(url_for('index'))

user_places_required = int(request.form['places'])

if user_places_required <= 0:
flash("You must book at least one place.", "error")
return render_template('welcome.html', club=club, competitions=competitions), 400
elif user_places_required > int(competition['numberOfPlaces']):
flash("Not enough places available.", "error")
return render_template('welcome.html', club=club, competitions=competitions), 400
elif user_places_required > int(club["points"]):
flash("Not enough points available.", "error")
return render_template('welcome.html', club=club, competitions=competitions), 400
elif user_places_required > 12:
flash("You cannot book more than 12 places per competition.", "error")
return render_template('welcome.html', club=club, competitions=competitions), 400
else:
competition['numberOfPlaces'] = int(competition['numberOfPlaces']) - user_places_required
club['points'] = int(club['points']) - user_places_required
flash(f"Great! Booking complete for {user_places_required} places!", "success")
return redirect(url_for('show_summary', email=club['email']))

return render_template('welcome.html', club=club, competitions=competitions)

# TODO: Add route for points display
@app.route('/display_points_club', methods=['POST','GET'])
def display_points_club():
"""Affiche les points des clubs et garde le contexte du club connecté"""
email = request.form.get("email") if request.method == "POST" else request.args.get("email")
club = next((club for club in clubs if club['email'] == email), None)

return render_template('display_points_club.html', clubs=clubs, club=club)

@app.route('/logout')
def logout():
return redirect(url_for('index'))
return redirect(url_for('index'))
14 changes: 12 additions & 2 deletions templates/booking.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@
<body>
<h2>{{competition['name']}}</h2>
Places available: {{competition['numberOfPlaces']}}
<form action="/purchasePlaces" method="post">
<form action="{{ url_for('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=""/>
<label for="places">How many places?</label>
<input type="number" name="places" id="places" required min="1">
<button type="submit">Book</button>
</form>
</body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul class="flashes">
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</html>
54 changes: 54 additions & 0 deletions templates/display_points_club.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Table | Club Points Overview | GUDLFT</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
text-align: center;
}
h1 {
color: #333;
}
table {
width: 50%;
margin: 20px auto;
border-collapse: collapse;
background-color: #fff;
box-shadow: 2px 2px 12px rgba(0,0,0,0.1);
}
th, td {
padding: 10px;
border: 1px solid #ddd;
}
th {
background-color: #ccc;
}
tr:nth-child(even) {
background-color: #eee;
}
tr:hover {
background-color: #ddd;
}
</style>
</head>
<body>
<h1>Club Points Table</h1>
<table>
<tr>
<th>Club Name</th>
<th>Total Points Available</th>
</tr>
{% for club in clubs %}
<tr>
<td>{{ club['name'] }}</td>
<td>{{ club['points'] }}</td>
</tr>
{% endfor %}
</table>
<br>
<p><a href="{{ url_for('show_summary', email=club['email']) }}">Return to home page</a></p>
</body>
</html>
11 changes: 10 additions & 1 deletion templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
<body>
<h1>Welcome to the GUDLFT Registration Portal!</h1>
Please enter your secretary email to continue:
<form action="showSummary" method="post">
<form action="show_summary" method="post">
<label for="email">Email:</label>
<input type="email" name="email" id=""/>
<button type="submit">Enter</button>
</form>
</body>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul class="flashes">
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</html>
1 change: 1 addition & 0 deletions templates/welcome.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ <h3>Competitions:</h3>
{% endfor %}
</ul>
{%endwith%}
<p><a href="{{ url_for('display_points_club', email=club['email']) }}">View Club Points</a></p>

</body>
</html>
Empty file added tests/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest
from server import app

@pytest.fixture(scope="module")
def client():
"""Fixture pour créer un client de test Flask"""
app.config["TESTING"] = True
app.config["SECRET_KEY"] = "test_secret"
with app.test_client() as client:
yield client


@pytest.fixture(scope="function")
def setup_data(monkeypatch):
mock_competitions = [
{"name": "Competition 1", "numberOfPlaces": "20"},
{"name": "Competition 2", "numberOfPlaces": "5"},
]
mock_clubs = [
{"name": "Club A", "email": "[email protected]", "points": "10"},
{"name": "Club B", "email": "[email protected]", "points": "3"},
{"name": "Club C", "email": "[email protected]", "points": "15"},
]

monkeypatch.setattr("server.competitions", mock_competitions)
monkeypatch.setattr("server.clubs", mock_clubs)

return mock_competitions, mock_clubs
Loading