Feat Widgets
- Widget de Home - Widget Cards por Provincias - Widget Mapa por Categorias
This commit is contained in:
@@ -142,7 +142,9 @@ public class ResultadosController : ControllerBase
|
||||
.ToListAsync();
|
||||
|
||||
// Obtenemos TODOS los logos relevantes en una sola consulta
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking()
|
||||
.Where(l => l.EleccionId == eleccionId || l.EleccionId == 0) // Trae los de la elección actual y los de fallback
|
||||
.ToListAsync();
|
||||
|
||||
// --- LÓGICA DE AGRUPACIÓN Y CÁLCULO CORREGIDA ---
|
||||
var resultadosAgrupados = resultadosPorMunicipio
|
||||
@@ -1040,13 +1042,25 @@ public class ResultadosController : ControllerBase
|
||||
|
||||
private async Task<IActionResult> GetPanelMunicipal(int eleccionId, int ambitoId, int categoriaId)
|
||||
{
|
||||
// 1. Validar y obtener la entidad del municipio
|
||||
// 1. Obtener la entidad del municipio y, a partir de ella, la de su provincia.
|
||||
var municipio = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.Id == ambitoId && a.NivelId == 30);
|
||||
|
||||
if (municipio == null) return NotFound($"No se encontró el municipio con ID {ambitoId}.");
|
||||
|
||||
// 2. Obtener los votos solo para ESE municipio
|
||||
var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.DistritoId == municipio.DistritoId && a.NivelId == 10);
|
||||
|
||||
// Si por alguna razón no encontramos la provincia, no podemos continuar.
|
||||
if (provincia == null) return NotFound($"No se pudo determinar la provincia para el municipio con ID {ambitoId}.");
|
||||
|
||||
// 2. Cargar todos los overrides de candidatos y logos relevantes (igual que en la vista provincial).
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking()
|
||||
.Where(c => c.EleccionId == eleccionId || c.EleccionId == 0).ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking()
|
||||
.Where(l => l.EleccionId == eleccionId || l.EleccionId == 0).ToListAsync();
|
||||
|
||||
// 3. Obtener los votos solo para ESE municipio (esto no cambia).
|
||||
var resultadosCrudos = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||
.Include(r => r.AgrupacionPolitica)
|
||||
.Where(r => r.EleccionId == eleccionId &&
|
||||
@@ -1054,29 +1068,38 @@ public class ResultadosController : ControllerBase
|
||||
r.AmbitoGeograficoId == ambitoId)
|
||||
.ToListAsync();
|
||||
|
||||
// El resto de la lógica es muy similar, pero ahora usamos los helpers con el ID de la provincia.
|
||||
if (!resultadosCrudos.Any())
|
||||
{
|
||||
// Devolver un DTO vacío pero válido si no hay resultados
|
||||
return Ok(new PanelElectoralDto
|
||||
{
|
||||
AmbitoNombre = municipio.Nombre,
|
||||
MapaData = new List<ResultadoMapaDto>(), // El mapa estará vacío en la vista de un solo municipio
|
||||
MapaData = new List<ResultadoMapaDto>(),
|
||||
ResultadosPanel = new List<AgrupacionResultadoDto>(),
|
||||
EstadoRecuento = new EstadoRecuentoDto()
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Calcular los resultados para el panel lateral (son los mismos datos crudos)
|
||||
var totalVotosMunicipio = (decimal)resultadosCrudos.Sum(r => r.CantidadVotos);
|
||||
var resultadosPanel = resultadosCrudos
|
||||
.Select(g => new AgrupacionResultadoDto
|
||||
.Select(g =>
|
||||
{
|
||||
Id = g.AgrupacionPolitica.Id,
|
||||
Nombre = g.AgrupacionPolitica.Nombre,
|
||||
NombreCorto = g.AgrupacionPolitica.NombreCorto,
|
||||
Color = g.AgrupacionPolitica.Color,
|
||||
Votos = g.CantidadVotos,
|
||||
Porcentaje = totalVotosMunicipio > 0 ? (g.CantidadVotos / totalVotosMunicipio) * 100 : 0
|
||||
// 4. ¡LA CLAVE! Usamos el ID de la PROVINCIA para buscar el override.
|
||||
var candidatoMatch = FindBestCandidatoMatch(todosLosOverrides, g.AgrupacionPolitica.Id, categoriaId, provincia.Id, eleccionId);
|
||||
var logoMatch = FindBestLogoMatch(todosLosLogos, g.AgrupacionPolitica.Id, categoriaId, provincia.Id, eleccionId);
|
||||
|
||||
return new AgrupacionResultadoDto
|
||||
{
|
||||
Id = g.AgrupacionPolitica.Id,
|
||||
Nombre = g.AgrupacionPolitica.Nombre,
|
||||
NombreCorto = g.AgrupacionPolitica.NombreCorto,
|
||||
Color = g.AgrupacionPolitica.Color,
|
||||
Votos = g.CantidadVotos,
|
||||
Porcentaje = totalVotosMunicipio > 0 ? (g.CantidadVotos / totalVotosMunicipio) * 100 : 0,
|
||||
// Asignamos los datos del override encontrado
|
||||
NombreCandidato = candidatoMatch?.NombreCandidato,
|
||||
LogoUrl = logoMatch?.LogoUrl
|
||||
};
|
||||
})
|
||||
.OrderByDescending(r => r.Votos)
|
||||
.ToList();
|
||||
@@ -1088,7 +1111,7 @@ public class ResultadosController : ControllerBase
|
||||
var respuesta = new PanelElectoralDto
|
||||
{
|
||||
AmbitoNombre = municipio.Nombre,
|
||||
MapaData = new List<ResultadoMapaDto>(), // El mapa no muestra sub-geografías aquí
|
||||
MapaData = new List<ResultadoMapaDto>(),
|
||||
ResultadosPanel = resultadosPanel,
|
||||
EstadoRecuento = new EstadoRecuentoDto
|
||||
{
|
||||
@@ -1107,38 +1130,41 @@ public class ResultadosController : ControllerBase
|
||||
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
|
||||
if (provincia == null) return NotFound($"No se encontró la provincia con DistritoId {distritoId}.");
|
||||
|
||||
// --- INICIO DE LA OPTIMIZACIÓN ---
|
||||
// 1. Agrupar y sumar directamente en la base de datos. EF lo traducirá a un SQL eficiente.
|
||||
// --- INICIO DE LA MODIFICACIÓN ---
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking().Where(c => c.EleccionId == eleccionId || c.EleccionId == 0).ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().Where(l => l.EleccionId == eleccionId || l.EleccionId == 0).ToListAsync();
|
||||
// --- FIN DE LA MODIFICACIÓN ---
|
||||
|
||||
// ... (la lógica de agregación de votos no cambia)
|
||||
var resultadosAgregados = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||
.Where(r => r.EleccionId == eleccionId &&
|
||||
r.CategoriaId == categoriaId &&
|
||||
r.AmbitoGeografico.DistritoId == distritoId &&
|
||||
r.AmbitoGeografico.NivelId == 30)
|
||||
.GroupBy(r => r.AgrupacionPolitica) // Agrupar por la entidad
|
||||
.Select(g => new
|
||||
{
|
||||
Agrupacion = g.Key,
|
||||
TotalVotos = g.Sum(r => r.CantidadVotos)
|
||||
})
|
||||
.Where(r => r.EleccionId == eleccionId && r.CategoriaId == categoriaId && r.AmbitoGeografico.DistritoId == distritoId && r.AmbitoGeografico.NivelId == 30)
|
||||
.GroupBy(r => r.AgrupacionPolitica)
|
||||
.Select(g => new { Agrupacion = g.Key, TotalVotos = g.Sum(r => r.CantidadVotos) })
|
||||
.ToListAsync();
|
||||
|
||||
// 2. Calcular el total de votos en memoria (sobre una lista ya pequeña)
|
||||
var totalVotosProvincia = (decimal)resultadosAgregados.Sum(r => r.TotalVotos);
|
||||
|
||||
// 3. Mapear a DTO (muy rápido)
|
||||
var resultadosPanel = resultadosAgregados
|
||||
.Select(g => new AgrupacionResultadoDto
|
||||
.Select(g =>
|
||||
{
|
||||
Id = g.Agrupacion.Id,
|
||||
Nombre = g.Agrupacion.Nombre,
|
||||
NombreCorto = g.Agrupacion.NombreCorto,
|
||||
Color = g.Agrupacion.Color,
|
||||
Votos = g.TotalVotos,
|
||||
Porcentaje = totalVotosProvincia > 0 ? (g.TotalVotos / totalVotosProvincia) * 100 : 0
|
||||
// Aplicamos la misma lógica de búsqueda de overrides
|
||||
var candidatoMatch = FindBestCandidatoMatch(todosLosOverrides, g.Agrupacion.Id, categoriaId, provincia.Id, eleccionId);
|
||||
var logoMatch = FindBestLogoMatch(todosLosLogos, g.Agrupacion.Id, categoriaId, provincia.Id, eleccionId);
|
||||
|
||||
return new AgrupacionResultadoDto
|
||||
{
|
||||
Id = g.Agrupacion.Id,
|
||||
Nombre = g.Agrupacion.Nombre,
|
||||
NombreCorto = g.Agrupacion.NombreCorto,
|
||||
Color = g.Agrupacion.Color,
|
||||
Votos = g.TotalVotos,
|
||||
Porcentaje = totalVotosProvincia > 0 ? (g.TotalVotos / totalVotosProvincia) * 100 : 0,
|
||||
NombreCandidato = candidatoMatch?.NombreCandidato, // <-- DATO AÑADIDO
|
||||
LogoUrl = logoMatch?.LogoUrl // <-- DATO AÑADIDO
|
||||
};
|
||||
})
|
||||
.OrderByDescending(r => r.Votos)
|
||||
.ToList();
|
||||
// --- FIN DE LA OPTIMIZACIÓN ---
|
||||
|
||||
var estadoRecuento = await _dbContext.EstadosRecuentosGenerales.AsNoTracking()
|
||||
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.AmbitoGeograficoId == provincia.Id && e.CategoriaId == categoriaId);
|
||||
@@ -1159,43 +1185,46 @@ public class ResultadosController : ControllerBase
|
||||
|
||||
private async Task<IActionResult> GetPanelNacional(int eleccionId, int categoriaId)
|
||||
{
|
||||
// --- INICIO DE LA OPTIMIZACIÓN ---
|
||||
// 1. Agrupar y sumar directamente en la base de datos a nivel nacional.
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking().Where(c => c.EleccionId == eleccionId || c.EleccionId == 0).ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().Where(l => l.EleccionId == eleccionId || l.EleccionId == 0).ToListAsync();
|
||||
|
||||
|
||||
var resultadosAgregados = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||
.Where(r => r.EleccionId == eleccionId && r.CategoriaId == categoriaId)
|
||||
.GroupBy(r => r.AgrupacionPolitica)
|
||||
.Select(g => new
|
||||
{
|
||||
Agrupacion = g.Key,
|
||||
TotalVotos = g.Sum(r => r.CantidadVotos)
|
||||
})
|
||||
.Select(g => new { Agrupacion = g.Key, TotalVotos = g.Sum(r => r.CantidadVotos) })
|
||||
.ToListAsync();
|
||||
|
||||
// 2. Calcular el total de votos en memoria
|
||||
var totalVotosNacional = (decimal)resultadosAgregados.Sum(r => r.TotalVotos);
|
||||
|
||||
// 3. Mapear a DTO
|
||||
var resultadosPanel = resultadosAgregados
|
||||
.Select(g => new AgrupacionResultadoDto
|
||||
.Select(g =>
|
||||
{
|
||||
var candidatoMatch = FindBestCandidatoMatch(todosLosOverrides, g.Agrupacion.Id, categoriaId, null, eleccionId);
|
||||
var logoMatch = FindBestLogoMatch(todosLosLogos, g.Agrupacion.Id, categoriaId, null, eleccionId);
|
||||
|
||||
return new AgrupacionResultadoDto
|
||||
{
|
||||
Id = g.Agrupacion.Id,
|
||||
Nombre = g.Agrupacion.Nombre,
|
||||
NombreCorto = g.Agrupacion.NombreCorto,
|
||||
Color = g.Agrupacion.Color,
|
||||
Votos = g.TotalVotos,
|
||||
Porcentaje = totalVotosNacional > 0 ? (g.TotalVotos / totalVotosNacional) * 100 : 0
|
||||
})
|
||||
.OrderByDescending(r => r.Votos)
|
||||
.ToList();
|
||||
// --- FIN DE LA OPTIMIZACIÓN ---
|
||||
Porcentaje = totalVotosNacional > 0 ? (g.TotalVotos / totalVotosNacional) * 100 : 0,
|
||||
NombreCandidato = null,
|
||||
LogoUrl = logoMatch?.LogoUrl
|
||||
};
|
||||
})
|
||||
.OrderByDescending(r => r.Votos)
|
||||
.ToList();
|
||||
|
||||
var estadoRecuento = await _dbContext.EstadosRecuentosGenerales.AsNoTracking()
|
||||
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.CategoriaId == categoriaId);
|
||||
.FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.CategoriaId == categoriaId && e.AmbitoGeografico.NivelId == 0);
|
||||
|
||||
var respuesta = new PanelElectoralDto
|
||||
{
|
||||
AmbitoNombre = "Argentina",
|
||||
MapaData = new List<ResultadoMapaDto>(), // Se carga por separado
|
||||
MapaData = new List<ResultadoMapaDto>(),
|
||||
ResultadosPanel = resultadosPanel,
|
||||
EstadoRecuento = new EstadoRecuentoDto
|
||||
{
|
||||
@@ -1208,9 +1237,9 @@ public class ResultadosController : ControllerBase
|
||||
|
||||
[HttpGet("mapa-resultados")]
|
||||
public async Task<IActionResult> GetResultadosMapaPorMunicipio(
|
||||
[FromRoute] int eleccionId,
|
||||
[FromQuery] int categoriaId,
|
||||
[FromQuery] string? distritoId = null)
|
||||
[FromRoute] int eleccionId,
|
||||
[FromQuery] int categoriaId,
|
||||
[FromQuery] string? distritoId = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(distritoId))
|
||||
{
|
||||
@@ -1413,77 +1442,196 @@ public class ResultadosController : ControllerBase
|
||||
return Ok(new { Diputados = diputados, Senadores = senadores });
|
||||
}
|
||||
|
||||
[HttpGet("resumen-por-provincia")]
|
||||
public async Task<IActionResult> GetResumenPorProvincia([FromRoute] int eleccionId)
|
||||
// --- INICIO DE FUNCIONES DE AYUDA ---
|
||||
private CandidatoOverride? FindBestCandidatoMatch(
|
||||
List<CandidatoOverride> overrides, string agrupacionId, int categoriaId, int? ambitoId, int eleccionId)
|
||||
{
|
||||
const int categoriaDiputadosNacionales = 2;
|
||||
return overrides.FirstOrDefault(c => c.EleccionId == eleccionId && c.AgrupacionPoliticaId == agrupacionId && c.CategoriaId == categoriaId && c.AmbitoGeograficoId == ambitoId)
|
||||
?? overrides.FirstOrDefault(c => c.EleccionId == eleccionId && c.AgrupacionPoliticaId == agrupacionId && c.CategoriaId == categoriaId && c.AmbitoGeograficoId == null)
|
||||
?? overrides.FirstOrDefault(c => c.EleccionId == 0 && c.AgrupacionPoliticaId == agrupacionId && c.CategoriaId == categoriaId && c.AmbitoGeograficoId == ambitoId)
|
||||
?? overrides.FirstOrDefault(c => c.EleccionId == 0 && c.AgrupacionPoliticaId == agrupacionId && c.CategoriaId == categoriaId && c.AmbitoGeograficoId == null);
|
||||
}
|
||||
private LogoAgrupacionCategoria? FindBestLogoMatch(
|
||||
List<LogoAgrupacionCategoria> logos, string agrupacionId, int categoriaId, int? ambitoId, int eleccionId)
|
||||
{
|
||||
// Prioridad 1: Coincidencia exacta (Elección, Categoría, Ámbito)
|
||||
return logos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == agrupacionId && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == ambitoId)
|
||||
// Prioridad 2: Coincidencia por Elección y Categoría (Ámbito genérico)
|
||||
?? logos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == agrupacionId && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)
|
||||
// Prioridad 3: Coincidencia de Fallback por Ámbito (Elección genérica)
|
||||
?? logos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == agrupacionId && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == ambitoId)
|
||||
// Prioridad 4: Coincidencia de Fallback por Categoría (Elección y Ámbito genéricos)
|
||||
?? logos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == agrupacionId && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)
|
||||
|
||||
// --- INICIO DE LA CORRECCIÓN ---
|
||||
// Prioridad 5: LOGO GLOBAL. Coincidencia solo por Partido (Elección y Categoría genéricas)
|
||||
// Se busca EleccionId = 0 y CategoriaId = 0 (en lugar de null) para que coincida con la lógica de los otros widgets.
|
||||
?? logos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == agrupacionId && l.CategoriaId == 0 && l.AmbitoGeograficoId == null);
|
||||
// --- FIN DE LA CORRECCIÓN ---
|
||||
}
|
||||
|
||||
var todasLasProyecciones = await _dbContext.ProyeccionesBancas.AsNoTracking()
|
||||
.Where(p => p.EleccionId == eleccionId && p.CategoriaId == categoriaDiputadosNacionales)
|
||||
.ToDictionaryAsync(p => p.AmbitoGeograficoId + "_" + p.AgrupacionPoliticaId);
|
||||
[HttpGet("resumen-por-provincia")]
|
||||
public async Task<IActionResult> GetResumenPorProvincia(
|
||||
[FromRoute] int eleccionId,
|
||||
[FromQuery] string? focoDistritoId = null,
|
||||
[FromQuery] int? focoCategoriaId = null,
|
||||
[FromQuery] int cantidadResultados = 2)
|
||||
{
|
||||
if (cantidadResultados < 1) cantidadResultados = 1;
|
||||
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking()
|
||||
.Where(c => c.EleccionId == eleccionId && c.CategoriaId == categoriaDiputadosNacionales)
|
||||
.ToListAsync();
|
||||
const int catDiputadosNac = 2;
|
||||
const int catSenadoresNac = 1;
|
||||
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking()
|
||||
.Where(l => l.EleccionId == eleccionId && l.CategoriaId == categoriaDiputadosNacionales)
|
||||
.ToListAsync();
|
||||
var provinciasQueRenuevanSenadores = new HashSet<string> { "01", "06", "08", "15", "16", "17", "22", "23" };
|
||||
|
||||
var datosBrutos = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||
.Where(a => a.NivelId == 10)
|
||||
.Select(provincia => new
|
||||
{
|
||||
ProvinciaAmbitoId = provincia.Id,
|
||||
ProvinciaDistritoId = provincia.DistritoId!,
|
||||
ProvinciaNombre = provincia.Nombre,
|
||||
EstadoRecuento = _dbContext.EstadosRecuentosGenerales
|
||||
.Where(e => e.EleccionId == eleccionId && e.CategoriaId == categoriaDiputadosNacionales && e.AmbitoGeograficoId == provincia.Id)
|
||||
.Select(e => new EstadoRecuentoDto { /* ... */ })
|
||||
.FirstOrDefault(),
|
||||
ResultadosBrutos = _dbContext.ResultadosVotos
|
||||
.Where(r => r.EleccionId == eleccionId && r.CategoriaId == categoriaDiputadosNacionales && r.AmbitoGeografico.DistritoId == provincia.DistritoId)
|
||||
.GroupBy(r => r.AgrupacionPolitica)
|
||||
.Select(g => new { Agrupacion = g.Key, Votos = g.Sum(r => r.CantidadVotos) })
|
||||
.OrderByDescending(x => x.Votos)
|
||||
.Take(2)
|
||||
.ToList()
|
||||
})
|
||||
.OrderBy(p => p.ProvinciaNombre)
|
||||
.ToListAsync();
|
||||
// --- CORRECCIÓN FINAL: Simplificar la carga de datos de soporte ---
|
||||
var todasLasProyecciones = await _dbContext.ProyeccionesBancas.AsNoTracking().Where(p => p.EleccionId == eleccionId && (p.CategoriaId == catDiputadosNac || p.CategoriaId == catSenadoresNac)).ToDictionaryAsync(p => p.AmbitoGeograficoId + "_" + p.AgrupacionPoliticaId + "_" + p.CategoriaId);
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking().Where(c => c.EleccionId == eleccionId || c.EleccionId == 0).ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().Where(l => l.EleccionId == eleccionId || l.EleccionId == 0).ToListAsync();
|
||||
var todosLosEstados = await _dbContext.EstadosRecuentosGenerales.AsNoTracking().Include(e => e.CategoriaElectoral).Where(e => e.EleccionId == eleccionId && (e.CategoriaId == catDiputadosNac || e.CategoriaId == catSenadoresNac)).ToDictionaryAsync(e => e.AmbitoGeograficoId + "_" + e.CategoriaId);
|
||||
var mapaMunicipioADistrito = await _dbContext.AmbitosGeograficos.AsNoTracking().Where(a => a.NivelId == 30 && a.DistritoId != null).ToDictionaryAsync(a => a.Id, a => a.DistritoId!);
|
||||
var todosLosVotos = await _dbContext.ResultadosVotos.AsNoTracking().Where(r => r.EleccionId == eleccionId && (r.CategoriaId == catDiputadosNac || r.CategoriaId == catSenadoresNac)).Select(r => new { r.AmbitoGeograficoId, r.CategoriaId, r.AgrupacionPoliticaId, r.CantidadVotos }).ToListAsync();
|
||||
var votosAgregados = todosLosVotos.Where(v => mapaMunicipioADistrito.ContainsKey(v.AmbitoGeograficoId)).GroupBy(v => new { DistritoId = mapaMunicipioADistrito[v.AmbitoGeograficoId], v.CategoriaId, v.AgrupacionPoliticaId }).Select(g => new { g.Key.DistritoId, g.Key.CategoriaId, g.Key.AgrupacionPoliticaId, Votos = g.Sum(v => v.CantidadVotos) }).ToList();
|
||||
|
||||
var resultadosFinales = datosBrutos.Select(provinciaData =>
|
||||
var agrupaciones = await _dbContext.AgrupacionesPoliticas.AsNoTracking().ToDictionaryAsync(a => a.Id);
|
||||
var resultadosFinales = new List<ResumenProvinciaDto>();
|
||||
var provinciasQuery = _dbContext.AmbitosGeograficos.AsNoTracking().Where(a => a.NivelId == 10);
|
||||
if (!string.IsNullOrEmpty(focoDistritoId)) { provinciasQuery = provinciasQuery.Where(p => p.DistritoId == focoDistritoId); }
|
||||
var provincias = await provinciasQuery.ToListAsync();
|
||||
if (!provincias.Any()) { return Ok(resultadosFinales); }
|
||||
|
||||
foreach (var provincia in provincias.OrderBy(p => p.Nombre))
|
||||
{
|
||||
var totalVotosProvincia = (decimal)provinciaData.ResultadosBrutos.Sum(r => r.Votos);
|
||||
return new ResumenProvinciaDto
|
||||
var categoriasDeLaProvincia = new List<int>();
|
||||
if (focoCategoriaId.HasValue) { if ((focoCategoriaId.Value == catDiputadosNac) || (focoCategoriaId.Value == catSenadoresNac && provinciasQueRenuevanSenadores.Contains(provincia.DistritoId!))) categoriasDeLaProvincia.Add(focoCategoriaId.Value); }
|
||||
else { categoriasDeLaProvincia.Add(catDiputadosNac); if (provinciasQueRenuevanSenadores.Contains(provincia.DistritoId!)) categoriasDeLaProvincia.Add(catSenadoresNac); }
|
||||
|
||||
var dtoProvincia = new ResumenProvinciaDto { ProvinciaId = provincia.DistritoId!, ProvinciaNombre = provincia.Nombre, Categorias = new List<CategoriaResumenDto>() };
|
||||
|
||||
foreach (var categoriaId in categoriasDeLaProvincia)
|
||||
{
|
||||
ProvinciaId = provinciaData.ProvinciaDistritoId,
|
||||
ProvinciaNombre = provinciaData.ProvinciaNombre,
|
||||
EstadoRecuento = provinciaData.EstadoRecuento,
|
||||
Resultados = provinciaData.ResultadosBrutos.Select(r =>
|
||||
var resultadosCategoriaCompleta = votosAgregados.Where(r => r.DistritoId == provincia.DistritoId && r.CategoriaId == categoriaId);
|
||||
var totalVotosCategoria = (decimal)resultadosCategoriaCompleta.Sum(r => r.Votos);
|
||||
var votosAgrupados = resultadosCategoriaCompleta.OrderByDescending(x => x.Votos).Take(cantidadResultados).Select(r => new { Agrupacion = agrupaciones[r.AgrupacionPoliticaId], r.Votos }).ToList();
|
||||
todosLosEstados.TryGetValue(provincia.Id + "_" + categoriaId, out var estado);
|
||||
|
||||
dtoProvincia.Categorias.Add(new CategoriaResumenDto
|
||||
{
|
||||
var provinciaAmbitoId = provinciaData.ProvinciaAmbitoId;
|
||||
CategoriaId = categoriaId,
|
||||
CategoriaNombre = estado?.CategoriaElectoral.Nombre ?? (categoriaId == catDiputadosNac ? "DIPUTADOS NACIONALES" : "SENADORES NACIONALES"),
|
||||
EstadoRecuento = estado != null ? new EstadoRecuentoDto
|
||||
{
|
||||
ParticipacionPorcentaje = estado.ParticipacionPorcentaje,
|
||||
MesasTotalizadasPorcentaje = estado.MesasTotalizadasPorcentaje,
|
||||
CantidadVotantes = estado.CantidadVotantes
|
||||
} : null,
|
||||
Resultados = votosAgrupados.Select(r =>
|
||||
{
|
||||
var provinciaAmbitoId = provincia.Id;
|
||||
var candidatoMatch = FindBestCandidatoMatch(todosLosOverrides, r.Agrupacion.Id, categoriaId, provinciaAmbitoId, eleccionId);
|
||||
|
||||
string? logoFinal =
|
||||
// Prioridad 1: Override Específico (EleccionId, CategoriaId, AmbitoId)
|
||||
todosLosLogos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == provinciaAmbitoId)?.LogoUrl
|
||||
// Prioridad 2: Override por Elección y Categoría (general a ámbitos)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)?.LogoUrl
|
||||
// Prioridad 3: Fallback Global para la CATEGORÍA (EleccionId = 0, CategoriaId)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)?.LogoUrl
|
||||
// Prioridad 4: Fallback "Super Global" (EleccionId = 0, CategoriaId = 0)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == 0 && l.AmbitoGeograficoId == null)?.LogoUrl;
|
||||
|
||||
return new ResultadoCandidatoDto
|
||||
{
|
||||
AgrupacionId = r.Agrupacion.Id,
|
||||
NombreAgrupacion = r.Agrupacion.NombreCorto ?? r.Agrupacion.Nombre,
|
||||
NombreAgrupacion = r.Agrupacion.Nombre,
|
||||
NombreCortoAgrupacion = r.Agrupacion.NombreCorto,
|
||||
NombreCandidato = candidatoMatch?.NombreCandidato,
|
||||
Color = r.Agrupacion.Color,
|
||||
Votos = r.Votos,
|
||||
NombreCandidato = (todosLosOverrides.FirstOrDefault(c => c.AgrupacionPoliticaId == r.Agrupacion.Id && c.AmbitoGeograficoId == provinciaAmbitoId)
|
||||
?? todosLosOverrides.FirstOrDefault(c => c.AgrupacionPoliticaId == r.Agrupacion.Id && c.AmbitoGeograficoId == null))
|
||||
?.NombreCandidato,
|
||||
FotoUrl = (todosLosLogos.FirstOrDefault(l => l.AgrupacionPoliticaId == r.Agrupacion.Id && l.AmbitoGeograficoId == provinciaAmbitoId)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.AgrupacionPoliticaId == r.Agrupacion.Id && l.AmbitoGeograficoId == null))
|
||||
?.LogoUrl,
|
||||
BancasObtenidas = todasLasProyecciones.ContainsKey(provinciaAmbitoId + "_" + r.Agrupacion.Id)
|
||||
? todasLasProyecciones[provinciaAmbitoId + "_" + r.Agrupacion.Id].NroBancas
|
||||
: 0,
|
||||
Porcentaje = totalVotosProvincia > 0 ? (r.Votos / totalVotosProvincia) * 100 : 0
|
||||
FotoUrl = logoFinal,
|
||||
BancasObtenidas = todasLasProyecciones.TryGetValue(provinciaAmbitoId + "_" + r.Agrupacion.Id + "_" + categoriaId, out var proyeccion) ? proyeccion.NroBancas : 0,
|
||||
Porcentaje = totalVotosCategoria > 0 ? (r.Votos / totalVotosCategoria) * 100 : 0
|
||||
};
|
||||
}).ToList()
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
});
|
||||
}
|
||||
if (dtoProvincia.Categorias.Any()) { resultadosFinales.Add(dtoProvincia); }
|
||||
}
|
||||
return Ok(resultadosFinales);
|
||||
}
|
||||
|
||||
[HttpGet("~/api/elecciones/home-resumen")]
|
||||
public async Task<IActionResult> GetHomeResumen(
|
||||
[FromQuery] int eleccionId,
|
||||
[FromQuery] string distritoId,
|
||||
[FromQuery] int categoriaId)
|
||||
{
|
||||
var provincia = await _dbContext.AmbitosGeograficos.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
|
||||
|
||||
if (provincia == null) return NotFound($"No se encontró la provincia con DistritoId {distritoId}.");
|
||||
|
||||
var votosAgregados = await _dbContext.ResultadosVotos.AsNoTracking()
|
||||
.Where(r => r.EleccionId == eleccionId &&
|
||||
r.CategoriaId == categoriaId &&
|
||||
r.AmbitoGeografico.DistritoId == distritoId)
|
||||
.GroupBy(r => r.AgrupacionPolitica)
|
||||
.Select(g => new { Agrupacion = g.Key, Votos = g.Sum(r => r.CantidadVotos) })
|
||||
.OrderByDescending(x => x.Votos)
|
||||
.ToListAsync();
|
||||
|
||||
var todosLosOverrides = await _dbContext.CandidatosOverrides.AsNoTracking().Where(c => c.EleccionId == eleccionId || c.EleccionId == 0).ToListAsync();
|
||||
var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias.AsNoTracking().Where(l => l.EleccionId == eleccionId || l.EleccionId == 0).ToListAsync();
|
||||
var estado = await _dbContext.EstadosRecuentosGenerales.AsNoTracking().Include(e => e.CategoriaElectoral).FirstOrDefaultAsync(e => e.EleccionId == eleccionId && e.CategoriaId == categoriaId && e.AmbitoGeograficoId == provincia.Id);
|
||||
var votosNoPositivosAgregados = await _dbContext.EstadosRecuentos.AsNoTracking().Where(e => e.EleccionId == eleccionId && e.CategoriaId == categoriaId && e.AmbitoGeografico.DistritoId == distritoId).GroupBy(e => 1).Select(g => new { VotosEnBlanco = g.Sum(e => e.VotosEnBlanco), VotosNulos = g.Sum(e => e.VotosNulos), VotosRecurridos = g.Sum(e => e.VotosRecurridos) }).FirstOrDefaultAsync();
|
||||
|
||||
var totalVotosPositivos = (decimal)votosAgregados.Sum(r => r.Votos);
|
||||
var votosEnBlanco = votosNoPositivosAgregados?.VotosEnBlanco ?? 0;
|
||||
var votosTotales = totalVotosPositivos + votosEnBlanco + (votosNoPositivosAgregados?.VotosNulos ?? 0) + (votosNoPositivosAgregados?.VotosRecurridos ?? 0);
|
||||
|
||||
var respuesta = new CategoriaResumenHomeDto
|
||||
{
|
||||
CategoriaId = categoriaId,
|
||||
CategoriaNombre = estado?.CategoriaElectoral.Nombre ?? (categoriaId == 2 ? "DIPUTADOS NACIONALES" : "SENADORES NACIONALES"),
|
||||
UltimaActualizacion = estado?.FechaTotalizacion ?? DateTime.UtcNow,
|
||||
EstadoRecuento = estado != null ? new EstadoRecuentoDto
|
||||
{
|
||||
ParticipacionPorcentaje = estado.ParticipacionPorcentaje,
|
||||
MesasTotalizadasPorcentaje = estado.MesasTotalizadasPorcentaje,
|
||||
CantidadVotantes = estado.CantidadVotantes
|
||||
} : null,
|
||||
VotosEnBlanco = votosEnBlanco,
|
||||
VotosEnBlancoPorcentaje = votosTotales > 0 ? (votosEnBlanco / votosTotales) * 100 : 0,
|
||||
VotosTotales = (long)votosTotales,
|
||||
Resultados = votosAgregados.Select(r =>
|
||||
{
|
||||
var provinciaAmbitoId = provincia.Id;
|
||||
var candidatoMatch = FindBestCandidatoMatch(todosLosOverrides, r.Agrupacion.Id, categoriaId, provinciaAmbitoId, eleccionId);
|
||||
|
||||
string? logoFinal =
|
||||
// Prioridad 1: Override Específico (EleccionId, CategoriaId, AmbitoId)
|
||||
todosLosLogos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == provinciaAmbitoId)?.LogoUrl
|
||||
// Prioridad 2: Override por Elección y Categoría (general a ámbitos)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == eleccionId && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)?.LogoUrl
|
||||
// Prioridad 3: Fallback Global para la CATEGORÍA (EleccionId = 0, CategoriaId)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null)?.LogoUrl
|
||||
// Prioridad 4: Fallback "Super Global" (EleccionId = 0, CategoriaId = 0)
|
||||
?? todosLosLogos.FirstOrDefault(l => l.EleccionId == 0 && l.AgrupacionPoliticaId == r.Agrupacion.Id && l.CategoriaId == 0 && l.AmbitoGeograficoId == null)?.LogoUrl;
|
||||
|
||||
return new ResultadoCandidatoDto
|
||||
{
|
||||
AgrupacionId = r.Agrupacion.Id,
|
||||
NombreAgrupacion = r.Agrupacion.Nombre,
|
||||
NombreCortoAgrupacion = r.Agrupacion.NombreCorto,
|
||||
NombreCandidato = candidatoMatch?.NombreCandidato,
|
||||
Color = r.Agrupacion.Color,
|
||||
Votos = r.Votos,
|
||||
Porcentaje = totalVotosPositivos > 0 ? (r.Votos / totalVotosPositivos) * 100 : 0,
|
||||
FotoUrl = logoFinal
|
||||
};
|
||||
}).ToList()
|
||||
};
|
||||
|
||||
return Ok(respuesta);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user