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

mise en place du module coverage #275

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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/
1 change: 1 addition & 0 deletions Python_Testing
Submodule Python_Testing added at 4ca9cd
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
55 changes: 55 additions & 0 deletions app/booking_manager/club_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# app/booking_manager/club_manager.py

import json
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 # Pour la sauvegarde
loader = JSONDataLoader(clubs_file)
data = 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)
50 changes: 50 additions & 0 deletions app/booking_manager/competition_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# app/booking_manager/competition_manager.py

import json
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 # Pour la sauvegarde
loader = JSONDataLoader(competitions_file)
data = 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)
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
114 changes: 114 additions & 0 deletions app/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# app/server.py

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

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

# Instanciation du service de réservation, avec les JSON par défaut.
# Si vous souhaitez utiliser des fichiers de test différents,
# configurez-les dans le code ou via des variables d'environnement.
booking_service = BookingService(
clubs_file="data/clubs.json",
competitions_file="data/competitions.json"
)


@app.route("/")
def index():
"""
Page d'accueil de l'application.
"""
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é, renvoie vers la page d'accueil 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"))

# Affiche la liste des compétitions disponibles
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 et une compétition donnés.
Si le club ou la compétition n'existe pas, renvoie vers l'index.
"""
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.
- Si la réservation réussit, on affiche 'Great-booking complete!'
- Sinon, on affiche 'Impossible de réserver...'
"""
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"))

# Vérification de l'existence du club et de la compétition
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"))

# Appel à la logique métier
success = booking_service.purchase_places(
club_name, competition_name, places_requested)
if success:
flash("Great-booking complete!")
else:
flash("Impossible de réserver ces places (Règle non respectée).")

# Recharger les données du club et de la compétition (après mise à jour)
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():
"""
Affiche la liste des clubs et leurs points disponibles.
Page publique, sans besoin de login.
"""
clubs = booking_service.club_manager.clubs
return render_template("clubs_points.html", clubs=clubs)


@app.route("/logout")
def logout():
"""
Déconnecte le club (retour à l'accueil).
"""
return redirect(url_for("index"))
31 changes: 31 additions & 0 deletions app/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!-- app/templates/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}GUDLFT{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav>
<ul>
<li><a href="{{ url_for('index') }}">Accueil</a></li>
<li><a href="{{ url_for('clubs_points') }}">Points Clubs</a></li>
<li><a href="{{ url_for('logout') }}">Déconnexion</a></li>
</ul>
</nav>
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</div>
</body>
</html>
16 changes: 16 additions & 0 deletions app/templates/booking.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- app/templates/booking.html -->
{% extends "base.html" %}

{% block title %}Réservation | {{ competition['name'] }}{% endblock %}

{% block content %}
<h2>{{ competition['name'] }}</h2>
<p>Places disponibles: {{ competition['number_of_places'] }}</p>
<form action="/purchasePlaces" method="post">
<input type="hidden" name="club" value="{{ club['name'] }}">
<input type="hidden" name="competition" value="{{ competition['name'] }}">
<label for="places">Combien de places ?</label>
<input type="number" name="places" id="places" min="1" required>
<button type="submit">Réserver</button>
</form>
{% endblock %}
24 changes: 24 additions & 0 deletions app/templates/clubs_points.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- app/templates/clubs_points.html -->
{% extends "base.html" %}

{% block title %}Points des Clubs{% endblock %}

{% block content %}
<h2>Points des Clubs</h2>
<table border="1">
<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>
{% endblock %}
Loading