Fix fecha totalizados nivel provincia

This commit is contained in:
2025-08-23 13:19:35 -03:00
parent 13c6accd15
commit 8192185bc5
12 changed files with 518 additions and 36 deletions

View File

@@ -184,59 +184,119 @@ public class CriticalDataWorker : BackgroundService
}
/// <summary>
/// Obtiene y actualiza el resumen de votos a nivel provincial.
/// Esta versión mejorada utiliza una transacción para garantizar la consistencia de los datos.
/// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial.
/// Esta versión actualizada guarda tanto los votos por agrupación (en ResumenesVotos)
/// como el estado general del recuento, incluyendo la fecha de totalización (en EstadosRecuentosGenerales),
/// asegurando que toda la operación sea atómica mediante una transacción de 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 SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
{
try
{
// Creamos un scope de DbContext para esta operación.
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
if (provincia == null) return;
// Obtenemos el registro de la Provincia (NivelId 10).
var provincia = await dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
var resumen = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!);
// --- CAMBIO CLAVE: Lógica de actualización robusta ---
// Solo procedemos si la respuesta de la API es válida Y contiene datos de votos positivos.
if (resumen?.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
// Si no encontramos el ámbito de la provincia, no podemos continuar.
if (provincia == null)
{
// Usamos una transacción explícita para asegurar que la operación sea atómica:
// O se completa todo (borrado e inserción), o no se hace nada.
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) para el sondeo de resumen.");
return;
}
// Llamamos a la API para obtener el resumen de datos provincial.
var resumenDto = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!);
// Solo procedemos si la API devolvió una respuesta válida y no nula.
if (resumenDto != null)
{
// Iniciamos una transacción explícita. Esto garantiza que todas las operaciones de base de datos
// dentro de este bloque (el DELETE, los INSERTs y los UPDATEs) se completen con éxito,
// o si algo falla, se reviertan todas, manteniendo la consistencia de los datos.
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
// 1. Borramos los datos viejos.
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
// --- 1. ACTUALIZAR LA TABLA 'ResumenesVotos' ---
// 2. Insertamos los nuevos datos.
foreach (var voto in nuevosVotos)
// Verificamos si la respuesta contiene una lista de votos positivos.
if (resumenDto.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
{
dbContext.ResumenesVotos.Add(new ResumenVoto
// Estrategia "Borrar y Reemplazar": vaciamos la tabla antes de insertar los nuevos datos.
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
// Añadimos cada nuevo registro de voto al DbContext.
foreach (var voto in nuevosVotos)
{
AmbitoGeograficoId = provincia.Id,
AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje
});
dbContext.ResumenesVotos.Add(new ResumenVoto
{
AmbitoGeograficoId = provincia.Id,
AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje
});
}
}
// 3. Guardamos los cambios y confirmamos la transacción.
// --- 2. ACTUALIZAR LA TABLA 'EstadosRecuentosGenerales' ---
// El endpoint de Resumen no especifica una categoría, por lo que aplicamos sus datos de estado de recuento
// a todas las categorías que tenemos en nuestra base de datos.
var todasLasCategorias = await dbContext.CategoriasElectorales.AsNoTracking().ToListAsync(stoppingToken);
foreach (var categoria in todasLasCategorias)
{
// Buscamos el registro existente usando la clave primaria compuesta.
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(new object[] { provincia.Id, categoria.Id }, stoppingToken);
// Si no existe, lo creamos.
if (registroDb == null)
{
registroDb = new EstadoRecuentoGeneral { AmbitoGeograficoId = provincia.Id, CategoriaId = categoria.Id };
dbContext.EstadosRecuentosGenerales.Add(registroDb);
}
// Parseamos la fecha de forma segura para evitar errores con cadenas vacías o nulas.
if (DateTime.TryParse(resumenDto.FechaTotalizacion, out var parsedDate))
{
registroDb.FechaTotalizacion = parsedDate.ToUniversalTime();
}
// Mapeamos el resto de los datos del estado del recuento.
registroDb.MesasEsperadas = resumenDto.EstadoRecuento.MesasEsperadas;
registroDb.MesasTotalizadas = resumenDto.EstadoRecuento.MesasTotalizadas;
registroDb.MesasTotalizadasPorcentaje = resumenDto.EstadoRecuento.MesasTotalizadasPorcentaje;
registroDb.CantidadElectores = resumenDto.EstadoRecuento.CantidadElectores;
registroDb.CantidadVotantes = resumenDto.EstadoRecuento.CantidadVotantes;
registroDb.ParticipacionPorcentaje = resumenDto.EstadoRecuento.ParticipacionPorcentaje;
}
// 3. CONFIRMAR Y GUARDAR
// Guardamos todos los cambios preparados (DELETEs, INSERTs, UPDATEs) en la base de datos.
await dbContext.SaveChangesAsync(stoppingToken);
// Confirmamos la transacción para hacer los cambios permanentes.
await transaction.CommitAsync(stoppingToken);
_logger.LogInformation("Sondeo de Resumen Provincial completado. La tabla ha sido actualizada.");
_logger.LogInformation("Sondeo de Resumen Provincial completado. Las tablas han sido actualizadas.");
}
else
{
// Si la API no devuelve datos de votos, no hacemos NADA en la base de datos.
_logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos, la tabla no fue modificada.");
// Si la API no devolvió datos (ej. devuelve null), no hacemos nada en la BD.
_logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos.");
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Sondeo de resumen provincial cancelado.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error en el sondeo de Resumen Provincial.");
// Capturamos cualquier otro error inesperado para que el worker no se detenga.
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resumen Provincial.");
}
}