Fix Bacas - Itera Sobre Cada Provincia Y Categoría
This commit is contained in:
		| @@ -71,12 +71,10 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   /// <summary> |   /// <summary> | ||||||
|   /// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral. |   /// Sondea la proyección de bancas a nivel Provincial. | ||||||
|   /// Esta versión es completamente robusta: maneja respuestas de API vacías o con fechas mal formadas, |   /// Esta versión corregida itera sobre cada provincia y consulta las categorías específicas | ||||||
|   /// guarda la CategoriaId y usa una transacción atómica para la escritura en base de datos. |   /// (Diputados para todas, Senadores solo para las que renuevan). | ||||||
|   /// </summary> |   /// </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 | ||||||
| @@ -84,94 +82,96 @@ public class LowPriorityDataWorker : BackgroundService | |||||||
|       using var scope = _serviceProvider.CreateScope(); |       using var scope = _serviceProvider.CreateScope(); | ||||||
|       var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); |       var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); | ||||||
|  |  | ||||||
|       var categoriasDeBancas = await dbContext.CategoriasElectorales |       // 1. Definimos las constantes y la lista de provincias que renuevan senadores. | ||||||
|           .AsNoTracking() |       const int catDiputadosNac = 3; | ||||||
|           .Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS")) |       const int catSenadoresNac = 2; | ||||||
|           .ToListAsync(stoppingToken); |       var provinciasQueRenuevanSenadores = new HashSet<string> { "01", "06", "08", "15", "16", "17", "22", "24" }; | ||||||
|  |  | ||||||
|       var ambitosASondear = await dbContext.AmbitosGeograficos |       // 2. Obtenemos las provincias (ámbitos de Nivel 10) de nuestra base de datos. | ||||||
|  |       var ambitosProvinciales = await dbContext.AmbitosGeograficos | ||||||
|           .AsNoTracking() |           .AsNoTracking() | ||||||
|           .Where(a => a.NivelId == 10 && a.DistritoId != null) |           .Where(a => a.NivelId == 10 && a.DistritoId != null) | ||||||
|           .ToListAsync(stoppingToken); |           .ToListAsync(stoppingToken); | ||||||
|  |  | ||||||
|       if (!categoriasDeBancas.Any() || !ambitosASondear.Any()) |       if (!ambitosProvinciales.Any()) | ||||||
|       { |       { | ||||||
|         _logger.LogWarning("No se encontraron categorías de bancas o ámbitos provinciales en la BD. Omitiendo sondeo de bancas."); |         _logger.LogWarning("No se encontraron ámbitos provinciales en la BD. Omitiendo sondeo de bancas."); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       _logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial para {count} provincias...", ambitosASondear.Count); |       _logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial para {count} provincias...", ambitosProvinciales.Count); | ||||||
|  |  | ||||||
|       var todasLasProyecciones = new List<ProyeccionBanca>(); |       var todasLasProyecciones = new List<ProyeccionBanca>(); | ||||||
|       bool hasReceivedAnyNewData = false; |       bool hasReceivedAnyNewData = false; | ||||||
|       var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken); |       var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken); | ||||||
|  |  | ||||||
|       foreach (var ambito in ambitosASondear) |       // 3. Iteramos sobre CADA provincia para hacer las llamadas específicas. | ||||||
|  |       foreach (var provincia in ambitosProvinciales) | ||||||
|       { |       { | ||||||
|         if (stoppingToken.IsCancellationRequested) break; |         if (stoppingToken.IsCancellationRequested) break; | ||||||
|         foreach (var categoria in categoriasDeBancas) |  | ||||||
|  |         // Función auxiliar para no repetir código. Procesa la respuesta de la API. | ||||||
|  |         async Task ProcesarRespuestaApi(Core.DTOs.RepartoBancasDto? dto, int categoriaId) | ||||||
|         { |         { | ||||||
|           if (stoppingToken.IsCancellationRequested) break; |           if (dto?.RepartoBancas is not { Count: > 0 } bancas) return; | ||||||
|  |  | ||||||
|           var repartoBancasDto = await _apiService.GetBancasAsync(authToken, ambito.DistritoId!, ambito.SeccionProvincialId, categoria.Id); |           hasReceivedAnyNewData = true; | ||||||
|           if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas) |           DateTime fechaTotalizacion = DateTime.TryParse(dto.FechaTotalizacion, out var parsedDate) ? parsedDate : DateTime.UtcNow; | ||||||
|  |  | ||||||
|  |           foreach (var banca in bancas) | ||||||
|           { |           { | ||||||
|             hasReceivedAnyNewData = true; |             if (!agrupacionesEnDb.ContainsKey(banca.IdAgrupacion)) | ||||||
|             DateTime fechaTotalizacion; |  | ||||||
|             if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate)) |  | ||||||
|             { |             { | ||||||
|               fechaTotalizacion = DateTime.Now; |               var nuevaAgrupacion = new AgrupacionPolitica | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|               fechaTotalizacion = parsedDate; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             foreach (var banca in bancas) |  | ||||||
|             { |  | ||||||
|               if (!agrupacionesEnDb.ContainsKey(banca.IdAgrupacion)) |  | ||||||
|               { |               { | ||||||
|                 var nuevaAgrupacion = new AgrupacionPolitica |                 Id = banca.IdAgrupacion, | ||||||
|                 { |                 Nombre = banca.NombreAgrupacion, | ||||||
|                   Id = banca.IdAgrupacion, |                 IdTelegrama = banca.IdAgrupacionTelegrama ?? string.Empty | ||||||
|                   Nombre = banca.NombreAgrupacion, |               }; | ||||||
|                   IdTelegrama = banca.IdAgrupacionTelegrama ?? string.Empty |               await dbContext.AgrupacionesPoliticas.AddAsync(nuevaAgrupacion, stoppingToken); | ||||||
|                 }; |               agrupacionesEnDb.Add(nuevaAgrupacion.Id, nuevaAgrupacion); | ||||||
|                 await dbContext.AgrupacionesPoliticas.AddAsync(nuevaAgrupacion, stoppingToken); |  | ||||||
|                 agrupacionesEnDb.Add(nuevaAgrupacion.Id, nuevaAgrupacion); |  | ||||||
|               } |  | ||||||
|               todasLasProyecciones.Add(new ProyeccionBanca |  | ||||||
|               { |  | ||||||
|                 EleccionId = EleccionId, |  | ||||||
|                 AmbitoGeograficoId = ambito.Id, |  | ||||||
|                 AgrupacionPoliticaId = banca.IdAgrupacion, |  | ||||||
|                 NroBancas = banca.NroBancas, |  | ||||||
|                 CategoriaId = categoria.Id, |  | ||||||
|                 FechaTotalizacion = fechaTotalizacion |  | ||||||
|               }); |  | ||||||
|             } |             } | ||||||
|  |             todasLasProyecciones.Add(new ProyeccionBanca | ||||||
|  |             { | ||||||
|  |               EleccionId = EleccionId, | ||||||
|  |               AmbitoGeograficoId = provincia.Id, | ||||||
|  |               AgrupacionPoliticaId = banca.IdAgrupacion, | ||||||
|  |               NroBancas = banca.NroBancas, | ||||||
|  |               CategoriaId = categoriaId, // Usamos la categoría correcta | ||||||
|  |               FechaTotalizacion = fechaTotalizacion | ||||||
|  |             }); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         // 4. Siempre consultamos por Diputados (Categoría 3) para la provincia actual. | ||||||
|  |         //    Pasamos 'null' en seccionProvincialId para obtener el total provincial. | ||||||
|  |         var diputadosDto = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, catDiputadosNac); | ||||||
|  |         await ProcesarRespuestaApi(diputadosDto, catDiputadosNac); | ||||||
|  |  | ||||||
|  |         if (stoppingToken.IsCancellationRequested) break; | ||||||
|  |  | ||||||
|  |         // 5. SOLO si la provincia está en la lista, consultamos por Senadores (Categoría 2). | ||||||
|  |         if (provinciasQueRenuevanSenadores.Contains(provincia.DistritoId!)) | ||||||
|  |         { | ||||||
|  |           var senadoresDto = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, catSenadoresNac); | ||||||
|  |           await ProcesarRespuestaApi(senadoresDto, catSenadoresNac); | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  |       // La lógica para guardar en la base de datos se mantiene, ya que es robusta. | ||||||
|       if (hasReceivedAnyNewData) |       if (hasReceivedAnyNewData) | ||||||
|       { |       { | ||||||
|         _logger.LogInformation("Se recibieron datos válidos de bancas. Procediendo a actualizar la base de datos..."); |         _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); | ||||||
|  |  | ||||||
|         // Guardar nuevas agrupaciones si las hay |  | ||||||
|         if (dbContext.ChangeTracker.HasChanges()) |         if (dbContext.ChangeTracker.HasChanges()) | ||||||
|         { |         { | ||||||
|           await dbContext.SaveChangesAsync(stoppingToken); |           await dbContext.SaveChangesAsync(stoppingToken); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // --- LÍNEA CORREGIDA --- |  | ||||||
|         // Se reemplaza ExecuteSqlRawAsync por ExecuteDeleteAsync, que es type-safe |  | ||||||
|         // y maneja correctamente el CancellationToken. |  | ||||||
|         await dbContext.ProyeccionesBancas |         await dbContext.ProyeccionesBancas | ||||||
|             .Where(p => p.EleccionId == EleccionId) |             .Where(p => p.EleccionId == EleccionId && (p.CategoriaId == catDiputadosNac || p.CategoriaId == catSenadoresNac)) | ||||||
|             .ExecuteDeleteAsync(stoppingToken); |             .ExecuteDeleteAsync(stoppingToken); | ||||||
|         // --- FIN DE LA CORRECCIÓN --- |  | ||||||
|  |  | ||||||
|         await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken); |         await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken); | ||||||
|         await dbContext.SaveChangesAsync(stoppingToken); |         await dbContext.SaveChangesAsync(stoppingToken); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user