import requests import logging import os import socket import getpass from typing import Optional, Dict, Any from datetime import datetime logger = logging.getLogger(__name__) class TrackingManager: """Gestiona la comunicación con el servidor de seguimiento web.""" def __init__(self, base_url: str = "http://localhost:8000"): self.base_url = base_url self.current_batch_id: Optional[str] = None self.is_enabled = True self._session = requests.Session() # Usar sesión para reutilizar conexiones def start_batch(self, root_path: str, total_radicados: int, config: Optional[Dict] = None, csv_output_path: Optional[str] = None): """Notifica el inicio de un nuevo lote al servidor.""" if not self.is_enabled: return try: payload = { "root_path": root_path, "total_radicados": total_radicados, "csv_output_path": csv_output_path, "hostname": socket.gethostname(), "username": getpass.getuser(), "config": config or {} } response = self._session.post(f"{self.base_url}/batches", json=payload, timeout=5) if response.status_code == 200: self.current_batch_id = response.json().get("id") logger.info(f"Tracking iniciado: Batch ID {self.current_batch_id}") else: logger.warning(f"Error al iniciar tracking: {response.status_code}") except Exception as e: logger.error(f"Error de conexión con el servidor de tracking: {e}") def update_progress(self, current_index: int, success_count: int, error_count: int, total_tokens: int, total_cost_usd: float, status: str = "processing"): """Actualiza el progreso del lote actual.""" if not self.is_enabled or not self.current_batch_id: return try: payload = { "current_radicado_index": current_index, "success_count": success_count, "error_count": error_count, "total_tokens": total_tokens, "total_cost_usd": total_cost_usd, "status": status } self._session.patch(f"{self.base_url}/batches/{self.current_batch_id}", json=payload, timeout=1) except Exception as e: logger.debug(f"Error al actualizar tracking: {e}") def log(self, message: str, level: str = "info", radicado: str = None): """Envía una entrada de log al servidor de tracking.""" if not self.is_enabled or not self.current_batch_id: return try: payload = {"message": message, "level": level, "radicado": radicado} self._session.post( f"{self.base_url}/batches/{self.current_batch_id}/logs", json=payload, timeout=1 ) except Exception as e: logger.debug(f"Error al enviar log a tracking: {e}") def report_phase(self, phase: str, radicado: str = None): """Reporta la fase actual de procesamiento (extract, normalize, csv, completed).""" if not self.is_enabled or not self.current_batch_id: return try: payload = {"current_phase": phase, "current_radicado": radicado} self._session.patch(f"{self.base_url}/batches/{self.current_batch_id}", json=payload, timeout=1) except Exception as e: logger.debug(f"Error al reportar fase a tracking: {e}") def report_error(self, error_message: str): """Reporta un error crítico al servidor.""" if not self.is_enabled or not self.current_batch_id: return try: payload = {"last_error": error_message} self._session.patch(f"{self.base_url}/batches/{self.current_batch_id}", json=payload, timeout=1) except Exception as e: logger.debug(f"Error al reportar error de tracking: {e}") def complete_batch(self, success_count: int, error_count: int, total_tokens: int, total_cost_usd: float): """Notifica la finalización exitosa del lote.""" self.update_progress( current_index=-1, # Indica finalización si se desea success_count=success_count, error_count=error_count, total_tokens=total_tokens, total_cost_usd=total_cost_usd, status="completed" ) self.current_batch_id = None