from typing import Optional, List from datetime import datetime, date, time from sqlalchemy.orm import joinedload from sqlalchemy import and_, or_ from app.models.reservation import Reservation, ReservationStatus from app.repositories.base_repository import BaseRepository from app import db class ReservationRepository(BaseRepository[Reservation]): """Repositorio para la persistencia y consulta de reservas de aulas.""" def __init__(self): super().__init__(Reservation) def find_conflicts(self, classroom_id: int, start_time: datetime, end_time: datetime, exclude_reservation_id: Optional[int] = None) -> List[Reservation]: """ Encuentra reservas activas que se solapan en el mismo aula y franja horaria. Condición de solapamiento: (start_time < existing.end_time) AND (end_time > existing.start_time). """ query = Reservation.query.filter( Reservation.classroom_id == classroom_id, Reservation.status != ReservationStatus.CANCELLED.value, Reservation.start_time < end_time, Reservation.end_time > start_time ) if exclude_reservation_id: query = query.filter(Reservation.id != exclude_reservation_id) return query.all() def get_by_date_range(self, start_dt: datetime, end_dt: datetime, classroom_id: Optional[int] = None) -> List[Reservation]: """Obtiene reservas dentro de una ventana temporal con carga ansiosa de relaciones.""" query = Reservation.query.options( joinedload(Reservation.classroom), joinedload(Reservation.commission), joinedload(Reservation.user) ).filter( Reservation.start_time >= start_dt, Reservation.start_time <= end_dt, Reservation.status != ReservationStatus.CANCELLED.value ) if classroom_id: query = query.filter(Reservation.classroom_id == classroom_id) return query.order_by(Reservation.start_time.asc()).all() def get_by_teacher(self, teacher_id: int) -> List[Reservation]: """Obtiene reservas asignadas a un docente.""" return Reservation.query.filter_by( user_id=teacher_id ).order_by(Reservation.start_time.desc()).all() def get_by_commission(self, commission_id: int) -> List[Reservation]: """Obtiene todas las reservas asociadas a una comisión.""" return Reservation.query.filter_by( commission_id=commission_id ).order_by(Reservation.start_time.asc()).all()