Fix Bancas 2
This commit is contained in:
		| @@ -248,51 +248,85 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|   /// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral. |   /// 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. |   /// Esta versión recolecta todos los datos disponibles y los guarda en una única transacción. | ||||||
|   /// </summary> |   /// </summary> | ||||||
|  |   /// <summary> | ||||||
|  |   /// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral. | ||||||
|  |   /// Esta versión incluye logging detallado para diagnosticar problemas de deserialización | ||||||
|  |   /// o respuestas inesperadas de la API, y guarda los datos en una transacción atómica. | ||||||
|  |   /// </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 SondearProyeccionBancasAsync(string authToken, CancellationToken stoppingToken) |   private async Task SondearProyeccionBancasAsync(string authToken, CancellationToken stoppingToken) | ||||||
|   { |   { | ||||||
|     try |     try | ||||||
|     { |     { | ||||||
|  |       // Usamos un scope de DbContext para esta operación, asegurando una conexión limpia. | ||||||
|       using var scope = _serviceProvider.CreateScope(); |       using var scope = _serviceProvider.CreateScope(); | ||||||
|       var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); |       var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); | ||||||
|  |  | ||||||
|  |       // 1. OBTENER DATOS DE CONFIGURACIÓN DE NUESTRA BD | ||||||
|  |  | ||||||
|  |       // Buscamos las categorías que reparten bancas (Senadores y Diputados). | ||||||
|       var categoriasDeBancas = await dbContext.CategoriasElectorales |       var categoriasDeBancas = await dbContext.CategoriasElectorales | ||||||
|           .AsNoTracking() |           .AsNoTracking() | ||||||
|           .Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS")) |           .Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS")) | ||||||
|           .ToListAsync(stoppingToken); |           .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|  |       // Obtenemos el registro de la Provincia para hacer la consulta a nivel provincial. | ||||||
|       var provincia = await dbContext.AmbitosGeograficos |       var provincia = await dbContext.AmbitosGeograficos | ||||||
|           .AsNoTracking() |           .AsNoTracking() | ||||||
|           .FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); |           .FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); | ||||||
|  |  | ||||||
|  |       // Obtenemos todas las Secciones Electorales para hacer las consultas por sección. | ||||||
|       var seccionesElectorales = await dbContext.AmbitosGeograficos |       var seccionesElectorales = await dbContext.AmbitosGeograficos | ||||||
|           .AsNoTracking() |           .AsNoTracking() | ||||||
|           .Where(a => a.NivelId == 20 && a.DistritoId != null && a.SeccionProvincialId != null) |           .Where(a => a.NivelId == 20 && a.DistritoId != null && a.SeccionProvincialId != null) | ||||||
|           .ToListAsync(stoppingToken); |           .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|  |       // Si falta alguna configuración base, no podemos continuar. | ||||||
|       if (!categoriasDeBancas.Any() || provincia == null) |       if (!categoriasDeBancas.Any() || provincia == null) | ||||||
|       { |       { | ||||||
|         _logger.LogWarning("No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo."); |         _logger.LogWarning("No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo de bancas."); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       _logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales...", seccionesElectorales.Count); |       _logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales...", seccionesElectorales.Count); | ||||||
|  |  | ||||||
|       // --- LÓGICA CORREGIDA --- |       // 2. RECOLECTAR DATOS DE LA API | ||||||
|       bool hasReceivedAnyNewData = false; |  | ||||||
|       var todasLasProyecciones = new List<ProyeccionBanca>(); |  | ||||||
|  |  | ||||||
|       // 1. Bucle Provincial |       // Una lista para acumular todas las proyecciones que encontremos. | ||||||
|  |       var todasLasProyecciones = new List<ProyeccionBanca>(); | ||||||
|  |       // Una bandera para saber si hemos recibido al menos una banca con datos. | ||||||
|  |       bool hasReceivedAnyNewData = false; | ||||||
|  |  | ||||||
|  |       // Bucle para el nivel Provincial | ||||||
|       foreach (var categoria in categoriasDeBancas) |       foreach (var categoria in categoriasDeBancas) | ||||||
|       { |       { | ||||||
|         if (stoppingToken.IsCancellationRequested) break; |         if (stoppingToken.IsCancellationRequested) break; | ||||||
|  |  | ||||||
|         var repartoBancas = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, categoria.Id); |         var repartoBancas = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, categoria.Id); | ||||||
|  |  | ||||||
|         // Comprobamos si la respuesta no es nula y si la lista de bancas TIENE ELEMENTOS. |         // --- Lógica de Verificación Explícita --- | ||||||
|         if (repartoBancas?.RepartoBancas is { Count: > 0 } bancas) |         if (repartoBancas == null) | ||||||
|         { |         { | ||||||
|           // Si encontramos datos, activamos la bandera. |           // Esto ocurriría si GetBancasAsync devuelve null (ej. por un error 500 o un JsonException). | ||||||
|           hasReceivedAnyNewData = true; |           _logger.LogWarning("La respuesta para bancas provinciales (cat: {catId}) fue nula o inválida.", categoria.Id); | ||||||
|           foreach (var banca in bancas) |           continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (repartoBancas.RepartoBancas == null) | ||||||
|  |         { | ||||||
|  |           // Esto es por si la API devuelve un JSON válido pero sin la propiedad "repartoBancas". | ||||||
|  |           _logger.LogWarning("La lista 'RepartoBancas' en la respuesta provincial (cat: {catId}) es nula.", categoria.Id); | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (repartoBancas.RepartoBancas.Count > 0) | ||||||
|  |         { | ||||||
|  |           // ¡Éxito! Hemos encontrado datos. | ||||||
|  |           _logger.LogInformation("Se encontraron {count} registros de bancas a nivel provincial para la categoría {catId}.", repartoBancas.RepartoBancas.Count, categoria.Id); | ||||||
|  |           hasReceivedAnyNewData = true; // Activamos la bandera. | ||||||
|  |  | ||||||
|  |           foreach (var banca in repartoBancas.RepartoBancas) | ||||||
|           { |           { | ||||||
|             todasLasProyecciones.Add(new ProyeccionBanca |             todasLasProyecciones.Add(new ProyeccionBanca | ||||||
|             { |             { | ||||||
| @@ -304,7 +338,7 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // 2. Bucle por Sección |       // Bucle para el nivel de Sección Electoral | ||||||
|       foreach (var seccion in seccionesElectorales) |       foreach (var seccion in seccionesElectorales) | ||||||
|       { |       { | ||||||
|         if (stoppingToken.IsCancellationRequested) break; |         if (stoppingToken.IsCancellationRequested) break; | ||||||
| @@ -313,10 +347,10 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|           if (stoppingToken.IsCancellationRequested) break; |           if (stoppingToken.IsCancellationRequested) break; | ||||||
|           var repartoBancas = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionProvincialId!, categoria.Id); |           var repartoBancas = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionProvincialId!, categoria.Id); | ||||||
|  |  | ||||||
|           if (repartoBancas?.RepartoBancas is { Count: > 0 } bancas) |           if (repartoBancas?.RepartoBancas?.Count > 0) | ||||||
|           { |           { | ||||||
|             hasReceivedAnyNewData = true; |             hasReceivedAnyNewData = true; | ||||||
|             foreach (var banca in bancas) |             foreach (var banca in repartoBancas.RepartoBancas) | ||||||
|             { |             { | ||||||
|               todasLasProyecciones.Add(new ProyeccionBanca |               todasLasProyecciones.Add(new ProyeccionBanca | ||||||
|               { |               { | ||||||
| @@ -329,27 +363,31 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       // 3. Guardado Final |       // 3. GUARDAR DATOS EN LA BASE DE DATOS | ||||||
|       // La lógica de guardado ahora depende de la bandera. |  | ||||||
|  |       // Solo procedemos a escribir en la BD si la bandera se activó. | ||||||
|       if (hasReceivedAnyNewData) |       if (hasReceivedAnyNewData) | ||||||
|       { |       { | ||||||
|         _logger.LogInformation("Se recibieron {count} registros de proyección de bancas. Actualizando la tabla...", todasLasProyecciones.Count); |         _logger.LogInformation("Se recibieron datos válidos de bancas. Procediendo a actualizar la base de datos..."); | ||||||
|  |  | ||||||
|         await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken); |         await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken); | ||||||
|  |  | ||||||
|         await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken); |         await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken); | ||||||
|         // Guardamos la lista completa, incluso los que tienen 0 bancas. |  | ||||||
|         await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken); |         await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken); | ||||||
|         await dbContext.SaveChangesAsync(stoppingToken); |         await dbContext.SaveChangesAsync(stoppingToken); | ||||||
|         await transaction.CommitAsync(stoppingToken); |         await transaction.CommitAsync(stoppingToken); | ||||||
|  |  | ||||||
|         _logger.LogInformation("Sondeo de Bancas completado. La tabla de proyecciones ha sido actualizada."); |         _logger.LogInformation("La tabla de proyecciones ha sido actualizada con {count} registros.", todasLasProyecciones.Count); | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
|         _logger.LogInformation("Sondeo de Bancas completado. No se encontraron datos nuevos de proyección, la tabla no fue modificada."); |         _logger.LogInformation("Sondeo de Bancas completado. No se encontraron datos nuevos de proyección, la tabla no fue modificada."); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |     catch (OperationCanceledException) | ||||||
|  |     { | ||||||
|  |       _logger.LogInformation("Sondeo de bancas cancelado."); | ||||||
|  |     } | ||||||
|     catch (Exception ex) |     catch (Exception ex) | ||||||
|     { |     { | ||||||
|       _logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Bancas."); |       _logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Bancas."); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user