Feat Front Widgets Refactizados y Ajustes Backend
This commit is contained in:
		| @@ -21,13 +21,13 @@ public class CatalogosController : ControllerBase | ||||
|     [HttpGet("municipios")] | ||||
|     public async Task<IActionResult> GetMunicipios() | ||||
|     { | ||||
|         // El NivelId 5 corresponde a "Municipio" según los datos que hemos visto. | ||||
|         // CORRECCIÓN: Los partidos/municipios corresponden al NivelId 30 (Sección) | ||||
|         var municipios = await _dbContext.AmbitosGeograficos | ||||
|             .AsNoTracking() | ||||
|             .Where(a => a.NivelId == 5 && a.MunicipioId != null) | ||||
|             .Where(a => a.NivelId == 30 && a.SeccionId != null) // <-- NivelId 30 | ||||
|             .Select(a => new MunicipioSimpleDto | ||||
|             { | ||||
|                 Id = a.MunicipioId!, | ||||
|                 Id = a.SeccionId!, // <-- Usamos SeccionId como el ID | ||||
|                 Nombre = a.Nombre | ||||
|             }) | ||||
|             .OrderBy(m => m.Nombre) | ||||
| @@ -35,4 +35,14 @@ public class CatalogosController : ControllerBase | ||||
|  | ||||
|         return Ok(municipios); | ||||
|     } | ||||
|  | ||||
|     [HttpGet("agrupaciones")] | ||||
|     public async Task<IActionResult> GetAgrupaciones() | ||||
|     { | ||||
|         var agrupaciones = await _dbContext.AgrupacionesPoliticas | ||||
|             .AsNoTracking() | ||||
|             .Select(a => new { a.Id, a.Nombre }) // Devuelve solo lo necesario | ||||
|             .ToListAsync(); | ||||
|         return Ok(agrupaciones); | ||||
|     } | ||||
| } | ||||
| @@ -21,17 +21,18 @@ public class ResultadosController : ControllerBase | ||||
|         _logger = logger; | ||||
|     } | ||||
|  | ||||
|     [HttpGet("municipio/{municipioId}")] | ||||
|     public async Task<IActionResult> GetResultadosPorMunicipio(string municipioId) | ||||
|     [HttpGet("partido/{seccionId}")] | ||||
|     public async Task<IActionResult> GetResultadosPorPartido(string seccionId) | ||||
|     { | ||||
|         // 1. Buscamos el ámbito geográfico correspondiente al municipio | ||||
|         // 1. Buscamos el ámbito geográfico correspondiente al PARTIDO (Nivel 30) | ||||
|         var ambito = await _dbContext.AmbitosGeograficos | ||||
|             .AsNoTracking() | ||||
|             .FirstOrDefaultAsync(a => a.MunicipioId == municipioId); | ||||
|             // CAMBIO CLAVE: Buscamos por SeccionId y NivelId para ser precisos | ||||
|             .FirstOrDefaultAsync(a => a.SeccionId == seccionId && a.NivelId == 30); | ||||
|  | ||||
|         if (ambito == null) | ||||
|         { | ||||
|             return NotFound(new { message = $"No se encontró el municipio con ID {municipioId}" }); | ||||
|             return NotFound(new { message = $"No se encontró el partido con ID {seccionId}" }); | ||||
|         } | ||||
|  | ||||
|         // 2. Buscamos el estado del recuento para ese ámbito | ||||
| @@ -41,25 +42,34 @@ public class ResultadosController : ControllerBase | ||||
|  | ||||
|         if (estadoRecuento == null) | ||||
|         { | ||||
|             return NotFound(new { message = $"No se han encontrado resultados para el municipio {ambito.Nombre}" }); | ||||
|             // Devolvemos una respuesta vacía pero válida para el frontend | ||||
|             return Ok(new MunicipioResultadosDto | ||||
|             { | ||||
|                 MunicipioNombre = ambito.Nombre, | ||||
|                 UltimaActualizacion = DateTime.UtcNow, | ||||
|                 PorcentajeEscrutado = 0, | ||||
|                 PorcentajeParticipacion = 0, | ||||
|                 Resultados = new List<AgrupacionResultadoDto>(), | ||||
|                 VotosAdicionales = new VotosAdicionalesDto() | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         // 3. Buscamos todos los votos para ese ámbito, incluyendo el nombre de la agrupación | ||||
|         // 3. Buscamos todos los votos para ese ámbito | ||||
|         var resultadosVotos = await _dbContext.ResultadosVotos | ||||
|             .AsNoTracking() | ||||
|             .Include(rv => rv.AgrupacionPolitica) // ¡Crucial para obtener el nombre del partido! | ||||
|             .Include(rv => rv.AgrupacionPolitica) | ||||
|             .Where(rv => rv.AmbitoGeograficoId == ambito.Id) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         // 4. Calculamos el total de votos positivos para el porcentaje | ||||
|         // 4. Calculamos el total de votos positivos | ||||
|         long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos); | ||||
|  | ||||
|         // 5. Mapeamos todo a nuestro DTO de respuesta | ||||
|         // 5. Mapeamos al DTO de respuesta | ||||
|         var respuestaDto = new MunicipioResultadosDto | ||||
|         { | ||||
|             MunicipioNombre = ambito.Nombre, | ||||
|             UltimaActualizacion = estadoRecuento.FechaTotalizacion, | ||||
|             PorcentajeEscrutado = estadoRecuento.MesasTotalizadas * 100.0m / (estadoRecuento.MesasEsperadas > 0 ? estadoRecuento.MesasEsperadas : 1), | ||||
|             PorcentajeEscrutado = estadoRecuento.MesasTotalizadasPorcentaje, | ||||
|             PorcentajeParticipacion = estadoRecuento.ParticipacionPorcentaje, | ||||
|             Resultados = resultadosVotos.Select(rv => new AgrupacionResultadoDto | ||||
|             { | ||||
| @@ -75,7 +85,6 @@ public class ResultadosController : ControllerBase | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         // Devolvemos el resultado | ||||
|         return Ok(respuestaDto); | ||||
|     } | ||||
|  | ||||
| @@ -157,8 +166,6 @@ public class ResultadosController : ControllerBase | ||||
|     [HttpGet("mapa")] | ||||
|     public async Task<IActionResult> GetResultadosParaMapa() | ||||
|     { | ||||
|         // Esta consulta es mucho más eficiente y se traduce bien a SQL. | ||||
|         // Paso 1: Para cada ámbito, encontrar la cantidad máxima de votos. | ||||
|         var maxVotosPorAmbito = _dbContext.ResultadosVotos | ||||
|             .GroupBy(rv => rv.AmbitoGeograficoId) | ||||
|             .Select(g => new | ||||
| @@ -167,24 +174,93 @@ public class ResultadosController : ControllerBase | ||||
|                 MaxVotos = g.Max(v => v.CantidadVotos) | ||||
|             }); | ||||
|  | ||||
|         // Paso 2: Unir los resultados originales con los máximos para encontrar el registro ganador. | ||||
|         // Esto nos da, para cada ámbito, el registro completo del partido que tuvo más votos. | ||||
|         var resultadosGanadores = await _dbContext.ResultadosVotos | ||||
|             .Join( | ||||
|                 maxVotosPorAmbito, | ||||
|                 voto => new { AmbitoId = voto.AmbitoGeograficoId, Votos = voto.CantidadVotos }, | ||||
|                 max => new { AmbitoId = max.AmbitoId, Votos = max.MaxVotos }, | ||||
|                 (voto, max) => voto // Nos quedamos con el objeto 'ResultadoVoto' completo | ||||
|                 (voto, max) => voto | ||||
|             ) | ||||
|             .Include(rv => rv.AmbitoGeografico) // Incluimos el ámbito para obtener el MunicipioId | ||||
|             .Where(rv => rv.AmbitoGeografico.MunicipioId != null) | ||||
|             .Include(rv => rv.AmbitoGeografico) | ||||
|             .Where(rv => rv.AmbitoGeografico.NivelId == 30) // Aseguramos que solo sean los ámbitos de nivel 30 | ||||
|             .Select(rv => new | ||||
|             { | ||||
|                 MunicipioId = rv.AmbitoGeografico.MunicipioId, | ||||
|                 // CORRECCIÓN CLAVE: Devolvemos los campos que el frontend necesita para funcionar. | ||||
|  | ||||
|                 // 1. El ID de la BD para hacer clic y pedir detalles. | ||||
|                 AmbitoId = rv.AmbitoGeografico.Id, | ||||
|  | ||||
|                 // 2. El NOMBRE del departamento/municipio para encontrar y colorear el polígono. | ||||
|                 DepartamentoNombre = rv.AmbitoGeografico.Nombre, | ||||
|  | ||||
|                 // 3. El ID del partido ganador. | ||||
|                 AgrupacionGanadoraId = rv.AgrupacionPoliticaId | ||||
|             }) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         return Ok(resultadosGanadores); | ||||
|     } | ||||
|  | ||||
|     [HttpGet("municipio/{ambitoId}")] // Cambiamos el nombre del parámetro de ruta | ||||
|     public async Task<IActionResult> GetResultadosPorMunicipio(int ambitoId) // Cambiamos el tipo de string a int | ||||
|     { | ||||
|         _logger.LogInformation("Buscando resultados para AmbitoGeograficoId: {AmbitoId}", ambitoId); | ||||
|  | ||||
|         // PASO 1: Buscar el Ámbito Geográfico directamente por su CLAVE PRIMARIA (AmbitoGeograficoId). | ||||
|         var ambito = await _dbContext.AmbitosGeograficos | ||||
|             .AsNoTracking() | ||||
|             .FirstOrDefaultAsync(a => a.Id == ambitoId && a.NivelId == 30); // Usamos a.Id == ambitoId | ||||
|  | ||||
|         if (ambito == null) | ||||
|         { | ||||
|             _logger.LogWarning("No se encontró el ámbito para el ID interno: {AmbitoId} o no es Nivel 30.", ambitoId); | ||||
|             return NotFound(new { message = $"No se encontró el municipio con ID interno {ambitoId}" }); | ||||
|         } | ||||
|         _logger.LogInformation("Ámbito encontrado: Id={AmbitoId}, Nombre={AmbitoNombre}", ambito.Id, ambito.Nombre); | ||||
|  | ||||
|         // PASO 2: Usar la CLAVE PRIMARIA (ambito.Id) para buscar el estado del recuento. | ||||
|         var estadoRecuento = await _dbContext.EstadosRecuentos | ||||
|             .AsNoTracking() | ||||
|             .FirstOrDefaultAsync(e => e.AmbitoGeograficoId == ambito.Id); | ||||
|  | ||||
|         if (estadoRecuento == null) | ||||
|         { | ||||
|             _logger.LogWarning("No se encontró EstadoRecuento para AmbitoGeograficoId: {AmbitoId}", ambito.Id); | ||||
|             return NotFound(new { message = $"No se han encontrado resultados de recuento para el municipio {ambito.Nombre}" }); | ||||
|         } | ||||
|  | ||||
|         // PASO 3: Usar la CLAVE PRIMARIA (ambito.Id) para buscar los votos. | ||||
|         var resultadosVotos = await _dbContext.ResultadosVotos | ||||
|             .AsNoTracking() | ||||
|             .Include(rv => rv.AgrupacionPolitica) // Incluimos el nombre del partido | ||||
|             .Where(rv => rv.AmbitoGeograficoId == ambito.Id) | ||||
|             .OrderByDescending(rv => rv.CantidadVotos) | ||||
|             .ToListAsync(); | ||||
|  | ||||
|         // PASO 4: Calcular el total de votos positivos para el porcentaje. | ||||
|         long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos); | ||||
|  | ||||
|         // PASO 5: Mapear todo al DTO de respuesta que el frontend espera. | ||||
|         var respuestaDto = new MunicipioResultadosDto | ||||
|         { | ||||
|             MunicipioNombre = ambito.Nombre, | ||||
|             UltimaActualizacion = estadoRecuento.FechaTotalizacion, | ||||
|             PorcentajeEscrutado = estadoRecuento.MesasTotalizadasPorcentaje, | ||||
|             PorcentajeParticipacion = estadoRecuento.ParticipacionPorcentaje, | ||||
|             Resultados = resultadosVotos.Select(rv => new AgrupacionResultadoDto | ||||
|             { | ||||
|                 Nombre = rv.AgrupacionPolitica.Nombre, | ||||
|                 Votos = rv.CantidadVotos, | ||||
|                 Porcentaje = totalVotosPositivos > 0 ? (rv.CantidadVotos * 100.0m / totalVotosPositivos) : 0 | ||||
|             }).ToList(), | ||||
|             VotosAdicionales = new VotosAdicionalesDto | ||||
|             { | ||||
|                 EnBlanco = estadoRecuento.VotosEnBlanco, | ||||
|                 Nulos = estadoRecuento.VotosNulos, | ||||
|                 Recurridos = estadoRecuento.VotosRecurridos | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         return Ok(respuestaDto); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user