Files
Elecciones-2025/Elecciones-Web/src/Elecciones.Worker/CriticalDataWorker.cs

699 lines
30 KiB
C#
Raw Normal View History

//Elecciones.Worker/CriticalDataWorker.cs
2025-08-20 16:58:18 -03:00
using Elecciones.Database;
using Elecciones.Database.Entities;
using Elecciones.Infrastructure.Services;
using Microsoft.EntityFrameworkCore;
using System.Collections.Concurrent;
namespace Elecciones.Worker;
public class CriticalDataWorker : BackgroundService
{
2025-10-16 15:46:44 -03:00
private const int EleccionId = 2;
2025-08-20 16:58:18 -03:00
private readonly ILogger<CriticalDataWorker> _logger;
private readonly SharedTokenService _tokenService;
private readonly IServiceProvider _serviceProvider;
2025-08-23 11:01:54 -03:00
private readonly IElectoralApiService _apiService;
private readonly WorkerConfigService _configService;
2025-08-20 16:58:18 -03:00
public CriticalDataWorker(
ILogger<CriticalDataWorker> logger,
SharedTokenService tokenService,
IServiceProvider serviceProvider,
IElectoralApiService apiService,
WorkerConfigService configService)
2025-08-20 16:58:18 -03:00
{
_logger = logger;
_tokenService = tokenService;
_serviceProvider = serviceProvider;
2025-08-23 11:01:54 -03:00
_apiService = apiService;
_configService = configService;
2025-08-20 16:58:18 -03:00
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker de Datos Críticos iniciado.");
try
{
await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken);
}
2025-08-23 11:01:54 -03:00
catch (TaskCanceledException) { return; }
2025-08-20 16:58:18 -03:00
int cicloContador = 0;
while (!stoppingToken.IsCancellationRequested)
{
var cicloInicio = DateTime.UtcNow;
cicloContador++;
_logger.LogInformation("--- Iniciando Ciclo de Datos Críticos #{ciclo} ---", cicloContador);
var authToken = await _tokenService.GetValidAuthTokenAsync(stoppingToken);
if (string.IsNullOrEmpty(authToken))
{
_logger.LogError("Ciclo Crítico: No se pudo obtener token. Reintentando en 30 segundos.");
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
continue;
}
var settings = await _configService.GetSettingsAsync();
if (settings.Prioridad == "Resultados" && settings.ResultadosActivado)
{
_logger.LogInformation("Ejecutando tareas de Resultados en alta prioridad.");
2025-10-17 15:49:15 -03:00
await SondearEstadoRecuentoGeneralAsync(authToken, stoppingToken);
await SondearResultadosMunicipalesAsync(authToken, stoppingToken);
await SondearResumenProvincialAsync(authToken, stoppingToken);
}
else if (settings.Prioridad == "Telegramas" && settings.BajasActivado)
{
_logger.LogInformation("Ejecutando tareas de Baja Prioridad en alta prioridad.");
await SondearProyeccionBancasAsync(authToken, stoppingToken);
2025-10-17 10:12:12 -03:00
//await SondearNuevosTelegramasAsync(authToken, stoppingToken);
}
else
{
_logger.LogInformation("Worker de alta prioridad inactivo según la configuración.");
}
2025-08-20 16:58:18 -03:00
var cicloFin = DateTime.UtcNow;
var duracionCiclo = cicloFin - cicloInicio;
_logger.LogInformation("--- Ciclo de Datos Críticos #{ciclo} completado en {duration:N2} segundos. ---", cicloContador, duracionCiclo.TotalSeconds);
var tiempoDeEspera = TimeSpan.FromSeconds(30) - duracionCiclo;
if (tiempoDeEspera < TimeSpan.Zero) tiempoDeEspera = TimeSpan.Zero;
try
{
await Task.Delay(tiempoDeEspera, stoppingToken);
}
2025-08-23 11:01:54 -03:00
catch (TaskCanceledException) { break; }
2025-08-20 16:58:18 -03:00
}
}
/// <summary>
/// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral.
/// Esta versión es completamente robusta: maneja respuestas de API vacías o con fechas mal formadas,
/// guarda la CategoriaId y usa una transacción atómica para la escritura en 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 SondearProyeccionBancasAsync(string authToken, CancellationToken stoppingToken)
{
try
{
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var categoriasDeBancas = await dbContext.CategoriasElectorales
.AsNoTracking()
.Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS"))
.ToListAsync(stoppingToken);
2025-10-17 12:13:45 -03:00
// --- MODIFICACIÓN 1: Obtener todos los ámbitos en una sola consulta ---
var ambitosASondear = await dbContext.AmbitosGeograficos
.AsNoTracking()
2025-10-17 12:13:45 -03:00
.Where(a => (a.NivelId == 10 || a.NivelId == 20) && a.DistritoId != null)
.ToListAsync(stoppingToken);
2025-10-17 12:13:45 -03:00
var provincia = ambitosASondear.FirstOrDefault(a => a.NivelId == 10);
var seccionesElectorales = ambitosASondear.Where(a => a.NivelId == 20).ToList();
if (!categoriasDeBancas.Any() || provincia == null)
{
_logger.LogWarning("No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo de bancas.");
return;
}
_logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales...", seccionesElectorales.Count);
var todasLasProyecciones = new List<ProyeccionBanca>();
bool hasReceivedAnyNewData = false;
2025-10-17 12:13:45 -03:00
// --- MODIFICACIÓN 2: Usar un diccionario para no buscar repetidamente en la BD ---
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
2025-10-17 12:13:45 -03:00
// Bucle combinado para todos los ámbitos
foreach (var ambito in ambitosASondear)
{
if (stoppingToken.IsCancellationRequested) break;
2025-10-17 12:13:45 -03:00
foreach (var categoria in categoriasDeBancas)
{
if (stoppingToken.IsCancellationRequested) break;
2025-10-17 12:13:45 -03:00
// Llamada a la API (lógica adaptada para ambos niveles)
var repartoBancasDto = await _apiService.GetBancasAsync(authToken, ambito.DistritoId!, ambito.SeccionProvincialId, categoria.Id);
if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas)
{
hasReceivedAnyNewData = true;
DateTime fechaTotalizacion;
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
{
2025-10-17 12:13:45 -03:00
_logger.LogWarning("No se pudo parsear FechaTotalizacion ('{dateString}') para bancas. Usando la hora actual.", repartoBancasDto.FechaTotalizacion);
fechaTotalizacion = DateTime.UtcNow;
}
else
{
fechaTotalizacion = parsedDate.ToUniversalTime();
}
foreach (var banca in bancas)
{
2025-10-17 12:13:45 -03:00
// --- MODIFICACIÓN 3: Lógica de "Upsert" para Agrupaciones ---
if (!agrupacionesEnDb.ContainsKey(banca.IdAgrupacion))
{
_logger.LogWarning("Agrupación con ID {AgrupacionId} ('{Nombre}') no encontrada. Creándola desde los datos de bancas.", banca.IdAgrupacion, banca.NombreAgrupacion);
var nuevaAgrupacion = new AgrupacionPolitica
{
Id = banca.IdAgrupacion,
Nombre = banca.NombreAgrupacion,
IdTelegrama = banca.IdAgrupacionTelegrama ?? string.Empty
};
await dbContext.AgrupacionesPoliticas.AddAsync(nuevaAgrupacion, stoppingToken);
agrupacionesEnDb.Add(nuevaAgrupacion.Id, nuevaAgrupacion); // Añadir al diccionario para no volver a crearla
}
todasLasProyecciones.Add(new ProyeccionBanca
{
2025-10-16 15:46:44 -03:00
EleccionId = EleccionId,
2025-10-17 12:13:45 -03:00
AmbitoGeograficoId = ambito.Id,
AgrupacionPoliticaId = banca.IdAgrupacion,
NroBancas = banca.NroBancas,
CategoriaId = categoria.Id,
FechaTotalizacion = fechaTotalizacion
});
}
}
}
}
if (hasReceivedAnyNewData)
{
_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);
2025-10-17 12:13:45 -03:00
// Si se crearon nuevas agrupaciones, se guardarán aquí primero.
await dbContext.SaveChangesAsync(stoppingToken);
// Luego, procedemos con las proyecciones.
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);
await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken);
await dbContext.SaveChangesAsync(stoppingToken);
await transaction.CommitAsync(stoppingToken);
_logger.LogInformation("La tabla de proyecciones ha sido actualizada con {count} registros.", todasLasProyecciones.Count);
}
else
{
2025-10-17 12:13:45 -03:00
_logger.LogInformation("Sondeo de Bancas completado. No se encontraron datos nuevos, la tabla no fue modificada.");
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Sondeo de bancas cancelado.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Bancas.");
}
}
/// <summary>
/// Busca y descarga nuevos telegramas de forma masiva y concurrente.
/// Este método crea una lista de todas las combinaciones de Partido/Categoría,
/// las consulta a la API con un grado de paralelismo controlado, y cada tarea concurrente
/// maneja su propia lógica de descarga y guardado en la 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 SondearNuevosTelegramasAsync(string authToken, CancellationToken stoppingToken)
{
try
{
_logger.LogInformation("--- Iniciando sondeo de Nuevos Telegramas (modo de bajo perfil) ---");
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
2025-09-08 14:25:19 -03:00
// La obtención de partidos y categorías no cambia
var partidos = await dbContext.AmbitosGeograficos.AsNoTracking()
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
.ToListAsync(stoppingToken);
2025-09-08 14:25:19 -03:00
var categorias = await dbContext.CategoriasElectorales.AsNoTracking().ToListAsync(stoppingToken);
if (!partidos.Any() || !categorias.Any()) return;
foreach (var partido in partidos)
{
foreach (var categoria in categorias)
{
if (stoppingToken.IsCancellationRequested) return;
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!, categoria.Id);
if (listaTelegramasApi is { Count: > 0 })
{
2025-09-08 14:25:19 -03:00
// Creamos el DbContext para la operación de guardado
using var innerScope = _serviceProvider.CreateScope();
var innerDbContext = innerScope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var idsYaEnDb = await innerDbContext.Telegramas
.Where(t => listaTelegramasApi.Contains(t.Id))
2025-09-08 14:25:19 -03:00
.Select(t => t.Id).ToListAsync(stoppingToken);
var nuevosTelegramasIds = listaTelegramasApi.Except(idsYaEnDb).ToList();
if (nuevosTelegramasIds.Any())
{
_logger.LogInformation("Se encontraron {count} telegramas nuevos en '{partido}' para '{cat}'. Descargando...", nuevosTelegramasIds.Count, partido.Nombre, categoria.Nombre);
2025-09-08 14:25:19 -03:00
var originalTimeout = innerDbContext.Database.GetCommandTimeout();
try
{
2025-09-08 14:25:19 -03:00
innerDbContext.Database.SetCommandTimeout(180);
_logger.LogDebug("Timeout de BD aumentado a 180s para descarga de telegramas.");
int contadorLote = 0;
const int tamanoLote = 100;
2025-09-08 14:25:19 -03:00
foreach (var mesaId in nuevosTelegramasIds)
{
2025-09-08 14:25:19 -03:00
if (stoppingToken.IsCancellationRequested) return;
2025-09-08 14:25:19 -03:00
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
if (telegramaFile != null)
{
2025-09-08 14:25:19 -03:00
var ambitoMesa = await innerDbContext.AmbitosGeograficos.AsNoTracking()
.FirstOrDefaultAsync(a => a.MesaId == mesaId, stoppingToken);
if (ambitoMesa != null)
{
2025-09-08 14:25:19 -03:00
var nuevoTelegrama = new Telegrama
{
2025-10-16 15:46:44 -03:00
EleccionId = EleccionId,
2025-09-08 14:25:19 -03:00
Id = telegramaFile.NombreArchivo,
AmbitoGeograficoId = ambitoMesa.Id,
ContenidoBase64 = telegramaFile.Imagen,
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
};
await innerDbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
contadorLote++;
}
else
{
_logger.LogWarning("No se encontró un ámbito geográfico para la mesa con MesaId {MesaId}. El telegrama no será guardado.", mesaId);
}
}
2025-09-08 14:25:19 -03:00
await Task.Delay(250, stoppingToken);
if (contadorLote >= tamanoLote)
{
2025-09-08 14:25:19 -03:00
await innerDbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Guardado un lote de {count} telegramas.", contadorLote);
contadorLote = 0;
}
}
2025-09-08 10:22:51 -03:00
2025-09-08 14:25:19 -03:00
if (contadorLote > 0)
2025-09-08 10:22:51 -03:00
{
await innerDbContext.SaveChangesAsync(stoppingToken);
2025-09-08 14:25:19 -03:00
_logger.LogInformation("Guardado el último lote de {count} telegramas.", contadorLote);
2025-09-08 10:22:51 -03:00
}
}
2025-09-08 14:25:19 -03:00
finally
2025-09-08 10:22:51 -03:00
{
2025-09-08 14:25:19 -03:00
innerDbContext.Database.SetCommandTimeout(originalTimeout);
_logger.LogDebug("Timeout de BD restaurado a su valor original ({timeout}s).", originalTimeout);
}
2025-09-08 14:25:19 -03:00
} // Fin del if (nuevosTelegramasIds.Any())
// Movemos el delay aquí para que solo se ejecute si hubo telegramas en la respuesta de la API
await Task.Delay(100, stoppingToken);
} // Fin del if (listaTelegramasApi is not null)
}
}
_logger.LogInformation("Sondeo de Telegramas completado.");
}
catch (OperationCanceledException)
{
_logger.LogInformation("Sondeo de telegramas cancelado.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas.");
}
}
2025-08-20 16:58:18 -03:00
private async Task SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
{
try
{
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
var municipiosASondear = await dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
.ToListAsync(stoppingToken);
2025-08-23 11:01:54 -03:00
var todasLasCategorias = await dbContext.CategoriasElectorales
2025-08-20 16:58:18 -03:00
.AsNoTracking()
2025-08-23 11:01:54 -03:00
.ToListAsync(stoppingToken);
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
if (!municipiosASondear.Any() || !todasLasCategorias.Any())
2025-08-20 16:58:18 -03:00
{
2025-08-23 11:01:54 -03:00
_logger.LogWarning("No se encontraron Partidos (NivelId 30) o Categorías para sondear resultados.");
2025-08-20 16:58:18 -03:00
return;
}
2025-08-23 11:01:54 -03:00
_logger.LogInformation("Iniciando sondeo de resultados para {m} municipios y {c} categorías...", municipiosASondear.Count, todasLasCategorias.Count);
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
foreach (var municipio in municipiosASondear)
2025-08-20 16:58:18 -03:00
{
2025-08-23 11:01:54 -03:00
if (stoppingToken.IsCancellationRequested) break;
var tareasCategoria = todasLasCategorias.Select(async categoria =>
2025-08-20 16:58:18 -03:00
{
2025-08-23 11:01:54 -03:00
var resultados = await _apiService.GetResultadosAsync(authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoria.Id);
2025-08-20 16:58:18 -03:00
if (resultados != null)
{
2025-08-23 11:01:54 -03:00
using var innerScope = _serviceProvider.CreateScope();
var innerDbContext = innerScope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
// --- LLAMADA CORRECTA ---
await GuardarResultadosDeAmbitoAsync(innerDbContext, municipio.Id, categoria.Id, resultados, stoppingToken);
}
});
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
await Task.WhenAll(tareasCategoria);
2025-08-20 16:58:18 -03:00
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados municipales.");
}
}
2025-08-23 11:01:54 -03:00
private async Task GuardarResultadosDeAmbitoAsync(
EleccionesDbContext dbContext, int ambitoId, int categoriaId,
Elecciones.Core.DTOs.ResultadosDto resultadosDto, CancellationToken stoppingToken)
2025-08-20 16:58:18 -03:00
{
2025-08-23 11:01:54 -03:00
var estadoRecuento = await dbContext.EstadosRecuentos.FindAsync(new object[] { ambitoId, categoriaId }, stoppingToken);
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
if (estadoRecuento == null)
2025-08-20 16:58:18 -03:00
{
2025-10-16 15:46:44 -03:00
estadoRecuento = new EstadoRecuento { EleccionId = EleccionId, AmbitoGeograficoId = ambitoId, CategoriaId = categoriaId };
2025-08-23 11:01:54 -03:00
dbContext.EstadosRecuentos.Add(estadoRecuento);
}
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultadosDto.FechaTotalizacion).ToUniversalTime();
estadoRecuento.MesasEsperadas = resultadosDto.EstadoRecuento.MesasEsperadas;
estadoRecuento.MesasTotalizadas = resultadosDto.EstadoRecuento.MesasTotalizadas;
estadoRecuento.MesasTotalizadasPorcentaje = resultadosDto.EstadoRecuento.MesasTotalizadasPorcentaje;
estadoRecuento.CantidadElectores = resultadosDto.EstadoRecuento.CantidadElectores;
estadoRecuento.CantidadVotantes = resultadosDto.EstadoRecuento.CantidadVotantes;
estadoRecuento.ParticipacionPorcentaje = resultadosDto.EstadoRecuento.ParticipacionPorcentaje;
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
if (resultadosDto.ValoresTotalizadosOtros != null)
{
estadoRecuento.VotosEnBlanco = resultadosDto.ValoresTotalizadosOtros.VotosEnBlanco;
estadoRecuento.VotosEnBlancoPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosEnBlancoPorcentaje;
estadoRecuento.VotosNulos = resultadosDto.ValoresTotalizadosOtros.VotosNulos;
estadoRecuento.VotosNulosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosNulosPorcentaje;
estadoRecuento.VotosRecurridos = resultadosDto.ValoresTotalizadosOtros.VotosRecurridos;
estadoRecuento.VotosRecurridosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosRecurridosPorcentaje;
2025-10-15 11:44:22 -03:00
estadoRecuento.VotosComando = resultadosDto.ValoresTotalizadosOtros.VotosComando;
estadoRecuento.VotosComandoPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosComandoPorcentaje;
estadoRecuento.VotosImpugnados = resultadosDto.ValoresTotalizadosOtros.VotosImpugnados;
estadoRecuento.VotosImpugnadosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosImpugnadosPorcentaje;
2025-08-23 11:01:54 -03:00
}
2025-08-20 16:58:18 -03:00
2025-08-23 11:01:54 -03:00
foreach (var votoPositivoDto in resultadosDto.ValoresTotalizadosPositivos)
{
2025-10-16 15:34:12 -03:00
// PASO 1: VERIFICAR SI LA AGRUPACIÓN YA EXISTE EN NUESTRA BD
var agrupacion = await dbContext.AgrupacionesPoliticas.FindAsync(votoPositivoDto.IdAgrupacion);
// PASO 2: SI NO EXISTE, LA CREAMOS "SOBRE LA MARCHA"
if (agrupacion == null)
{
_logger.LogWarning("Agrupación con ID {AgrupacionId} ('{Nombre}') no encontrada en el catálogo local. Creándola desde los datos de resultados.",
votoPositivoDto.IdAgrupacion, votoPositivoDto.NombreAgrupacion);
agrupacion = new AgrupacionPolitica
{
Id = votoPositivoDto.IdAgrupacion,
Nombre = votoPositivoDto.NombreAgrupacion,
// El IdTelegrama puede ser nulo, usamos el operador '??' para asignar un string vacío si es así.
IdTelegrama = votoPositivoDto.IdAgrupacionTelegrama ?? string.Empty
};
await dbContext.AgrupacionesPoliticas.AddAsync(agrupacion, stoppingToken);
// No es necesario llamar a SaveChangesAsync aquí, se hará al final.
}
// PASO 3: CONTINUAR CON LA LÓGICA DE GUARDADO DEL VOTO
2025-08-23 11:01:54 -03:00
var resultadoVoto = await dbContext.ResultadosVotos.FirstOrDefaultAsync(
rv => rv.AmbitoGeograficoId == ambitoId &&
rv.CategoriaId == categoriaId &&
rv.AgrupacionPoliticaId == votoPositivoDto.IdAgrupacion,
stoppingToken
);
if (resultadoVoto == null)
2025-08-20 16:58:18 -03:00
{
2025-08-23 11:01:54 -03:00
resultadoVoto = new ResultadoVoto
2025-08-20 16:58:18 -03:00
{
2025-10-16 15:46:44 -03:00
EleccionId = EleccionId,
2025-08-23 11:01:54 -03:00
AmbitoGeograficoId = ambitoId,
CategoriaId = categoriaId,
AgrupacionPoliticaId = votoPositivoDto.IdAgrupacion
};
dbContext.ResultadosVotos.Add(resultadoVoto);
2025-08-20 16:58:18 -03:00
}
2025-08-23 11:01:54 -03:00
resultadoVoto.CantidadVotos = votoPositivoDto.Votos;
resultadoVoto.PorcentajeVotos = votoPositivoDto.VotosPorcentaje;
2025-08-20 16:58:18 -03:00
}
2025-08-23 11:01:54 -03:00
try
{
await dbContext.SaveChangesAsync(stoppingToken);
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, "DbUpdateException al guardar resultados para AmbitoId {ambitoId} y CategoriaId {categoriaId}", ambitoId, categoriaId);
}
2025-08-20 16:58:18 -03:00
}
/// <summary>
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
/// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial para CADA categoría.
/// Este método itera sobre todas las provincias y categorías, obteniendo sus resultados consolidados
/// y guardándolos en las tablas 'ResumenesVotos' y 'EstadosRecuentosGenerales'.
2025-08-20 16:58:18 -03:00
/// </summary>
private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
{
try
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
_logger.LogInformation("Iniciando sondeo de Resúmenes Provinciales...");
2025-08-20 16:58:18 -03:00
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
var provinciasASondear = await dbContext.AmbitosGeograficos
2025-08-23 13:19:35 -03:00
.AsNoTracking()
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
.Where(a => a.NivelId == 10 && a.DistritoId != null)
.ToListAsync(stoppingToken);
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
var todasLasCategorias = await dbContext.CategoriasElectorales
.AsNoTracking()
.ToListAsync(stoppingToken);
if (!provinciasASondear.Any() || !todasLasCategorias.Any())
2025-08-23 13:19:35 -03:00
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
_logger.LogWarning("No se encontraron Provincias o Categorías para sondear resúmenes.");
2025-08-23 13:19:35 -03:00
return;
}
2025-08-20 16:58:18 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
foreach (var provincia in provinciasASondear)
2025-08-20 16:58:18 -03:00
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
if (stoppingToken.IsCancellationRequested) break;
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
foreach (var categoria in todasLasCategorias)
2025-08-23 13:19:35 -03:00
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
if (stoppingToken.IsCancellationRequested) break;
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
// Usamos GetResultados sin seccionId/municipioId para obtener el resumen del distrito.
var resultadosDto = await _apiService.GetResultadosAsync(authToken, provincia.DistritoId!, null, null, categoria.Id);
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
if (resultadosDto?.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
{
// Usamos una transacción para asegurar que el borrado y la inserción sean atómicos.
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
2025-08-20 16:58:18 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
// A. Borrar los resúmenes viejos SOLO para esta provincia y categoría.
await dbContext.ResumenesVotos
.Where(rv => rv.AmbitoGeograficoId == provincia.Id && rv.CategoriaId == categoria.Id)
.ExecuteDeleteAsync(stoppingToken);
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
// B. Añadir los nuevos resúmenes.
foreach (var voto in nuevosVotos)
{
dbContext.ResumenesVotos.Add(new ResumenVoto
{
2025-10-16 15:46:44 -03:00
EleccionId = EleccionId,
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
AmbitoGeograficoId = provincia.Id,
CategoriaId = categoria.Id,
AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje
});
}
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
// C. Guardar los cambios en la tabla ResumenesVotos.
await dbContext.SaveChangesAsync(stoppingToken);
2025-08-23 13:19:35 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
// No es necesario actualizar EstadosRecuentosGenerales aquí,
// ya que el método SondearEstadoRecuentoGeneralAsync se encarga de eso
// de forma más específica y eficiente.
2025-08-20 16:58:18 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
await transaction.CommitAsync(stoppingToken);
}
} // Fin bucle categorías
} // Fin bucle provincias
2025-08-20 16:58:18 -03:00
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
_logger.LogInformation("Sondeo de Resúmenes Provinciales completado.");
2025-08-20 16:58:18 -03:00
}
2025-08-23 13:19:35 -03:00
catch (OperationCanceledException)
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
_logger.LogInformation("Sondeo de resúmenes provinciales cancelado.");
2025-08-23 13:19:35 -03:00
}
2025-08-20 16:58:18 -03:00
catch (Exception ex)
{
feat(Worker): Adaptación integral para la API de Elecciones Nacionales Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito. Cambios principales: - **Refactorización de `SondearResumenProvincialAsync`:** - Se elimina la dependencia del endpoint obsoleto `/getResumen`. - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados. - **Expansión de `SondearResultadosMunicipalesAsync`:** - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad. - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios. - **Modificación del Modelo de Datos:** - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`. - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio. - **Ajustes de Nulabilidad en API Service:** - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625. - **Deshabilitación de Seeders de Ejemplo:** - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
2025-10-14 16:00:55 -03:00
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resúmenes Provinciales.");
2025-08-20 16:58:18 -03:00
}
}
/// <summary>
/// Obtiene y actualiza el estado general del recuento a nivel provincial para CADA categoría electoral.
/// Esta versión es robusta: consulta dinámicamente las categorías, usa la clave primaria compuesta
/// de la base de datos y guarda todos los cambios en una única transacción al final.
/// </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 SondearEstadoRecuentoGeneralAsync(string authToken, CancellationToken stoppingToken)
{
try
{
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
2025-10-17 15:49:15 -03:00
var provinciasASondear = await dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 10 && a.DistritoId != null)
.ToListAsync(stoppingToken);
2025-08-20 16:58:18 -03:00
2025-10-17 15:49:15 -03:00
// Busca NivelId 1 (País) o 0 como fallback.
var ambitoNacional = await dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.NivelId == 1 || a.NivelId == 0, stoppingToken);
2025-08-20 16:58:18 -03:00
var categoriasParaSondear = await dbContext.CategoriasElectorales
.AsNoTracking()
.ToListAsync(stoppingToken);
2025-10-17 15:49:15 -03:00
if (!provinciasASondear.Any() || !categoriasParaSondear.Any())
2025-08-20 16:58:18 -03:00
{
2025-10-17 15:49:15 -03:00
_logger.LogWarning("No se encontraron Provincias o Categorías para sondear estado general.");
2025-08-20 16:58:18 -03:00
return;
}
2025-10-17 15:49:15 -03:00
_logger.LogInformation("Iniciando sondeo de Estado Recuento General para {provCount} provincias, el total nacional y {catCount} categorías...", provinciasASondear.Count, categoriasParaSondear.Count);
2025-08-20 16:58:18 -03:00
2025-10-17 15:49:15 -03:00
// Sondeo a nivel provincial
foreach (var provincia in provinciasASondear)
2025-08-20 16:58:18 -03:00
{
if (stoppingToken.IsCancellationRequested) break;
2025-10-17 15:49:15 -03:00
foreach (var categoria in categoriasParaSondear)
{
if (stoppingToken.IsCancellationRequested) break;
2025-08-20 16:58:18 -03:00
2025-10-17 15:49:15 -03:00
var estadoDto = await _apiService.GetEstadoRecuentoGeneralAsync(authToken, provincia.DistritoId!, categoria.Id);
if (estadoDto != null)
{
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(new object[] { provincia.Id, categoria.Id }, stoppingToken);
if (registroDb == null)
{
registroDb = new EstadoRecuentoGeneral { EleccionId = EleccionId, AmbitoGeograficoId = provincia.Id, CategoriaId = categoria.Id };
dbContext.EstadosRecuentosGenerales.Add(registroDb);
}
registroDb.FechaTotalizacion = DateTime.UtcNow;
registroDb.MesasEsperadas = estadoDto.MesasEsperadas;
registroDb.MesasTotalizadas = estadoDto.MesasTotalizadas;
registroDb.MesasTotalizadasPorcentaje = estadoDto.MesasTotalizadasPorcentaje;
registroDb.CantidadElectores = estadoDto.CantidadElectores;
registroDb.CantidadVotantes = estadoDto.CantidadVotantes;
registroDb.ParticipacionPorcentaje = estadoDto.ParticipacionPorcentaje;
}
}
}
2025-08-20 16:58:18 -03:00
2025-10-17 15:49:15 -03:00
// Bloque para el sondeo a nivel nacional
if (ambitoNacional != null && !stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Sondeando totales a nivel Nacional (Ambito ID: {ambitoId})...", ambitoNacional.Id);
foreach (var categoria in categoriasParaSondear)
2025-08-20 16:58:18 -03:00
{
2025-10-17 15:49:15 -03:00
if (stoppingToken.IsCancellationRequested) break;
var estadoNacionalDto = await _apiService.GetEstadoRecuentoGeneralAsync(authToken, "", categoria.Id);
if (estadoNacionalDto != null)
2025-08-20 16:58:18 -03:00
{
2025-10-17 15:49:15 -03:00
var registroNacionalDb = await dbContext.EstadosRecuentosGenerales.FindAsync(new object[] { ambitoNacional.Id, categoria.Id }, stoppingToken);
if (registroNacionalDb == null)
2025-08-20 16:58:18 -03:00
{
2025-10-17 15:49:15 -03:00
registroNacionalDb = new EstadoRecuentoGeneral { EleccionId = EleccionId, AmbitoGeograficoId = ambitoNacional.Id, CategoriaId = categoria.Id };
dbContext.EstadosRecuentosGenerales.Add(registroNacionalDb);
}
registroNacionalDb.FechaTotalizacion = DateTime.UtcNow;
registroNacionalDb.MesasEsperadas = estadoNacionalDto.MesasEsperadas;
registroNacionalDb.MesasTotalizadas = estadoNacionalDto.MesasTotalizadas;
registroNacionalDb.MesasTotalizadasPorcentaje = estadoNacionalDto.MesasTotalizadasPorcentaje;
registroNacionalDb.CantidadElectores = estadoNacionalDto.CantidadElectores;
registroNacionalDb.CantidadVotantes = estadoNacionalDto.CantidadVotantes;
registroNacionalDb.ParticipacionPorcentaje = estadoNacionalDto.ParticipacionPorcentaje;
_logger.LogInformation("Datos nacionales para categoría '{catNombre}' actualizados.", categoria.Nombre);
2025-08-20 16:58:18 -03:00
}
}
}
2025-10-17 15:49:15 -03:00
else if (ambitoNacional == null)
{
_logger.LogWarning("No se encontró el ámbito geográfico para el Nivel Nacional (NivelId 1 o 0). No se pueden capturar los totales del país.");
}
2025-08-20 16:58:18 -03:00
2025-10-17 15:49:15 -03:00
// Guardar todos los cambios
if (dbContext.ChangeTracker.HasChanges())
{
await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("Sondeo de Estado Recuento General completado. Se han guardado los cambios en la base de datos.");
}
else
{
_logger.LogInformation("Sondeo de Estado Recuento General completado. No se detectaron cambios.");
}
}
catch (OperationCanceledException)
{
_logger.LogInformation("Sondeo de Estado Recuento General cancelado.");
2025-08-20 16:58:18 -03:00
}
catch (Exception ex)
{
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Estado Recuento General.");
}
}
}