The constraint validation system uses the Chain of Responsibility pattern to validate room assignments and schedules. Each validation rule is encapsulated in a handler that can pass control to the next handler in the chain.
Location: src/core/restrictions/aulas/aula_compatible_handler.pyValidates: Room type matches course requirements
# Validation rulesif asignatura.tipo == 'laboratorio': # Must be a lab room if aula.tipo != 'laboratorio': return f"El aula de tipo {aula.tipo} no es compatible con la asignatura de tipo laboratorio"
Location: src/core/restrictions/aulas/aula_no_ocupada_doble_handler.pyValidates: Room is not occupied at the requested time
# Time overlap checkdef horarios_solapan(h1, h2): return not (h1.hora_fin <= h2.hora_inicio or h2.hora_fin <= h1.hora_inicio)if horarios_solapan(nuevo_horario, horario_existente): return f"Aula ocupada en el horario {hora_inicio}-{hora_fin}"
The availability check considers all reservations with reservado or ocupado status, regardless of semester.
from src.core.restrictions.restriction_handler import RestrictionHandlerfrom typing import Optional, Dict, Anyclass RestrictionHandler(ABC): """Base class for all constraint handlers.""" def __init__(self, next_handler: Optional['RestrictionHandler'] = None): self._next_handler = next_handler def set_next(self, next_handler: 'RestrictionHandler') -> 'RestrictionHandler': """Set the next handler in the chain.""" self._next_handler = next_handler return next_handler def handle(self, context: Dict[str, Any]) -> Optional[str]: """Execute validation and pass to next handler.""" # Validate context structure context_validation = self._validate_context_structure(context) if context_validation is not None: return context_validation # Execute specific validation result = self.validate(context) if result is not None: # Validation failed return result # Pass to next handler if self._next_handler: return self._next_handler.handle(context) return None @abstractmethod def validate(self, context: Dict[str, Any]) -> Optional[str]: """Implement specific validation logic.""" raise NotImplementedError("Must implement validate() in subclasses")
class ResourceRequirementHandler(RestrictionHandler): """Validates room has all required resources for the course.""" def validate(self, context: Dict[str, Any]) -> Optional[str]: aula = context.get('aula') asignatura = context.get('asignatura') if not aula or not asignatura: return "Missing required context: aula or asignatura" required_resources = set(asignatura.get('requiereRecursos', [])) available_resources = set(aula.get('id_recursos', [])) missing = required_resources - available_resources if missing: return (f"Aula no tiene los recursos requeridos: " f"{', '.join(missing)}") return None
Ensure teacher’s other classes are on the same campus:
class SameCampusHandler(RestrictionHandler): """Validates room is on the same campus as teacher's other classes.""" def validate(self, context: Dict[str, Any]) -> Optional[str]: aula = context.get('aula') docente = context.get('docente') programaciones = context.get('programaciones', []) dia = context.get('dia') if not all([aula, docente, dia]): return None # Skip if not all data available aula_sede = aula.get('id_sede') docente_id = docente.get('id') # Find teacher's other classes on same day other_classes = [ p for p in programaciones if p.get('docente_id') == docente_id and p.get('dia') == dia and p.get('estado') in ['reservado', 'ocupado'] ] if other_classes: # Get sedes from other classes other_sedes = set() aulas_dict = context.get('aulas', []) for clase in other_classes: aula_id = clase.get('aula_id') aula_info = next((a for a in aulas_dict if a['id'] == aula_id), None) if aula_info: other_sedes.add(aula_info.get('id_sede')) if other_sedes and aula_sede not in other_sedes: return (f"El docente tiene clases en otra sede el {dia}. " f"Se recomienda mantener todas las clases en la misma sede.") return None
# Specific and actionable"Capacidad insuficiente: 25 lugares para 30 estudiantes""El aula de tipo laboratorio no es compatible con la asignatura de tipo teorica""Aula ocupada o reservada en el horario 14:00-17:00 el Miércoles""Aula no tiene los recursos requeridos: R003 (Computadoras)"