Files
Elecciones-2025/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs

190 lines
7.8 KiB
C#
Raw Normal View History

// src/Elecciones.Api/Controllers/ResultadosController.cs
using Elecciones.Core.DTOs.ApiResponses;
using Elecciones.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elecciones.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ResultadosController : ControllerBase
{
private readonly EleccionesDbContext _dbContext;
private readonly ILogger<ResultadosController> _logger;
public ResultadosController(EleccionesDbContext dbContext, ILogger<ResultadosController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
[HttpGet("municipio/{municipioId}")]
public async Task<IActionResult> GetResultadosPorMunicipio(string municipioId)
{
// 1. Buscamos el ámbito geográfico correspondiente al municipio
var ambito = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.MunicipioId == municipioId);
if (ambito == null)
{
return NotFound(new { message = $"No se encontró el municipio con ID {municipioId}" });
}
// 2. Buscamos el estado del recuento para ese ámbito
var estadoRecuento = await _dbContext.EstadosRecuentos
.AsNoTracking()
.FirstOrDefaultAsync(e => e.AmbitoGeograficoId == ambito.Id);
if (estadoRecuento == null)
{
return NotFound(new { message = $"No se han encontrado resultados para el municipio {ambito.Nombre}" });
}
// 3. Buscamos todos los votos para ese ámbito, incluyendo el nombre de la agrupación
var resultadosVotos = await _dbContext.ResultadosVotos
.AsNoTracking()
.Include(rv => rv.AgrupacionPolitica) // ¡Crucial para obtener el nombre del partido!
.Where(rv => rv.AmbitoGeograficoId == ambito.Id)
.ToListAsync();
// 4. Calculamos el total de votos positivos para el porcentaje
long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos);
// 5. Mapeamos todo a nuestro DTO de respuesta
var respuestaDto = new MunicipioResultadosDto
{
MunicipioNombre = ambito.Nombre,
UltimaActualizacion = estadoRecuento.FechaTotalizacion,
PorcentajeEscrutado = estadoRecuento.MesasTotalizadas * 100.0m / (estadoRecuento.MesasEsperadas > 0 ? estadoRecuento.MesasEsperadas : 1),
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
}).OrderByDescending(r => r.Votos).ToList(),
VotosAdicionales = new VotosAdicionalesDto
{
EnBlanco = estadoRecuento.VotosEnBlanco,
Nulos = estadoRecuento.VotosNulos,
Recurridos = estadoRecuento.VotosRecurridos
}
};
// Devolvemos el resultado
return Ok(respuestaDto);
}
[HttpGet("provincia/{distritoId}")]
public async Task<IActionResult> GetResultadosProvinciales(string distritoId)
{
// TODO: Esta lógica debe ser reemplazada para leer datos reales de la BD
// cuando el worker comience a ingestar los totales a nivel provincial.
// Por ahora, devolvemos datos simulados para permitir el desarrollo del frontend.
var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking()
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
if (ambito == null)
{
return NotFound(new { message = "No se encontró la provincia" });
}
// Simulación
var random = new Random();
var respuestaSimulada = new ResumenProvincialDto
{
ProvinciaNombre = ambito.Nombre,
UltimaActualizacion = DateTime.UtcNow,
PorcentajeEscrutado = 78.45m,
PorcentajeParticipacion = 65.12m,
Resultados =
[
new() { Nombre = "ALIANZA POR EL FUTURO", Votos = 2500000 + random.Next(1, 1000), Porcentaje = 45.12m },
new() { Nombre = "FRENTE DE AVANZADA", Votos = 2100000 + random.Next(1, 1000), Porcentaje = 38.78m },
new() { Nombre = "UNION POPULAR", Votos = 800000 + random.Next(1, 1000), Porcentaje = 14.10m },
],
VotosAdicionales = new VotosAdicionalesDto { EnBlanco = 150000, Nulos = 80000, Recurridos = 1200 }
};
return Ok(await Task.FromResult(respuestaSimulada));
}
2025-08-15 17:31:51 -03:00
[HttpGet("bancas/{seccionId}")]
public async Task<IActionResult> GetBancasPorSeccion(string seccionId)
{
// 1. Buscamos el ámbito de la sección electoral
var seccion = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.SeccionId == seccionId && a.NivelId == 4); // Nivel 4 = Sección Electoral
if (seccion == null)
{
return NotFound(new { message = $"No se encontró la sección electoral con ID {seccionId}" });
}
// 2. Buscamos todas las proyecciones para ese ámbito, incluyendo el nombre de la agrupación
var proyecciones = await _dbContext.ProyeccionesBancas
.AsNoTracking()
.Include(p => p.AgrupacionPolitica) // Incluimos el nombre del partido
.Where(p => p.AmbitoGeograficoId == seccion.Id)
.Select(p => new
{
// Creamos un objeto anónimo para la respuesta, más limpio que un DTO para este caso simple
AgrupacionNombre = p.AgrupacionPolitica.Nombre,
Bancas = p.NroBancas
})
.OrderByDescending(p => p.Bancas)
.ToListAsync();
if (!proyecciones.Any())
{
return NotFound(new { message = $"No se han encontrado proyecciones de bancas para la sección {seccion.Nombre}" });
}
// 3. Devolvemos un objeto que contiene el nombre de la sección y la lista de resultados
return Ok(new
{
SeccionNombre = seccion.Nombre,
Proyeccion = proyecciones
});
}
[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
{
AmbitoId = g.Key,
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
)
.Include(rv => rv.AmbitoGeografico) // Incluimos el ámbito para obtener el MunicipioId
.Where(rv => rv.AmbitoGeografico.MunicipioId != null)
.Select(rv => new
{
MunicipioId = rv.AmbitoGeografico.MunicipioId,
AgrupacionGanadoraId = rv.AgrupacionPoliticaId
})
.ToListAsync();
return Ok(resultadosGanadores);
}
}