Reservation de salle
This commit is contained in:
+256
@@ -0,0 +1,256 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import asyncpg
|
||||
from datetime import datetime, date, time
|
||||
import os
|
||||
|
||||
# Configuration de la base de données
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@db:5432/dockezr")
|
||||
|
||||
# Pool de connexions
|
||||
pool = None
|
||||
|
||||
# Modèles Pydantic pour les Salles
|
||||
class RoomCreate(BaseModel):
|
||||
name: str
|
||||
capacity: int
|
||||
equipment: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
class Room(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
capacity: int
|
||||
equipment: Optional[str]
|
||||
description: Optional[str]
|
||||
|
||||
# Modèles Pydantic pour les Réservations
|
||||
class ReservationCreate(BaseModel):
|
||||
room_id: int
|
||||
user_name: str
|
||||
user_email: Optional[str] = None
|
||||
reservation_date: date
|
||||
start_time: time
|
||||
end_time: time
|
||||
purpose: str
|
||||
|
||||
class Reservation(BaseModel):
|
||||
id: int
|
||||
room_id: int
|
||||
room_name: Optional[str] = None
|
||||
user_name: str
|
||||
user_email: Optional[str] = None
|
||||
reservation_date: date
|
||||
start_time: time
|
||||
end_time: time
|
||||
purpose: str
|
||||
created_at: datetime
|
||||
|
||||
# Application FastAPI
|
||||
app = FastAPI(
|
||||
title="Expernet - Système de Réservation de Salles",
|
||||
version="1.0.0",
|
||||
description="API de gestion des réservations de salles pour le centre de formation Expernet"
|
||||
)
|
||||
|
||||
# Configuration CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["http://localhost:3000", "http://frontend:3000"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
global pool
|
||||
pool = await asyncpg.create_pool(DATABASE_URL)
|
||||
# Créer les tables si elles n'existent pas
|
||||
async with pool.acquire() as conn:
|
||||
# Table des salles
|
||||
await conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS rooms (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL UNIQUE,
|
||||
capacity INTEGER NOT NULL,
|
||||
equipment TEXT,
|
||||
description TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# Table des réservations
|
||||
await conn.execute('''
|
||||
CREATE TABLE IF NOT EXISTS reservations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
room_id INTEGER NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
|
||||
user_name VARCHAR(255) NOT NULL,
|
||||
user_email VARCHAR(255),
|
||||
reservation_date DATE NOT NULL,
|
||||
start_time TIME NOT NULL,
|
||||
end_time TIME NOT NULL,
|
||||
purpose TEXT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT NOW()
|
||||
)
|
||||
''')
|
||||
|
||||
# Insérer des salles par défaut si la table est vide
|
||||
count = await conn.fetchval('SELECT COUNT(*) FROM rooms')
|
||||
if count == 0:
|
||||
await conn.execute('''
|
||||
INSERT INTO rooms (name, capacity, equipment, description) VALUES
|
||||
('Salle Atlas', 30, 'Vidéoprojecteur, Tableau blanc, WiFi', 'Grande salle de formation idéale pour les cours'),
|
||||
('Salle Horizon', 15, 'Écran TV, Tableau blanc, WiFi', 'Salle de réunion moyenne capacité'),
|
||||
('Salle Innovation', 50, 'Vidéoprojecteur, Sono, WiFi, Climatisation', 'Salle de conférence grande capacité'),
|
||||
('Salle Connect', 8, 'Écran TV, Visioconférence, WiFi', 'Petite salle pour réunions d''équipe'),
|
||||
('Salle Digital', 20, '20 postes informatiques, Vidéoprojecteur, WiFi', 'Salle informatique pour formations pratiques')
|
||||
''')
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown():
|
||||
await pool.close()
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Bienvenue sur le système de réservation de salles Expernet!", "status": "running"}
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
return {"status": "healthy", "database": "connected"}
|
||||
|
||||
# === ROUTES POUR LES SALLES ===
|
||||
|
||||
@app.get("/rooms", response_model=List[Room])
|
||||
async def get_rooms():
|
||||
"""Récupérer toutes les salles"""
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch('SELECT * FROM rooms ORDER BY name')
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@app.get("/rooms/{room_id}", response_model=Room)
|
||||
async def get_room(room_id: int):
|
||||
"""Récupérer une salle spécifique"""
|
||||
async with pool.acquire() as conn:
|
||||
row = await conn.fetchrow('SELECT * FROM rooms WHERE id = $1', room_id)
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="Salle non trouvée")
|
||||
return dict(row)
|
||||
|
||||
@app.post("/rooms", response_model=Room)
|
||||
async def create_room(room: RoomCreate):
|
||||
"""Créer une nouvelle salle"""
|
||||
async with pool.acquire() as conn:
|
||||
try:
|
||||
row = await conn.fetchrow(
|
||||
'INSERT INTO rooms (name, capacity, equipment, description) VALUES ($1, $2, $3, $4) RETURNING *',
|
||||
room.name, room.capacity, room.equipment, room.description
|
||||
)
|
||||
return dict(row)
|
||||
except asyncpg.UniqueViolationError:
|
||||
raise HTTPException(status_code=400, detail="Une salle avec ce nom existe déjà")
|
||||
|
||||
@app.delete("/rooms/{room_id}")
|
||||
async def delete_room(room_id: int):
|
||||
"""Supprimer une salle"""
|
||||
async with pool.acquire() as conn:
|
||||
result = await conn.execute('DELETE FROM rooms WHERE id = $1', room_id)
|
||||
if result == "DELETE 0":
|
||||
raise HTTPException(status_code=404, detail="Salle non trouvée")
|
||||
return {"message": "Salle supprimée avec succès"}
|
||||
|
||||
# === ROUTES POUR LES RÉSERVATIONS ===
|
||||
|
||||
@app.get("/reservations", response_model=List[Reservation])
|
||||
async def get_reservations():
|
||||
"""Récupérer toutes les réservations avec les informations des salles"""
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch('''
|
||||
SELECT r.*, ro.name as room_name
|
||||
FROM reservations r
|
||||
JOIN rooms ro ON r.room_id = ro.id
|
||||
ORDER BY r.reservation_date DESC, r.start_time DESC
|
||||
''')
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@app.get("/reservations/room/{room_id}", response_model=List[Reservation])
|
||||
async def get_reservations_by_room(room_id: int):
|
||||
"""Récupérer les réservations d'une salle spécifique"""
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch('''
|
||||
SELECT r.*, ro.name as room_name
|
||||
FROM reservations r
|
||||
JOIN rooms ro ON r.room_id = ro.id
|
||||
WHERE r.room_id = $1
|
||||
ORDER BY r.reservation_date, r.start_time
|
||||
''', room_id)
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@app.get("/reservations/date/{reservation_date}")
|
||||
async def get_reservations_by_date(reservation_date: date):
|
||||
"""Récupérer les réservations pour une date spécifique"""
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch('''
|
||||
SELECT r.*, ro.name as room_name
|
||||
FROM reservations r
|
||||
JOIN rooms ro ON r.room_id = ro.id
|
||||
WHERE r.reservation_date = $1
|
||||
ORDER BY r.start_time
|
||||
''', reservation_date)
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@app.post("/reservations", response_model=Reservation)
|
||||
async def create_reservation(reservation: ReservationCreate):
|
||||
"""Créer une nouvelle réservation"""
|
||||
async with pool.acquire() as conn:
|
||||
# Vérifier que la salle existe
|
||||
room = await conn.fetchrow('SELECT * FROM rooms WHERE id = $1', reservation.room_id)
|
||||
if room is None:
|
||||
raise HTTPException(status_code=404, detail="Salle non trouvée")
|
||||
|
||||
# Vérifier que l'heure de fin est après l'heure de début
|
||||
if reservation.end_time <= reservation.start_time:
|
||||
raise HTTPException(status_code=400, detail="L'heure de fin doit être après l'heure de début")
|
||||
|
||||
# Vérifier les conflits de réservation
|
||||
conflicts = await conn.fetch('''
|
||||
SELECT * FROM reservations
|
||||
WHERE room_id = $1
|
||||
AND reservation_date = $2
|
||||
AND (
|
||||
(start_time < $4 AND end_time > $3)
|
||||
OR (start_time >= $3 AND start_time < $4)
|
||||
)
|
||||
''', reservation.room_id, reservation.reservation_date,
|
||||
reservation.start_time, reservation.end_time)
|
||||
|
||||
if conflicts:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="La salle est déjà réservée pour ce créneau horaire"
|
||||
)
|
||||
|
||||
# Créer la réservation
|
||||
row = await conn.fetchrow('''
|
||||
INSERT INTO reservations
|
||||
(room_id, user_name, user_email, reservation_date, start_time, end_time, purpose, created_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *
|
||||
''', reservation.room_id, reservation.user_name, reservation.user_email,
|
||||
reservation.reservation_date, reservation.start_time, reservation.end_time,
|
||||
reservation.purpose, datetime.utcnow())
|
||||
|
||||
result = dict(row)
|
||||
result['room_name'] = room['name']
|
||||
return result
|
||||
|
||||
@app.delete("/reservations/{reservation_id}")
|
||||
async def delete_reservation(reservation_id: int):
|
||||
"""Annuler une réservation"""
|
||||
async with pool.acquire() as conn:
|
||||
result = await conn.execute('DELETE FROM reservations WHERE id = $1', reservation_id)
|
||||
if result == "DELETE 0":
|
||||
raise HTTPException(status_code=404, detail="Réservation non trouvée")
|
||||
return {"message": "Réservation annulée avec succès"}
|
||||
|
||||
Reference in New Issue
Block a user