Skip to content
New issue

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

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

Already on GitHub? # to your account

Feature/locust #280

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
Binary file modified .DS_Store
Binary file not shown.
Binary file added .coverage
Binary file not shown.
2 changes: 2 additions & 0 deletions .flaskenv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FLASK_APP=app.server
FLASK_ENV=development
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ bin
include
lib
.Python
tests/

.envrc
__pycache__
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/
Empty file added app/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions app/booking_manager/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# app/booking_manager/__init__.py

from .booking_service import BookingService
from .club_manager import ClubManager
from .competition_manager import CompetitionManager
from .data_loader import JSONDataLoader
36 changes: 36 additions & 0 deletions app/booking_manager/booking_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# app/booking_manager/booking_service.py

from app.booking_manager.club_manager import ClubManager
from app.booking_manager.competition_manager import CompetitionManager


class BookingService:
"""
Ordonne le processus de réservation en utilisant les gestionnaires de clubs et de compétitions.
"""

def __init__(self, clubs_file: str, competitions_file: str):
self.club_manager = ClubManager(clubs_file)
self.competition_manager = CompetitionManager(competitions_file)

def purchase_places(self, club_name: str, competition_name: str, places_requested: int) -> bool:
club = self.club_manager.find_by_name(club_name)
competition = self.competition_manager.find_by_name(competition_name)

if not club or not competition:
return False
if places_requested > 12:
return False
if places_requested > club.points:
return False
if places_requested > competition.number_of_places:
return False

competition.number_of_places -= places_requested
club.points -= places_requested

# Sauvegarde des modifications dans les fichiers JSON
self.club_manager.save_clubs()
self.competition_manager.save_competitions()

return True
73 changes: 73 additions & 0 deletions app/booking_manager/club_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# app/booking_manager/club_manager.py

import json
import os
import shutil
from typing import List, Optional
from app.models import Club
from .data_loader import JSONDataLoader


class ClubManager:
"""
Gère le chargement, la recherche et la sauvegarde des clubs.
"""

def __init__(self, clubs_file: str):
self.clubs_file = clubs_file # Fichier "de travail"
self.loader = JSONDataLoader(clubs_file)
data = self.loader.load_data()
self.clubs: List[Club] = self._parse_clubs(data)

def _parse_clubs(self, data: dict) -> List[Club]:
clubs = []
for c in data.get("clubs", []):
clubs.append(
Club(
name=c["name"],
email=c["email"],
points=int(c["points"]),
id=c.get("id")
)
)
return clubs

def find_by_email(self, email: str) -> Optional[Club]:
return next((club for club in self.clubs if club.email == email), None)

def find_by_name(self, name: str) -> Optional[Club]:
return next((club for club in self.clubs if club.name == name), None)

def save_clubs(self, filepath: Optional[str] = None) -> None:
"""
Sauvegarde l'état actuel des clubs dans un fichier JSON.
"""
if filepath is None:
filepath = self.clubs_file
clubs_data = {"clubs": []}
for club in self.clubs:
club_dict = {
"id": club.id,
"name": club.name,
"email": club.email,
"points": str(club.points)
}
clubs_data["clubs"].append(club_dict)
with open(filepath, "w") as f:
json.dump(clubs_data, f, indent=4)

def reset_data(self, fresh_filepath: str) -> None:
"""
Copie le fichier fresh_filepath dans self.clubs_file,
puis recharge la liste des clubs en mémoire.
"""
if not os.path.exists(fresh_filepath):
raise FileNotFoundError(
f"Fichier source introuvable : {fresh_filepath}")

# 1. Copie fresh_filepath => self.clubs_file
shutil.copy(fresh_filepath, self.clubs_file)

# 2. Recharge en mémoire
data = self.loader.load_data()
self.clubs = self._parse_clubs(data)
68 changes: 68 additions & 0 deletions app/booking_manager/competition_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# app/booking_manager/competition_manager.py

import json
import os
import shutil
from typing import List, Optional
from app.models import Competition
from .data_loader import JSONDataLoader


class CompetitionManager:
"""
Gère le chargement, la recherche et la sauvegarde des compétitions.
"""

def __init__(self, competitions_file: str):
self.competitions_file = competitions_file # Fichier "de travail"
self.loader = JSONDataLoader(competitions_file)
data = self.loader.load_data()
self.competitions: List[Competition] = self._parse_competitions(data)

def _parse_competitions(self, data: dict) -> List[Competition]:
competitions = []
for c in data.get("competitions", []):
competitions.append(
Competition(
name=c["name"],
date=c["date"],
number_of_places=int(c["numberOfPlaces"])
)
)
return competitions

def find_by_name(self, name: str) -> Optional[Competition]:
return next((comp for comp in self.competitions if comp.name == name), None)

def save_competitions(self, filepath: Optional[str] = None) -> None:
"""
Sauvegarde l'état actuel des compétitions dans un fichier JSON.
"""
if filepath is None:
filepath = self.competitions_file
competitions_data = {"competitions": []}
for comp in self.competitions:
comp_dict = {
"name": comp.name,
"date": comp.date,
"numberOfPlaces": str(comp.number_of_places)
}
competitions_data["competitions"].append(comp_dict)
with open(filepath, "w") as f:
json.dump(competitions_data, f, indent=4)

def reset_data(self, fresh_filepath: str) -> None:
"""
Copie le fichier fresh_filepath dans self.competitions_file,
puis recharge les competitions en mémoire.
"""
if not os.path.exists(fresh_filepath):
raise FileNotFoundError(
f"Fichier source introuvable : {fresh_filepath}")

# 1. Copie fresh_filepath => self.competitions_file
shutil.copy(fresh_filepath, self.competitions_file)

# 2. Recharge en mémoire
data = self.loader.load_data() # relit le fichier de travail
self.competitions = self._parse_competitions(data)
21 changes: 21 additions & 0 deletions app/booking_manager/data_loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# app/booking_manager/data_loader.py

import json
import os
from typing import Any


class JSONDataLoader:
"""
Classe générique pour charger des données depuis un fichier JSON.
"""

def __init__(self, filepath: str):
self.filepath = filepath

def load_data(self) -> Any:
if not os.path.exists(self.filepath):
raise FileNotFoundError(f"Fichier introuvable : {self.filepath}")
with open(self.filepath, "r") as f:
data = json.load(f)
return data
15 changes: 15 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# app/models.py

class Competition:
def __init__(self, name, date, number_of_places):
self.name = name
self.date = date
self.number_of_places = number_of_places


class Club:
def __init__(self, name, email, points, id=None):
self.name = name
self.email = email
self.points = points
self.id = id
134 changes: 134 additions & 0 deletions app/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# app/server.py

from flask import Flask, render_template, request, redirect, flash, url_for, session
from app.booking_manager import BookingService

app = Flask(__name__, static_folder="../static")
app.secret_key = "secret_key_xyz"

# Instanciation du service de réservation
booking_service = BookingService(
clubs_file="data/clubs.json",
competitions_file="data/competitions.json"
)


@app.context_processor
def inject_club_email():
"""
Permet d'accéder à session['club_email'] dans les templates,
afin de savoir si un utilisateur est connecté.
"""
return dict(club_email=session.get('club_email'))


@app.route("/")
def index():
"""
Page d'accueil.
"""
return render_template("index.html")


@app.route("/showSummary", methods=["POST"])
def show_summary():
"""
Récupère l'email du club et affiche la page de résumé (welcome.html).
Si le club n'est pas trouvé, on redirige avec un message d'erreur.
"""
email = request.form.get("email", "")
club = booking_service.club_manager.find_by_email(email)
if not club:
flash("Email inconnu ou invalide.")
return redirect(url_for("index"))

session['club_email'] = club.email
competitions = booking_service.competition_manager.competitions
return render_template("welcome.html", club=club, competitions=competitions)


@app.route("/book/<competition>/<club>")
def book(competition, club):
"""
Affiche la page de réservation (booking.html) pour un club/compétition donné.
"""
found_competition = booking_service.competition_manager.find_by_name(
competition)
found_club = booking_service.club_manager.find_by_name(club)
if not found_competition or not found_club:
flash("Something went wrong - please try again")
return redirect(url_for("index"))

return render_template("booking.html", club=found_club, competition=found_competition)


@app.route("/purchasePlaces", methods=["POST"])
def purchase_places():
"""
Tente d'acheter 'places' places pour un club et une compétition.
Après avoir flashé un message de succès ou d'erreur,
on redirige vers /showPurchaseResult/<club_name> pour afficher la page finale.
"""
competition_name = request.form.get("competition")
club_name = request.form.get("club")
places_str = request.form.get("places")

try:
places_requested = int(places_str)
except ValueError:
flash("Le nombre de places est invalide.")
return redirect(url_for("index"))

found_competition = booking_service.competition_manager.find_by_name(
competition_name)
found_club = booking_service.club_manager.find_by_name(club_name)

if not found_competition or not found_club:
flash("Something went wrong - please try again.")
return redirect(url_for("index"))

# Logique de réservation
if places_requested > 12:
flash("Vous ne pouvez pas réserver plus de 12 places.")
success = False
else:
success = booking_service.purchase_places(
club_name, competition_name, places_requested)

if success:
flash(
f"Great-booking complete! Vous avez réservé {places_requested} places.")
else:
if places_requested <= 12:
flash("Le concours est complet ou vous n'avez pas assez de points.")

# Redirection vers la route qui affiche le résultat
return redirect(url_for("show_purchase_result", club_name=club_name))


@app.route("/showPurchaseResult/<club_name>")
def show_purchase_result(club_name):
"""
Affiche la page welcome.html, lit le message flash si besoin.
"""
updated_club = booking_service.club_manager.find_by_name(club_name)
competitions = booking_service.competition_manager.competitions
return render_template("welcome.html", club=updated_club, competitions=competitions)


@app.route("/clubsPoints")
def clubs_points():
"""
Liste des clubs et leurs points.
"""
clubs = booking_service.club_manager.clubs
return render_template("clubs_points.html", clubs=clubs)


@app.route("/logout")
def logout():
"""
Déconnecte le club.
"""
session.pop('club_email', None)
return redirect(url_for("index"))
Loading