From 63cc5ecec89b2110a8f6a3ea295aafcec7cea508 Mon Sep 17 00:00:00 2001 From: dmolinari Date: Mon, 27 Oct 2025 17:02:22 -0300 Subject: [PATCH] =?UTF-8?q?Feat=20Se=20A=C3=B1ade=20Sondeo=20Resultados=20?= =?UTF-8?q?Nivel=2050?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ElectoralApiService.cs | 9 +++-- .../Services/IElectoralApiService.cs | 2 +- .../Elecciones.Worker/CriticalDataWorker.cs | 38 ++++++++++++++++--- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/Services/ElectoralApiService.cs b/Elecciones-Web/src/Elecciones.Infrastructure/Services/ElectoralApiService.cs index 012147d..8ee7940 100644 --- a/Elecciones-Web/src/Elecciones.Infrastructure/Services/ElectoralApiService.cs +++ b/Elecciones-Web/src/Elecciones.Infrastructure/Services/ElectoralApiService.cs @@ -88,18 +88,15 @@ public class ElectoralApiService : IElectoralApiService return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync>() : null; } - public async Task GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId) + public async Task GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId, string? circuitoId = null) { using RateLimitLease lease = await _rateLimiter.AcquireAsync(1); if (!lease.IsAcquired) return null; var client = _httpClientFactory.CreateClient("ElectoralApiClient"); - // Construimos la URL base únicamente con los parámetros obligatorios. var requestUri = $"/api/resultados/getResultados?distritoId={distritoId}&categoriaId={categoriaId}"; - // Añadimos 'seccionId' a la URL SOLO si tiene un valor válido. - // Esto evita que se genere el problemático "&seccionId=" cuando es nulo o vacío. if (!string.IsNullOrEmpty(seccionId)) { requestUri += $"&seccionId={seccionId}"; @@ -108,6 +105,10 @@ public class ElectoralApiService : IElectoralApiService { requestUri += $"&municipioId={municipioId}"; } + if (!string.IsNullOrEmpty(circuitoId)) + { + requestUri += $"&circuitoId={circuitoId}"; + } var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Add("Authorization", $"Bearer {authToken}"); diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/Services/IElectoralApiService.cs b/Elecciones-Web/src/Elecciones.Infrastructure/Services/IElectoralApiService.cs index dba5404..fae2b01 100644 --- a/Elecciones-Web/src/Elecciones.Infrastructure/Services/IElectoralApiService.cs +++ b/Elecciones-Web/src/Elecciones.Infrastructure/Services/IElectoralApiService.cs @@ -11,7 +11,7 @@ public interface IElectoralApiService // Métodos para catálogos Task GetCatalogoAmbitosAsync(string authToken, int categoriaId); Task?> GetAgrupacionesAsync(string authToken, string? distritoId, int categoriaId); - Task GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId); + Task GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId, string? circuitoId = null); Task GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId); Task?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null); Task GetTelegramaFileAsync(string authToken, string mesaId); diff --git a/Elecciones-Web/src/Elecciones.Worker/CriticalDataWorker.cs b/Elecciones-Web/src/Elecciones.Worker/CriticalDataWorker.cs index cd2cf21..b766ba0 100644 --- a/Elecciones-Web/src/Elecciones.Worker/CriticalDataWorker.cs +++ b/Elecciones-Web/src/Elecciones.Worker/CriticalDataWorker.cs @@ -76,7 +76,7 @@ public class CriticalDataWorker : BackgroundService var ambitosProvinciales = await dbContext.AmbitosGeograficos.AsNoTracking().Where(a => a.NivelId == 10 && a.DistritoId != null).ToListAsync(stoppingToken); var ambitoNacional = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 1 || a.NivelId == 0, stoppingToken); - var categorias = await dbContext.CategoriasElectorales.AsNoTracking().ToListAsync(stoppingToken); + var categorias = await dbContext.CategoriasElectorales.AsNoTracking().Where(c => c.Id == 2 || c.Id == 3).ToListAsync(stoppingToken); // <-- Filtrado para Senadores y Diputados if (!ambitosProvinciales.Any() || !categorias.Any()) return; @@ -91,7 +91,6 @@ public class CriticalDataWorker : BackgroundService var resultadosDto = await _apiService.GetResultadosAsync(authToken, provincia.DistritoId!, null, null, categoria.Id); if (resultadosDto == null) continue; - // --- Guardar los datos agregados inmediatamente --- await GuardarDatosAgregadosAsync(dbContext, provincia.Id, categoria.Id, resultadosDto, stoppingToken); if (DateTime.TryParse(resultadosDto.FechaTotalizacion, out var fechaApi)) @@ -182,19 +181,46 @@ public class CriticalDataWorker : BackgroundService using var scope = _serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); + // 1. Obtenemos todos los municipios Y circuitos de la provincia en consultas eficientes. var municipiosDeLaProvincia = await dbContext.AmbitosGeograficos .AsNoTracking() .Where(a => a.NivelId == 30 && a.DistritoId == distritoId && a.SeccionId != null) .ToListAsync(); - _logger.LogInformation("Iniciando descarga detallada para {Count} municipios del distrito {DistritoId}...", municipiosDeLaProvincia.Count, distritoId); + var circuitosDeLaProvincia = await dbContext.AmbitosGeograficos + .AsNoTracking() + .Where(a => a.NivelId == 50 && a.DistritoId == distritoId && a.SeccionId != null && a.CircuitoId != null) + .ToListAsync(); + + // 2. Agrupamos los circuitos por el SeccionId (ID del municipio) para una búsqueda rápida. + var circuitosPorMunicipio = circuitosDeLaProvincia + .GroupBy(c => c.SeccionId) + .ToDictionary(g => g.Key!, g => g.ToList()); + + _logger.LogInformation("Iniciando descarga detallada para {Count} municipios y {CircuitCount} circuitos del distrito {DistritoId}...", + municipiosDeLaProvincia.Count, circuitosDeLaProvincia.Count, distritoId); foreach (var municipio in municipiosDeLaProvincia) { - var resultados = await _apiService.GetResultadosAsync(authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoriaId); - if (resultados != null) + // 3. Descargamos y guardamos los resultados a nivel de municipio (como antes). + var resultadosMunicipio = await _apiService.GetResultadosAsync(authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoriaId); + if (resultadosMunicipio != null) { - await GuardarResultadosDeAmbitoAsync(dbContext, municipio.Id, categoriaId, resultados, CancellationToken.None); + await GuardarResultadosDeAmbitoAsync(dbContext, municipio.Id, categoriaId, resultadosMunicipio, CancellationToken.None); + } + + // 4. Buscamos y recorremos los circuitos de este municipio. + if (circuitosPorMunicipio.TryGetValue(municipio.SeccionId!, out var circuitos)) + { + foreach (var circuito in circuitos) + { + // 5. Llamamos a la API con el circuitoId y guardamos los resultados. + var resultadosCircuito = await _apiService.GetResultadosAsync(authToken, circuito.DistritoId!, circuito.SeccionId!, null, categoriaId, circuito.CircuitoId!); + if (resultadosCircuito != null) + { + await GuardarResultadosDeAmbitoAsync(dbContext, circuito.Id, categoriaId, resultadosCircuito, CancellationToken.None); + } + } } }