from flask import Blueprint, request, jsonify, g from pydantic import ValidationError from app.schemas.optimizer_dto import OptimizerJobRequestDTO, SpringBootSyncPayloadDTO from app.services.optimizer_service import OptimizerService from app.utils.jwt_decorators import jwt_required api_optimizer_bp = Blueprint('api_optimizer', __name__, url_prefix='/api/v1/optimizer') @api_optimizer_bp.route('/jobs', methods=['POST']) @jwt_required def submit_optimization_job(): """ Despacha un trabajo de optimización heurística asíncrono. Retorna inmediatamente con HTTP 202 Accepted y el identificador de trabajo para sondeo. """ data = request.get_json(silent=True) or {} try: dto = OptimizerJobRequestDTO(**data) except ValidationError as ex: return jsonify({ 'error': 'UnprocessableEntity', 'message': 'Error de validación en los parámetros del optimizador.', 'details': ex.errors() }), 422 user_id = g.current_user.id if hasattr(g, 'current_user') and g.current_user else 1 job = OptimizerService.submit_job( commission_ids=dto.commission_ids, admin_user_id=user_id, start_date=dto.start_date, end_date=dto.end_date, generations=dto.generations, population_size=dto.population_size, workers=dto.workers ) return jsonify({ 'job_id': job.job_id, 'status': job.status.value, 'poll_url': f'/api/v1/optimizer/jobs/{job.job_id}', 'message': 'Trabajo de optimización puesto en cola con éxito.' }), 202 @api_optimizer_bp.route('/jobs/', methods=['GET']) @jwt_required def get_optimization_job(job_id: str): """ Obtiene el estado, progreso y resultados de un trabajo de optimización en cola. """ job = OptimizerService.get_job(job_id) if not job: return jsonify({ 'error': 'NotFound', 'message': f'Trabajo de optimización {job_id} no encontrado.' }), 404 return jsonify(job.to_dict()), 200 @api_optimizer_bp.route('/jobs', methods=['GET']) @jwt_required def list_optimization_jobs(): """ Lista los trabajos de optimización más recientes. """ limit = request.args.get('limit', default=20, type=int) jobs = OptimizerService.list_jobs(limit=limit) return jsonify({ 'total': len(jobs), 'jobs': jobs }), 200 @api_optimizer_bp.route('/spring-boot/sync', methods=['POST']) @jwt_required def sync_with_spring_boot(): """ Endpoint interoperable para la sincronización y resolución de horarios con microservicios Spring Boot. Recibe el payload en formato Spring Boot (camelCase) y retorna el cronograma optimizado. """ data = request.get_json(silent=True) or {} try: dto = SpringBootSyncPayloadDTO(**data) except ValidationError as ex: return jsonify({ 'error': 'UnprocessableEntity', 'message': 'Error de validación en el payload Spring Boot.', 'details': ex.errors() }), 422 user_id = g.current_user.id if hasattr(g, 'current_user') and g.current_user else 1 try: result = OptimizerService.sync_with_spring_boot(dto, admin_user_id=user_id) return jsonify(result), 200 except Exception as ex: return jsonify({ 'requestId': dto.requestId, 'status': 'ERROR', 'error': str(ex) }), 500