2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								using  Elecciones.Database ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  Elecciones.Database.Entities ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  Elecciones.Infrastructure.Services ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								using  Microsoft.EntityFrameworkCore ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								namespace  Elecciones.Worker ;  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								public  class  LowPriorityDataWorker  :  BackgroundService  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								{  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  readonly  ILogger < LowPriorityDataWorker >  _logger ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  readonly  SharedTokenService  _tokenService ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  readonly  IServiceProvider  _serviceProvider ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  readonly  IElectoralApiService  _apiService ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 17:29:50 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								  // Una variable para rastrear la tarea de telegramas, si está en ejecución. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  Task ?  _telegramasTask ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								  public  LowPriorityDataWorker ( 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 17:29:50 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								  ILogger < LowPriorityDataWorker >  logger , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  SharedTokenService  tokenService , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  IServiceProvider  serviceProvider , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  IElectoralApiService  apiService ) 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    _logger  =  logger ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    _tokenService  =  tokenService ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    _serviceProvider  =  serviceProvider ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    _apiService  =  apiService ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  protected  override  async  Task  ExecuteAsync ( CancellationToken  stoppingToken ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    _logger . LogInformation ( "Worker de Baja Prioridad iniciado." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 17:29:50 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    // La sincronización inicial sigue siendo un paso de bloqueo, es necesario. 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    await  SincronizarCatalogosMaestrosAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    while  ( ! stoppingToken . IsCancellationRequested ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "--- Iniciando Ciclo de Datos de Baja Prioridad ---" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  authToken  =  await  _tokenService . GetValidAuthTokenAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( string . IsNullOrEmpty ( authToken ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogError ( "Ciclo de Baja Prioridad: No se pudo obtener token. Reintentando en 1 minuto." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  Task . Delay ( TimeSpan . FromMinutes ( 1 ) ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        continue ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 17:29:50 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								      // --- LÓGICA DE EJECUCIÓN INDEPENDIENTE --- 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // 1. TAREA DE BANCAS: Siempre se ejecuta y se espera. Es rápida. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Iniciando sondeo de Bancas..." ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								      await  SondearProyeccionBancasAsync ( authToken ,  stoppingToken ) ; 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 17:29:50 -03:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Sondeo de Bancas completado." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // 2. TAREA DE TELEGRAMAS: "Dispara y Olvida" de forma segura. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Comprobamos si la tarea anterior de telegramas ya ha terminado. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( _telegramasTask  = =  null  | |  _telegramasTask . IsCompleted ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "Iniciando sondeo de Telegramas en segundo plano..." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Lanzamos la tarea de telegramas pero NO la esperamos con 'await'. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Guardamos una referencia a la tarea en nuestra variable de estado. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _telegramasTask  =  SondearNuevosTelegramasAsync ( authToken ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      else 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Si la descarga anterior todavía está en curso, nos saltamos este sondeo 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // para no acumular tareas y sobrecargar el sistema. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "El sondeo de telegramas anterior sigue en ejecución. Se omitirá en este ciclo." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
									
										
										
										
											2025-08-20 16:58:18 -03:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "--- Ciclo de Datos de Baja Prioridad completado. Esperando 5 minutos. ---" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  Task . Delay ( TimeSpan . FromMinutes ( 5 ) ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      catch  ( TaskCanceledException ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Descarga y sincroniza los catálogos base (Categorías, Ámbitos, Agrupaciones) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// desde la API a la base de datos local. Se ejecuta una sola vez al iniciar el worker. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Utiliza una estrategia de guardado en lotes para manejar grandes volúmenes de datos 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// sin sobrecargar la base de datos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <param name="stoppingToken">El token de cancelación para detener la operación.</param> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  async  Task  SincronizarCatalogosMaestrosAsync ( CancellationToken  stoppingToken ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Iniciando sincronización de catálogos maestros..." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // --- CORRECCIÓN: Usar el _tokenService inyectado --- 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  authToken  =  await  _tokenService . GetValidAuthTokenAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( string . IsNullOrEmpty ( authToken )  | |  stoppingToken . IsCancellationRequested ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogError ( "No se pudo obtener token para la sincronización de catálogos. La operación se cancela." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Creamos un scope de servicios para obtener una instancia fresca de DbContext. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      using  var  scope  =  _serviceProvider . CreateScope ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  dbContext  =  scope . ServiceProvider . GetRequiredService < EleccionesDbContext > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // PASO 2: Sincronizar las categorías electorales. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Es un catálogo pequeño y es la base para las siguientes consultas. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  categoriasApi  =  await  _apiService . GetCategoriasAsync ( authToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( categoriasApi  is  null  | |  ! categoriasApi . Any ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogWarning ( "La API no devolvió datos para el catálogo de Categorías. La sincronización no puede continuar." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  distinctCategorias  =  categoriasApi . GroupBy ( c  = >  c . CategoriaId ) . Select ( g  = >  g . First ( ) ) . OrderBy ( c  = >  c . Orden ) . ToList ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Se procesarán {count} categorías electorales." ,  distinctCategorias . Count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  categoriasEnDb  =  await  dbContext . CategoriasElectorales . ToDictionaryAsync ( c  = >  c . Id ,  c  = >  c ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  categoriaDto  in  distinctCategorias ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( ! categoriasEnDb . ContainsKey ( categoriaDto . CategoriaId ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          dbContext . CategoriasElectorales . Add ( new  CategoriaElectoral  {  Id  =  categoriaDto . CategoriaId ,  Nombre  =  categoriaDto . Nombre ,  Orden  =  categoriaDto . Orden  } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Guardamos las categorías primero para asegurar su existencia. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      await  dbContext . SaveChangesAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // PASO 3: Cargar los catálogos existentes en memoria para una comparación eficiente. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Esto evita hacer miles de consultas a la BD dentro de un bucle. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Para los ámbitos, creamos una clave única robusta que funciona incluso con campos nulos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  ambitosEnDb  =  new  Dictionary < string ,  AmbitoGeografico > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  todosLosAmbitos  =  await  dbContext . AmbitosGeograficos . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  ambito  in  todosLosAmbitos ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        string  clave  =  $"{ambito.NivelId}|{ambito.DistritoId}|{ambito.SeccionProvincialId}|{ambito.SeccionId}|{ambito.MunicipioId}|{ambito.CircuitoId}|{ambito.EstablecimientoId}|{ambito.MesaId}" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( ! ambitosEnDb . ContainsKey ( clave ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          ambitosEnDb . Add ( clave ,  ambito ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  agrupacionesEnDb  =  await  dbContext . AgrupacionesPoliticas . ToDictionaryAsync ( a  = >  a . Id ,  a  = >  a ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Variable para llevar la cuenta del total de registros insertados. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      int  totalCambiosGuardados  =  0 ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // PASO 4: Iterar sobre cada categoría para sincronizar sus ámbitos y agrupaciones. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  categoria  in  distinctCategorias ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( stoppingToken . IsCancellationRequested )  break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "--- Sincronizando datos para la categoría: {Nombre} (ID: {Id}) ---" ,  categoria . Nombre ,  categoria . CategoriaId ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        var  catalogoDto  =  await  _apiService . GetCatalogoAmbitosAsync ( authToken ,  categoria . CategoriaId ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( catalogoDto  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          // 4.1 - Procesar y añadir ÁMBITOS nuevos al DbContext 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          foreach  ( var  ambitoDto  in  catalogoDto . Ambitos ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            string  claveUnica  =  $"{ambitoDto.NivelId}|{ambitoDto.CodigoAmbitos.DistritoId}|{ambitoDto.CodigoAmbitos.SeccionProvincialId}|{ambitoDto.CodigoAmbitos.SeccionId}|{ambitoDto.CodigoAmbitos.MunicipioId}|{ambitoDto.CodigoAmbitos.CircuitoId}|{ambitoDto.CodigoAmbitos.EstablecimientoId}|{ambitoDto.CodigoAmbitos.MesaId}" ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( ! ambitosEnDb . ContainsKey ( claveUnica ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              var  nuevoAmbito  =  new  AmbitoGeografico 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                Nombre  =  ambitoDto . Nombre , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                NivelId  =  ambitoDto . NivelId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                DistritoId  =  ambitoDto . CodigoAmbitos . DistritoId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                SeccionProvincialId  =  ambitoDto . CodigoAmbitos . SeccionProvincialId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                SeccionId  =  ambitoDto . CodigoAmbitos . SeccionId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                MunicipioId  =  ambitoDto . CodigoAmbitos . MunicipioId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                CircuitoId  =  ambitoDto . CodigoAmbitos . CircuitoId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                EstablecimientoId  =  ambitoDto . CodigoAmbitos . EstablecimientoId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                MesaId  =  ambitoDto . CodigoAmbitos . MesaId , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              dbContext . AmbitosGeograficos . Add ( nuevoAmbito ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              ambitosEnDb . Add ( claveUnica ,  nuevoAmbito ) ;  // Añadir también al diccionario en memoria 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          // 4.2 - Procesar y añadir AGRUPACIONES nuevas al DbContext 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          var  provincia  =  catalogoDto . Ambitos . FirstOrDefault ( a  = >  a . NivelId  = =  10 ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  ( provincia  ! =  null  & &  ! string . IsNullOrEmpty ( provincia . CodigoAmbitos . DistritoId ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Usamos un try-catch porque no todas las categorías tienen agrupaciones a nivel provincial. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              var  agrupacionesApi  =  await  _apiService . GetAgrupacionesAsync ( authToken ,  provincia . CodigoAmbitos . DistritoId ,  categoria . CategoriaId ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              if  ( agrupacionesApi  ! =  null  & &  agrupacionesApi . Any ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                foreach  ( var  agrupacionDto  in  agrupacionesApi ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  if  ( ! agrupacionesEnDb . ContainsKey ( agrupacionDto . IdAgrupacion ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    var  nuevaAgrupacion  =  new  AgrupacionPolitica 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                      Id  =  agrupacionDto . IdAgrupacion , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                      IdTelegrama  =  agrupacionDto . IdAgrupacionTelegrama , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                      Nombre  =  agrupacionDto . NombreAgrupacion 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    dbContext . AgrupacionesPoliticas . Add ( nuevaAgrupacion ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    agrupacionesEnDb . Add ( nuevaAgrupacion . Id ,  nuevaAgrupacion ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              _logger . LogWarning ( ex ,  "No se pudieron obtener agrupaciones para la categoría '{catNombre}' ({catId})." ,  categoria . Nombre ,  categoria . CategoriaId ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Después de procesar todos los ámbitos y agrupaciones de UNA categoría, guardamos los cambios. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Esto divide la inserción masiva de ~50,000 registros en 3 transacciones más pequeñas, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // evitando timeouts y fallos en la base de datos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( dbContext . ChangeTracker . HasChanges ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          int  cambiosEnLote  =  await  dbContext . SaveChangesAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          totalCambiosGuardados  + =  cambiosEnLote ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          _logger . LogInformation ( "Guardados {count} registros de catálogo para la categoría '{catNombre}'." ,  cambiosEnLote ,  categoria . Nombre ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Ya no hay un SaveChangesAsync() gigante aquí. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "{count} nuevos registros de catálogo han sido guardados en total." ,  totalCambiosGuardados ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Sincronización de catálogos maestros finalizada." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogError ( ex ,  "Ocurrió un error CRÍTICO durante la sincronización de catálogos." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Sondea la proyección de bancas. Este método ahora es más completo: 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// 1. Consulta el reparto de bancas a nivel PROVINCIAL para cada categoría. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// 2. Consulta el reparto de bancas desglosado por SECCIÓN ELECTORAL para cada categoría. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Esta versión recolecta todos los datos disponibles y los guarda en una única transacción. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  async  Task  SondearProyeccionBancasAsync ( string  authToken ,  CancellationToken  stoppingToken ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      using  var  scope  =  _serviceProvider . CreateScope ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  dbContext  =  scope . ServiceProvider . GetRequiredService < EleccionesDbContext > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  categoriasDeBancas  =  await  dbContext . CategoriasElectorales 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . AsNoTracking ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . Where ( c  = >  c . Nombre . Contains ( "SENADORES" )  | |  c . Nombre . Contains ( "DIPUTADOS" ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  provincia  =  await  dbContext . AmbitosGeograficos 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . AsNoTracking ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . FirstOrDefaultAsync ( a  = >  a . NivelId  = =  10 ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  seccionesElectorales  =  await  dbContext . AmbitosGeograficos 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . AsNoTracking ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . Where ( a  = >  a . NivelId  = =  20  & &  a . DistritoId  ! =  null  & &  a . SeccionProvincialId  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( ! categoriasDeBancas . Any ( )  | |  provincia  = =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogWarning ( "No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales..." ,  seccionesElectorales . Count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Creamos una lista para recolectar todas las proyecciones que encontremos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  nuevasProyecciones  =  new  List < ProyeccionBanca > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // 1. Bucle para el nivel Provincial 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  categoria  in  categoriasDeBancas ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( stoppingToken . IsCancellationRequested )  break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        var  repartoBancas  =  await  _apiService . GetBancasAsync ( authToken ,  provincia . DistritoId ! ,  null ,  categoria . Id ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Si la lista de bancas no es nula (incluso si está vacía), la procesamos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( repartoBancas ? . RepartoBancas  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          foreach  ( var  banca  in  repartoBancas . RepartoBancas ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            nuevasProyecciones . Add ( new  ProyeccionBanca 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              AmbitoGeograficoId  =  provincia . Id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              AgrupacionPoliticaId  =  banca . IdAgrupacion , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              NroBancas  =  banca . NroBancas 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // 2. Bucle para el nivel de Sección Electoral 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  seccion  in  seccionesElectorales ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  ( stoppingToken . IsCancellationRequested )  break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        foreach  ( var  categoria  in  categoriasDeBancas ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  ( stoppingToken . IsCancellationRequested )  break ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          var  repartoBancas  =  await  _apiService . GetBancasAsync ( authToken ,  seccion . DistritoId ! ,  seccion . SeccionProvincialId ! ,  categoria . Id ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  ( repartoBancas ? . RepartoBancas  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            foreach  ( var  banca  in  repartoBancas . RepartoBancas ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              nuevasProyecciones . Add ( new  ProyeccionBanca 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                AmbitoGeograficoId  =  seccion . Id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                AgrupacionPoliticaId  =  banca . IdAgrupacion , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                NroBancas  =  banca . NroBancas 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              } ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // 3. Guardado Final 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Ahora la condición es simple: si nuestra lista recolectora tiene CUALQUIER COSA, actualizamos la BD. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( nuevasProyecciones . Any ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "Se recibieron {count} registros de proyección de bancas. Actualizando la tabla..." ,  nuevasProyecciones . Count ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  using  var  transaction  =  await  dbContext . Database . BeginTransactionAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  dbContext . Database . ExecuteSqlRawAsync ( "DELETE FROM ProyeccionesBancas" ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  dbContext . ProyeccionesBancas . AddRangeAsync ( nuevasProyecciones ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  dbContext . SaveChangesAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        await  transaction . CommitAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "Sondeo de Bancas completado. La tabla de proyecciones ha sido actualizada." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      else 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        // Si después de todas las llamadas, la lista sigue vacía, no hacemos nada. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        _logger . LogInformation ( "Sondeo de Bancas completado. No se encontraron datos de proyección, la tabla no fue modificada." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogError ( ex ,  "Ocurrió un error CRÍTICO en el sondeo de Bancas." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Busca y descarga nuevos telegramas de forma masiva y concurrente. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// Este método crea una lista de todas las combinaciones de Partido/Categoría, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// las consulta a la API con un grado de paralelismo controlado, y cada tarea concurrente 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// maneja su propia lógica de descarga y guardado en la base de datos. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// </summary> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <param name="authToken">El token de autenticación válido para la sesión.</param> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  /// <param name="stoppingToken">El token de cancelación para detener la operación.</param> 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  private  async  Task  SondearNuevosTelegramasAsync ( string  authToken ,  CancellationToken  stoppingToken ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    try 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "--- Iniciando sondeo de Nuevos Telegramas (modo de bajo perfil) ---" ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      using  var  scope  =  _serviceProvider . CreateScope ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  dbContext  =  scope . ServiceProvider . GetRequiredService < EleccionesDbContext > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  partidos  =  await  dbContext . AmbitosGeograficos 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . AsNoTracking ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . Where ( a  = >  a . NivelId  = =  30  & &  a . DistritoId  ! =  null  & &  a . SeccionId  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      var  categorias  =  await  dbContext . CategoriasElectorales 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . AsNoTracking ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      if  ( ! partidos . Any ( )  | |  ! categorias . Any ( ) )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // --- LÓGICA DE GOTEO LENTO --- 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      // Procesamos una combinación (partido/categoría) a la vez. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      foreach  ( var  partido  in  partidos ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        foreach  ( var  categoria  in  categorias ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          // Si la aplicación se apaga, salimos inmediatamente. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  ( stoppingToken . IsCancellationRequested )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          // Obtenemos la lista de IDs. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          var  listaTelegramasApi  =  await  _apiService . GetTelegramasTotalizadosAsync ( authToken ,  partido . DistritoId ! ,  partido . SeccionId ! ,  categoria . Id ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          if  ( listaTelegramasApi  is  {  Count :  >  0  } ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            // Usamos un DbContext propio para este bloque para asegurar que los cambios se guarden. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            using  var  innerScope  =  _serviceProvider . CreateScope ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  innerDbContext  =  innerScope . ServiceProvider . GetRequiredService < EleccionesDbContext > ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  idsYaEnDb  =  await  innerDbContext . Telegramas 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                . Where ( t  = >  listaTelegramasApi . Contains ( t . Id ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                . Select ( t  = >  t . Id ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                . ToListAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            var  nuevosTelegramasIds  =  listaTelegramasApi . Except ( idsYaEnDb ) . ToList ( ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  ( nuevosTelegramasIds . Any ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              _logger . LogInformation ( "Se encontraron {count} telegramas nuevos en '{partido}' para '{cat}'. Descargando..." ,  nuevosTelegramasIds . Count ,  partido . Nombre ,  categoria . Nombre ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              // Descargamos los archivos de uno en uno, con una pausa entre cada uno. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              foreach  ( var  mesaId  in  nuevosTelegramasIds ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( stoppingToken . IsCancellationRequested )  return ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                var  telegramaFile  =  await  _apiService . GetTelegramaFileAsync ( authToken ,  mesaId ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( telegramaFile  ! =  null ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  var  nuevoTelegrama  =  new  Telegrama 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    Id  =  telegramaFile . NombreArchivo , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    AmbitoGeograficoId  =  partido . Id , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    ContenidoBase64  =  telegramaFile . Imagen , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    FechaEscaneo  =  DateTime . Parse ( telegramaFile . FechaEscaneo ) . ToUniversalTime ( ) , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    FechaTotalizacion  =  DateTime . Parse ( telegramaFile . FechaTotalizacion ) . ToUniversalTime ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  } ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                  await  innerDbContext . Telegramas . AddAsync ( nuevoTelegrama ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                // PAUSA DELIBERADA: Esperamos un poco para no parecer un bot. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                await  Task . Delay ( 250 ,  stoppingToken ) ;  // 250ms de espera = 4 peticiones/segundo máximo. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								              await  innerDbContext . SaveChangesAsync ( stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          // PAUSA DELIBERADA: Esperamos un poco entre cada consulta de lista de telegramas. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								          await  Task . Delay ( 100 ,  stoppingToken ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Sondeo de Telegramas completado." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    catch  ( OperationCanceledException ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogInformation ( "Sondeo de telegramas cancelado." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    catch  ( Exception  ex ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								      _logger . LogError ( ex ,  "Ocurrió un error CRÍTICO en el sondeo de Telegramas." ) ; 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  // Pega aquí los métodos: 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  // - SincronizarCatalogosMaestrosAsync 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  // - SondearProyeccionBancasAsync 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								  // - SondearNuevosTelegramasAsync (la versión con goteo lento) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}