Fix bancas y telegramas
This commit is contained in:
		| @@ -83,13 +83,21 @@ public class ElectoralApiService : IElectoralApiService | |||||||
|         return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<RepartoBancasDto>() : null; |         return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<RepartoBancasDto>() : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public async Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId) |     public async Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null) | ||||||
|     { |     { | ||||||
|         var client = _httpClientFactory.CreateClient("ElectoralApiClient"); |         var client = _httpClientFactory.CreateClient("ElectoralApiClient"); | ||||||
|         var requestUri = $"/api/resultados/getTelegramasTotalizados?distritoId={distritoId}&seccionId={seccionId}"; |         var requestUri = $"/api/resultados/getTelegramasTotalizados?distritoId={distritoId}&seccionId={seccionId}"; | ||||||
|  |  | ||||||
|  |         // Añadimos el parámetro categoriaId a la URL SÓLO si se proporciona un valor. | ||||||
|  |         if (categoriaId.HasValue) | ||||||
|  |         { | ||||||
|  |             requestUri += $"&categoriaId={categoriaId.Value}"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         var request = new HttpRequestMessage(HttpMethod.Get, requestUri); |         var request = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||||||
|         request.Headers.Add("Authorization", $"Bearer {authToken}"); |         request.Headers.Add("Authorization", $"Bearer {authToken}"); | ||||||
|         var response = await client.SendAsync(request); |         var response = await client.SendAsync(request); | ||||||
|  |         // Si la respuesta es 400, devolvemos null para que el worker sepa que falló. | ||||||
|         return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<string[]>>() : null; |         return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<string[]>>() : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ public interface IElectoralApiService | |||||||
|     Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId); |     Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId); | ||||||
|     Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId); |     Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId); | ||||||
|     Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId); |     Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId); | ||||||
|     Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId); |     Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null); | ||||||
|     Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId); |     Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId); | ||||||
|     Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId); |     Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId); | ||||||
|     Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId); |     Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId); | ||||||
|   | |||||||
| @@ -466,96 +466,83 @@ public class Worker : BackgroundService | |||||||
|     { |     { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             // PASO 1: Preparar el DbContext |  | ||||||
|             // Creamos un scope para obtener una instancia fresca de DbContext para esta operación. |  | ||||||
|             using var scope = _serviceProvider.CreateScope(); |             using var scope = _serviceProvider.CreateScope(); | ||||||
|             var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); |             var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); | ||||||
|  |  | ||||||
|             // PASO 2: Obtener los Partidos/Municipios (NivelId = 30) |             // Obtenemos todos los partidos (NivelId=30) y todas las categorías para iterar | ||||||
|             // --- CORRECCIÓN CLAVE --- |  | ||||||
|             // La API requiere un 'seccionId' para listar telegramas, y el manual lo define como "Id del partido". |  | ||||||
|             // La respuesta de getCatalogo nos confirmó que los registros de Partidos/Municipios usan NivelId = 30. |  | ||||||
|             var partidos = await dbContext.AmbitosGeograficos |             var partidos = await dbContext.AmbitosGeograficos | ||||||
|                 .AsNoTracking() // Optimización: Solo necesitamos leer estos datos. |                 .AsNoTracking() | ||||||
|                 .Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null) |                 .Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null) | ||||||
|                 .ToListAsync(stoppingToken); |                 .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|             // Si la sincronización inicial no cargó ningún partido, no podemos continuar. |             var categorias = await dbContext.CategoriasElectorales | ||||||
|             if (!partidos.Any()) |                 .AsNoTracking() | ||||||
|  |                 .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|  |             if (!partidos.Any() || !categorias.Any()) | ||||||
|             { |             { | ||||||
|                 _logger.LogWarning("No se encontraron Partidos (NivelId 30) en la BD para sondear telegramas."); |                 _logger.LogWarning("No se encontraron partidos o categorías en la BD para sondear telegramas."); | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             _logger.LogInformation("Iniciando sondeo de Telegramas nuevos para {count} partidos...", partidos.Count); |             _logger.LogInformation("Iniciando sondeo de Telegramas nuevos para {partidosCount} partidos y {categoriasCount} categorías...", partidos.Count, categorias.Count); | ||||||
|  |  | ||||||
|             // PASO 3: Iterar sobre cada Partido para buscar telegramas. |  | ||||||
|             foreach (var partido in partidos) |             foreach (var partido in partidos) | ||||||
|             { |             { | ||||||
|                 if (stoppingToken.IsCancellationRequested) break; // Salida limpia si la aplicación se detiene. |                 if (stoppingToken.IsCancellationRequested) break; | ||||||
|  |  | ||||||
|                 // 3.1 - OBTENER LA LISTA COMPLETA DE IDs DE LA API PARA EL PARTIDO ACTUAL |                 foreach (var categoria in categorias) | ||||||
|                 var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!); |  | ||||||
|  |  | ||||||
|                 // Solo procesamos si la API devolvió una lista con al menos un telegrama. |  | ||||||
|                 if (listaTelegramasApi is { Count: > 0 }) |  | ||||||
|                 { |                 { | ||||||
|                     // La API devuelve una lista de arrays de string (ej. [ ["id1"], ["id2"] ]). |                     if (stoppingToken.IsCancellationRequested) break; | ||||||
|                     // Usamos .Select(t => t[0]) para extraer solo el ID de cada sub-array. |  | ||||||
|                     var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList(); |  | ||||||
|  |  | ||||||
|                     // 3.2 - COMPARAR CON LA BASE DE DATOS LOCAL PARA ENCONTRAR LOS NUEVOS |                     // Llamamos al servicio pasando la categoriaId. | ||||||
|                     // Esta es una consulta muy eficiente: le pedimos a la BD que nos devuelva solo los IDs que |                     // Si mañana la API ya no lo necesita, simplemente lo ignorará. | ||||||
|                     // ya existen de la lista que nos acaba de dar la API. |                     // Si lo vuelve a necesitar en el futuro, nuestro código ya está preparado. | ||||||
|                     var idsYaEnDb = await dbContext.Telegramas |                     var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!, categoria.Id); | ||||||
|                         .Where(t => idsDeApi.Contains(t.Id)) |  | ||||||
|                         .Select(t => t.Id) |  | ||||||
|                         .ToListAsync(stoppingToken); |  | ||||||
|  |  | ||||||
|                     // Comparamos las dos listas para obtener una tercera: la de los IDs que están en la API pero no en nuestra BD. |                     // El resto de la lógica es la misma: si la respuesta es válida y contiene datos... | ||||||
|                     var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList(); |                     if (listaTelegramasApi is { Count: > 0 }) | ||||||
|  |  | ||||||
|                     // Si no hay telegramas nuevos para este partido, simplemente continuamos con el siguiente. |  | ||||||
|                     if (!nuevosTelegramasIds.Any()) |  | ||||||
|                     { |                     { | ||||||
|                         continue; |                         var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList(); | ||||||
|                     } |                         var idsYaEnDb = await dbContext.Telegramas | ||||||
|  |                             .Where(t => idsDeApi.Contains(t.Id)) | ||||||
|  |                             .Select(t => t.Id) | ||||||
|  |                             .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|                     _logger.LogInformation("Se encontraron {count} telegramas nuevos en el partido '{nombrePartido}' ({seccionId}). Descargando...", nuevosTelegramasIds.Count, partido.Nombre, partido.SeccionId); |                         var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList(); | ||||||
|  |  | ||||||
|                     // 3.3 - DESCARGAR Y GUARDAR CADA NUEVO TELEGRAMA |                         if (!nuevosTelegramasIds.Any()) | ||||||
|                     foreach (var mesaId in nuevosTelegramasIds) |  | ||||||
|                     { |  | ||||||
|                         if (stoppingToken.IsCancellationRequested) break; |  | ||||||
|  |  | ||||||
|                         // Descargamos el contenido completo del telegrama (imagen base64 y metadatos). |  | ||||||
|                         var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId); |  | ||||||
|                         if (telegramaFile != null) |  | ||||||
|                         { |                         { | ||||||
|                             var nuevoTelegrama = new Telegrama |                             continue; | ||||||
|                             { |  | ||||||
|                                 Id = telegramaFile.NombreArchivo, |  | ||||||
|                                 AmbitoGeograficoId = partido.Id, // Lo asociamos al ID del partido. |  | ||||||
|                                 ContenidoBase64 = telegramaFile.Imagen, |  | ||||||
|                                 FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(), |  | ||||||
|                                 FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime() |  | ||||||
|                             }; |  | ||||||
|                             // Añadimos la nueva entidad a la sesión de EF Core para su inserción. |  | ||||||
|                             await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken); |  | ||||||
|                         } |                         } | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     // Guardamos los cambios en la base de datos después de procesar todos los telegramas nuevos de UN partido. |                         _logger.LogInformation("Se encontraron {count} telegramas nuevos en el partido '{nombre}' para la categoría '{cat}'. Descargando...", nuevosTelegramasIds.Count, partido.Nombre, categoria.Nombre); | ||||||
|                     // Esto es más eficiente que guardar en cada iteración del bucle interno. |  | ||||||
|                     await dbContext.SaveChangesAsync(stoppingToken); |                         foreach (var mesaId in nuevosTelegramasIds) | ||||||
|  |                         { | ||||||
|  |                             if (stoppingToken.IsCancellationRequested) break; | ||||||
|  |                             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 dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         await dbContext.SaveChangesAsync(stoppingToken); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             _logger.LogInformation("Sondeo de Telegramas completado."); |             _logger.LogInformation("Sondeo de Telegramas completado."); | ||||||
|         } |         } | ||||||
|         catch (Exception ex) |         catch (Exception ex) | ||||||
|         { |         { | ||||||
|             // Capturamos cualquier error para que no detenga el worker y lo registramos para depuración. |  | ||||||
|             _logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas."); |             _logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas."); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user