119 lines
5.2 KiB
Python
119 lines
5.2 KiB
Python
from typing import Optional, List, Dict, Any
|
|
import re
|
|
from sqlalchemy import func
|
|
from app.models.classroom import Classroom
|
|
from app.models.building import Building
|
|
from app.repositories.classroom_repository import ClassroomRepository
|
|
from app.schemas.classroom_dto import ClassroomCreateDTO, ClassroomUpdateDTO
|
|
from app import db
|
|
|
|
class ClassroomService:
|
|
"""Capa de servicios para la lógica de negocio y validación de aulas."""
|
|
|
|
def __init__(self, repository: Optional[ClassroomRepository] = None):
|
|
self.repository = repository or ClassroomRepository()
|
|
|
|
def get_classroom(self, classroom_id: int) -> Optional[Classroom]:
|
|
"""Obtiene un aula por su identificador."""
|
|
return self.repository.get_by_id(classroom_id)
|
|
|
|
def list_classrooms(self, building_id: Optional[int] = None, floor: Optional[str] = None,
|
|
only_virtual: bool = False, only_physical: bool = False) -> List[Classroom]:
|
|
"""Lista aulas filtradas según criterios de ubicación o modalidad."""
|
|
return self.repository.get_active_classrooms(
|
|
building_id=building_id,
|
|
floor=floor,
|
|
only_virtual=only_virtual,
|
|
only_physical=only_physical
|
|
)
|
|
|
|
def create_classroom(self, dto: ClassroomCreateDTO) -> Classroom:
|
|
"""
|
|
Crea una nueva aula aplicando reglas de negocio:
|
|
- Resolución o creación dinámica del edificio.
|
|
- Unicidad de código por piso y edificio.
|
|
- Capacidad adaptativa para aulas virtuales.
|
|
"""
|
|
building = None
|
|
if dto.building_id:
|
|
building = Building.query.get(dto.building_id)
|
|
if not building:
|
|
raise ValueError(f"El edificio con ID {dto.building_id} no existe.")
|
|
elif dto.building_name:
|
|
b_name = dto.building_name.strip()
|
|
building = Building.query.filter(func.lower(Building.name) == func.lower(b_name)).first()
|
|
if not building:
|
|
code_slug = re.sub(r'[^a-zA-Z0-9]+', '_', b_name.upper()).strip('_')
|
|
building = Building(name=b_name, code=code_slug, is_active=True)
|
|
db.session.add(building)
|
|
db.session.flush()
|
|
else:
|
|
raise ValueError("Debe especificar un edificio válido o el nombre de un nuevo edificio.")
|
|
|
|
# Verificar unicidad
|
|
existing = self.repository.get_by_code_and_building(building.id, dto.code, dto.floor)
|
|
if existing:
|
|
raise ValueError(f"Ya existe un aula con el código '{dto.code}' en el piso {dto.floor} de {building.name}.")
|
|
|
|
capacity = dto.capacity
|
|
is_virtual = ('VIRTUAL' in dto.code.upper()) or ('VIRTUAL' in building.name.upper())
|
|
if is_virtual and capacity < 999:
|
|
capacity = 9999
|
|
|
|
classroom = Classroom(
|
|
building_id=building.id,
|
|
building=building.name,
|
|
code=dto.code,
|
|
floor=dto.floor,
|
|
capacity=capacity,
|
|
description=dto.description,
|
|
is_active=dto.is_active
|
|
)
|
|
return self.repository.save(classroom)
|
|
|
|
def update_classroom(self, classroom_id: int, dto: ClassroomUpdateDTO) -> Classroom:
|
|
"""Actualiza atributos de un aula existente."""
|
|
classroom = self.repository.get_by_id(classroom_id)
|
|
if not classroom:
|
|
raise ValueError(f"Aula con ID {classroom_id} no encontrada.")
|
|
|
|
building_id = dto.building_id if dto.building_id is not None else classroom.building_id
|
|
code = dto.code.strip() if dto.code is not None else classroom.code
|
|
floor = dto.floor.strip() if dto.floor is not None else classroom.floor
|
|
|
|
# Si cambia código, piso o edificio, validar colisión
|
|
if (building_id != classroom.building_id or code != classroom.code or floor != classroom.floor):
|
|
existing = self.repository.get_by_code_and_building(building_id, code, floor)
|
|
if existing and existing.id != classroom.id:
|
|
raise ValueError(f"Ya existe otra aula con el código '{code}' en el piso {floor}.")
|
|
|
|
if dto.building_id is not None:
|
|
building = Building.query.get(dto.building_id)
|
|
if building:
|
|
classroom.building_id = building.id
|
|
classroom.building = building.name
|
|
if dto.code is not None:
|
|
classroom.code = code
|
|
if dto.floor is not None:
|
|
classroom.floor = floor
|
|
if dto.capacity is not None:
|
|
classroom.capacity = dto.capacity
|
|
if dto.description is not None:
|
|
classroom.description = dto.description
|
|
if dto.is_active is not None:
|
|
classroom.is_active = dto.is_active
|
|
|
|
return self.repository.save(classroom)
|
|
|
|
def delete_classroom(self, classroom_id: int) -> bool:
|
|
"""Elimina o desactiva un aula verificando dependencias."""
|
|
classroom = self.repository.get_by_id(classroom_id)
|
|
if not classroom:
|
|
return False
|
|
# Si tiene reservas históricas, se desactiva lógicamente para preservar integridad
|
|
if classroom.reservations:
|
|
classroom.is_active = False
|
|
self.repository.save(classroom)
|
|
return True
|
|
return self.repository.delete(classroom)
|