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.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ bin
include
lib
.Python
tests/
.envrc
__pycache__
__pycache__
.coverage
.DS_Store
156 changes: 156 additions & 0 deletions Locust_Test_Report.html

Large diffs are not rendered by default.

123 changes: 96 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,120 @@
# gudlift-registration
# GUDLFT - Réservation de Compétitions

1. Why
## 📖 Description

Application Flask pour les secrétaires de clubs permettant de réserver des places de compétitions en utilisant des points.

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.
**Fonctionnalités principales :**
- Connexion sécurisée par email
- Réservation de places (max 12 par club)
- Déduction automatique des points
- Tableau public des points clubs

2. Getting Started
## Fonctionnalités Implémentées

This project uses the following technologies:
### Phase 1 - Authentification & Réservations
- [x] Connexion secrétaires via email/mot de passe
- [x] Liste compétitions à venir
- [x] Formulaire réservation `/book/mpétition>/<club>`
- [x] Achat places `/purchasePlaces`
- [x] Déconnexion `/logout`

* Python v3.x+
### Phase 2 - Transparence & Performance
- [x] Tableau points public `/points` (lecture seule)
- [x] Tests Locust : 6 utilisateurs simultanés
- GET pages : 5-6ms (< 5s ✅)
- POST achat : 14ms (< 2s ✅)
- [x] 100% Couverture pour le code server.py

* [Flask](https://flask.palletsprojects.com/en/1.1.x/)
### Contraintes Métier
- [x] Max 12 places par club/compétition
- [x] Nombre de points requis pour la réservation
- [x] Pas de réservation pour les compétitions passées

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

## 🛠️ Installation et démarrage

* [Virtual environment](https://virtualenv.pypa.io/en/stable/installation.html)
### Prérequis
- Python 3.8 ou plus récent
- `pip` installé

This ensures you'll be able to install the correct packages without interfering with Python on your machine.
### Installation des dépendances

Before you begin, please ensure you have this installed globally.
Dans votre terminal, positionnez-vous dans le dossier du projet puis exécutez :

pip install -r requirements.txt

3. Installation
Cette commande installe les bibliothèques nécessaires.

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

- 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>
Pour démarrer l’application Flask localement, tapez :

- 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 --app server.py run -p 5000

- 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.
L’application sera accessible ensuite à l’adresse :
`http://127.0.0.1:5000`

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.
## 🧪 Tests automatisés

5. Testing
### Lancement des tests unitaires et d’intégration

You are free to use whatever testing framework you like-the main thing is that you can show what tests you are using.
Les tests sont organisés dans le dossier `tests/`. Pour exécuter tous les tests, utilisez :

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.
coverage run -m pytest


Cela lance tous les tests tout en mesurant la couverture du code.

### Visualiser le rapport de couverture

Pour obtenir un rapport détaillé de la couverture de code :

coverage report -m


L’objectif est d’avoir un taux minimum de 60 % de couverture, mais ici la couverture est à 100 % sur `server.py`.

---

## 🚀 Tests de performance avec Locust

### Description

Locust simule des utilisateurs réels pour tester la performance sous charge. Ici, 6 utilisateurs effectuent les actions de consultation et réservation.

### Lancement des tests Locust

Dans un nouveau terminal, lancez Locust avec :

locust -f locustfile.py --host=http://localhost:5000 --users 6 --spawn-rate 1 --run-time 30s


Ensuite, ouvrez un navigateur à l’adresse :
`http://localhost:8089`

Cliquez sur start avec 6 utilisateurs pour commencer les tests.

### Résultat attendu

Les temps de réponse doivent être :
- Inférieurs à 5 secondes pour le chargement des pages
- Inférieurs à 2 secondes pour les achats de places

Notre rapport `Locust_Test_Report.html` contient les résultats détaillés.

---

## Structure du projet

python_testing/
├── server.py # Application Flask principale
├── tests/
│ ├── test_unit.py # Tests unitaires
│ ├── test_integration.py # Tests d’intégration
│ └── conftest.py # Configuration pytest
├── locustfile.py # Scénarios de tests de performance Locust
├── Locust_Test_Report.html # Rapport de test de performance généré par Locust
└── README.md # Ce fichier
25 changes: 14 additions & 11 deletions clubs.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{"clubs":[
{
"clubs": [
{
"name":"Simply Lift",
"email":"john@simplylift.co",
"points":"13"
"name": "Simply Lift",
"email": "john@simplylift.co",
"points": "13"
},
{
"name":"Iron Temple",
"email": "admin@irontemple.com",
"points":"4"
"name": "Iron Temple",
"email": "admin@irontemple.com",
"points": "11"
},
{ "name":"She Lifts",
"email": "kate@shelifts.co.uk",
"points":"12"
{
"name": "She Lifts",
"email": "kate@shelifts.co.uk",
"points": "22"
}
]}
]
}
34 changes: 22 additions & 12 deletions competitions.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
{
"competitions": [
{
"name": "Spring Festival",
"date": "2020-03-27 10:00:00",
"numberOfPlaces": "25"
},
{
"name": "Fall Classic",
"date": "2020-10-22 13:30:00",
"numberOfPlaces": "13"
}
]
"competitions": [
{
"name": "Spring Festival",
"date": "2020-03-27 10:00:00",
"numberOfPlaces": "25"
},
{
"name": "Fall Classic",
"date": "2020-10-22 13:30:00",
"numberOfPlaces": "13"
},
{
"name": "Winter Gala",
"date": "2025-12-15 09:00:00",
"numberOfPlaces": "30"
},
{
"name": "New Year's Championship",
"date": "2026-01-02 11:00:00",
"numberOfPlaces": "10"
}
]
}
24 changes: 24 additions & 0 deletions locustfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from locust import HttpUser, task, between

class ClubSecretary(HttpUser):
wait_time = between(1, 3) # Pause 1-3s entre actions

@task
def visit_welcome(self):
self.client.get("/") # Page d'accueil

@task
def book_page(self):
self.client.get("/book/Winter Gala/Simply Lift") # Page réservation

@task
def points_page(self):
self.client.get("/points") # Tableau points

@task
def purchase_places(self):
self.client.post("/purchasePlaces", {
"competition": "Winter Gala",
"club": "Simply Lift",
"places": "1"
})
Loading