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