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/static css test #266

Open
wants to merge 4 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.
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.
84 changes: 84 additions & 0 deletions app/booking_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# app/booking_manager.py

import json
import os
from typing import List, Optional
from app.models import Club, Competition


class BookingManager:
"""Gère la logique de réservation : chargement des données, vérification des règles, etc."""

def __init__(self, clubs_file: str, competitions_file: str):
self.clubs: List[Club] = self.load_clubs(clubs_file)
self.competitions: List[Competition] = self.load_competitions(
competitions_file)

@staticmethod
def load_clubs(filepath: str) -> List[Club]:
"""Charge et retourne la liste de clubs depuis un fichier JSON."""
if not os.path.exists(filepath):
raise FileNotFoundError(f"Fichier introuvable : {filepath}")
with open(filepath, "r") as f:
data = json.load(f)
clubs = []
for c in data["clubs"]:
clubs.append(
Club(name=c["name"], email=c["email"], points=int(c["points"]))
)
return clubs

@staticmethod
def load_competitions(filepath: str) -> List[Competition]:
"""Charge et retourne la liste de compétitions depuis un fichier JSON."""
if not os.path.exists(filepath):
raise FileNotFoundError(f"Fichier introuvable : {filepath}")
with open(filepath, "r") as f:
data = json.load(f)
competitions = []
for c in data["competitions"]:
competitions.append(
Competition(
name=c["name"],
date=c["date"],
number_of_places=int(c["numberOfPlaces"])
)
)
return competitions

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

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

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

def purchase_places(self, club_name: str, competition_name: str, places_requested: int) -> bool:
"""Tente d'acheter `places_requested` places pour le `club_name` dans `competition_name`.
Renvoie True si l’opération réussit, False sinon (règles non respectées).
"""
club = self.find_club_by_name(club_name)
competition = self.find_competition_by_name(competition_name)

# Vérifications basiques
if not club or not competition:
return False

# 1) Pas plus de 12 places en une seule fois
if places_requested > 12:
return False

# 2) Pas plus de places que le club n'a de points
if places_requested > club.points:
return False

# 3) Pas plus de places que celles disponibles dans la compétition
if places_requested > competition.number_of_places:
return False

# Si tout est OK, on décrémente les places
competition.number_of_places -= places_requested
club.points -= places_requested
return True
13 changes: 13 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 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):
self.name = name
self.email = email
self.points = points
72 changes: 72 additions & 0 deletions app/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# app/server.py

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

# Indique que le dossier statique se trouve dans ../static (à la racine du projet)
# Les templates seront recherchés par défaut dans "templates" situé dans ce même dossier (ici "app/templates")
app = Flask(__name__, static_folder="../static")
app.secret_key = "secret_key_xyz"

# Instanciation du manager avec les chemins de fichiers en paramètre
manager = BookingManager(
clubs_file="data/clubs.json",
competitions_file="data/competitions.json"
)


@app.route("/")
def index():
return render_template("index.html")


@app.route("/showSummary", methods=["POST"])
def show_summary():
email = request.form.get("email", "")
club = manager.find_club_by_email(email)
if not club:
flash("Email inconnu ou invalide.")
return redirect(url_for("index"))
return render_template("welcome.html", club=club, competitions=manager.competitions)


@app.route("/book/<competition>/<club>")
def book(competition, club):
found_competition = manager.find_competition_by_name(competition)
found_club = manager.find_club_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():
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"))

success = manager.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).")

club = manager.find_club_by_name(club_name)
return render_template("welcome.html", club=club, competitions=manager.competitions)


@app.route("/clubsPoints")
def clubs_points():
return render_template("clubs_points.html", clubs=manager.clubs)


@app.route("/logout")
def logout():
return redirect(url_for("index"))
37 changes: 37 additions & 0 deletions app/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- 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>
<!-- Vous pouvez ajouter ici un lien vers votre CSS, par exemple : -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<!-- Barre de navigation -->
<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>

<!-- Zone pour afficher les messages flash -->
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}

<!-- Contenu spécifique à chaque page -->
{% 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 %}
13 changes: 13 additions & 0 deletions app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- app/templates/index.html -->
{% extends "base.html" %}

{% block title %}Accueil | GUDLFT{% endblock %}

{% block content %}
<h1>Bienvenue sur GUDLFT</h1>
<form action="/showSummary" method="post">
<label for="email">Email :</label>
<input type="email" name="email" id="email" required>
<button type="submit">Se connecter</button>
</form>
{% endblock %}
22 changes: 22 additions & 0 deletions app/templates/welcome.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- app/templates/welcome.html -->
{% extends "base.html" %}

{% block title %}Résumé | GUDLFT Registration{% endblock %}

{% block content %}
<h2>Welcome, {{ club['email'] }}</h2>
<p>Points disponibles: {{ club['points'] }}</p>

<h3>Compétitions :</h3>
<ul>
{% for comp in competitions %}
<li>
{{ comp['name'] }}<br />
Date: {{ comp['date'] }}<br />
Places disponibles: {{ comp['number_of_places'] }}<br />
<a href="{{ url_for('book', competition=comp['name'], club=club['name']) }}">Réserver</a>
</li>
<hr />
{% endfor %}
</ul>
{% endblock %}
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "Python Debugger: Flask",
"type": "debugpy",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app/server.py",
"FLASK_DEBUG": "1"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true,
"justMyCode": true
}
8 changes: 8 additions & 0 deletions pyvenv.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
home = /usr/local/bin
implementation = CPython
version_info = 3.12.2.final.0
virtualenv = 20.28.0
include-system-site-packages = false
base-prefix = /Library/Frameworks/Python.framework/Versions/3.12
base-exec-prefix = /Library/Frameworks/Python.framework/Versions/3.12
base-executable = /Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12
59 changes: 0 additions & 59 deletions server.py

This file was deleted.

Loading