Trabajo de ajuste en widgets y db para frontend

This commit is contained in:
2025-08-29 09:54:22 -03:00
parent 55954e18a7
commit 1ed9a49a53
93 changed files with 10259 additions and 609 deletions

View File

@@ -1,13 +1,9 @@
// src/Elecciones.Api/Controllers/ResultadosController.cs
using Elecciones.Core.DTOs.ApiResponses;
using Elecciones.Database;
using Elecciones.Database.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Elecciones.Core.DTOs.Configuration;
namespace Elecciones.Api.Controllers;
@@ -208,28 +204,23 @@ public class ResultadosController : ControllerBase
});
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
)
.Include(rv => rv.AmbitoGeografico)
.Where(rv => rv.AmbitoGeografico.NivelId == 30) // Aseguramos que solo sean los ámbitos de nivel 30
.Select(rv => new
{
// 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();
.Join(
maxVotosPorAmbito,
voto => new { AmbitoId = voto.AmbitoGeograficoId, Votos = voto.CantidadVotos },
max => new { AmbitoId = max.AmbitoId, Votos = max.MaxVotos },
(voto, max) => voto
)
.Include(rv => rv.AmbitoGeografico)
.Include(rv => rv.AgrupacionPolitica)
.Where(rv => rv.AmbitoGeografico.NivelId == 30)
.Select(rv => new
{
AmbitoId = rv.AmbitoGeografico.Id,
DepartamentoNombre = rv.AmbitoGeografico.Nombre,
AgrupacionGanadoraId = rv.AgrupacionPoliticaId,
ColorGanador = rv.AgrupacionPolitica.Color
})
.ToListAsync();
return Ok(resultadosGanadores);
}
@@ -298,18 +289,239 @@ public class ResultadosController : ControllerBase
}
[HttpGet("composicion-congreso")]
public IActionResult GetComposicionCongreso()
public async Task<IActionResult> GetComposicionCongreso()
{
// El framework .NET se encarga de leer appsettings.json y mapearlo a nuestras clases.
var composicionConfig = _configuration.GetSection("ComposicionCongreso")
.Get<ComposicionCongresoConfig>();
var config = await _dbContext.Configuraciones
.AsNoTracking()
.ToDictionaryAsync(c => c.Clave, c => c.Valor);
if (composicionConfig == null)
config.TryGetValue("UsarDatosDeBancadasOficiales", out var usarDatosOficialesValue);
bool usarDatosOficiales = usarDatosOficialesValue == "true";
if (usarDatosOficiales)
{
// Devolvemos un error si la sección no se encuentra en el archivo de configuración.
return NotFound("La configuración para la composición del congreso no fue encontrada.");
return await GetComposicionDesdeBancadasOficiales(config);
}
else
{
return await GetComposicionDesdeProyecciones(config);
}
}
private async Task<IActionResult> GetComposicionDesdeBancadasOficiales(Dictionary<string, string> config)
{
config.TryGetValue("MostrarOcupantes", out var mostrarOcupantesValue);
bool mostrarOcupantes = mostrarOcupantesValue == "true";
// Se declara la variable explícitamente como IQueryable<Bancada>
IQueryable<Bancada> bancadasQuery = _dbContext.Bancadas.AsNoTracking()
.Include(b => b.AgrupacionPolitica);
if (mostrarOcupantes)
{
// Ahora sí podemos añadir otro .Include() sin problemas de tipo
bancadasQuery = bancadasQuery.Include(b => b.Ocupante);
}
return Ok(composicionConfig);
var bancadas = await bancadasQuery.ToListAsync();
// --- CAMBIO 2: Eliminar la carga manual de Ocupantes ---
// Ya no necesitamos 'ocupantesLookup'. Se puede borrar todo este bloque:
/*
var ocupantesLookup = new Dictionary<int, OcupanteBanca>();
if (mostrarOcupantes)
{
ocupantesLookup = (await _dbContext.OcupantesBancas.AsNoTracking()
.ToListAsync())
.ToDictionary(o => o.BancadaId);
}
*/
var bancasPorAgrupacion = bancadas
.Where(b => b.AgrupacionPoliticaId != null)
.GroupBy(b => new { b.AgrupacionPoliticaId, b.Camara })
.Select(g => new
{
Agrupacion = g.First().AgrupacionPolitica,
g.Key.Camara,
BancasTotales = g.Count()
})
.ToList();
var presidenteDiputados = bancasPorAgrupacion
.Where(b => b.Camara == Core.Enums.TipoCamara.Diputados)
.OrderByDescending(b => b.BancasTotales)
.FirstOrDefault()?.Agrupacion;
config.TryGetValue("PresidenciaSenadores", out var idPartidoPresidenteSenadores);
var presidenteSenadores = await _dbContext.AgrupacionesPoliticas.FindAsync(idPartidoPresidenteSenadores);
object MapearPartidos(Core.Enums.TipoCamara camara)
{
// 1. Filtramos las bancadas que nos interesan (por cámara y que tengan partido).
var bancadasDeCamara = bancadas
.Where(b => b.Camara == camara && b.AgrupacionPolitica != null);
// 2. --- ¡EL CAMBIO CLAVE ESTÁ AQUÍ! ---
// Agrupamos por el ID de la Agrupación, no por el objeto.
// Esto garantiza que todas las bancadas del mismo partido terminen en el MISMO grupo.
var partidosDeCamara = bancadasDeCamara
.GroupBy(b => b.AgrupacionPolitica!.Id)
.Select(g => new
{
// La Agrupacion la podemos tomar del primer elemento del grupo,
// ya que todas las bancadas del grupo pertenecen al mismo partido.
Agrupacion = g.First().AgrupacionPolitica!,
// g ahora contiene la lista COMPLETA de bancadas para esta agrupación.
BancasDelPartido = g.ToList()
});
// 3. Ordenamos, como antes, pero ahora sobre una lista de grupos correcta.
var partidosOrdenados = (camara == Core.Enums.TipoCamara.Diputados)
? partidosDeCamara.OrderBy(p => p.Agrupacion.OrdenDiputados ?? 999)
: partidosDeCamara.OrderBy(p => p.Agrupacion.OrdenSenadores ?? 999);
// 4. Mapeamos al resultado final.
return partidosOrdenados
.ThenByDescending(p => p.BancasDelPartido.Count)
.Select(p =>
{
// Ahora 'p.BancasDelPartido' contiene TODAS las bancadas del partido (en tu caso, las 2).
// Cuando hagamos el .Select() aquí, recorrerá ambas y encontrará a los ocupantes.
var ocupantesDelPartido = p.BancasDelPartido
.Select(b => b.Ocupante)
.Where(o => o != null)
.ToList();
return new
{
p.Agrupacion.Id,
p.Agrupacion.Nombre,
p.Agrupacion.NombreCorto,
p.Agrupacion.Color,
BancasTotales = p.BancasDelPartido.Count,
// ¡Esta lista ahora debería contener a tus 2 ocupantes!
Ocupantes = mostrarOcupantes ? ocupantesDelPartido : new List<OcupanteBanca?>()
};
}).ToList();
}
// El resto del método permanece igual...
var diputados = new
{
CamaraNombre = "Cámara de Diputados",
TotalBancas = 92,
BancasEnJuego = 0,
Partidos = MapearPartidos(Core.Enums.TipoCamara.Diputados),
PresidenteBancada = presidenteDiputados != null ? new { presidenteDiputados.Color } : null
};
var senadores = new
{
CamaraNombre = "Cámara de Senadores",
TotalBancas = 46,
BancasEnJuego = 0,
Partidos = MapearPartidos(Core.Enums.TipoCamara.Senadores),
PresidenteBancada = presidenteSenadores != null ? new { presidenteSenadores.Color } : null
};
return Ok(new { Diputados = diputados, Senadores = senadores });
}
private async Task<IActionResult> GetComposicionDesdeProyecciones(Dictionary<string, string> config)
{
var bancasPorAgrupacion = await _dbContext.ProyeccionesBancas
.AsNoTracking()
.GroupBy(p => new { p.AgrupacionPoliticaId, p.CategoriaId })
.Select(g => new
{
AgrupacionId = g.Key.AgrupacionPoliticaId,
CategoriaId = g.Key.CategoriaId,
BancasTotales = g.Sum(p => p.NroBancas)
})
.ToListAsync();
var todasAgrupaciones = await _dbContext.AgrupacionesPoliticas.AsNoTracking().ToDictionaryAsync(a => a.Id);
config.TryGetValue("PresidenciaSenadores", out var idPartidoPresidenteSenadores);
todasAgrupaciones.TryGetValue(idPartidoPresidenteSenadores ?? "", out var presidenteSenadores);
string? idPartidoPresidenteDiputados = bancasPorAgrupacion
.Where(b => b.CategoriaId == 6)
.OrderByDescending(b => b.BancasTotales)
.FirstOrDefault()?.AgrupacionId;
todasAgrupaciones.TryGetValue(idPartidoPresidenteDiputados ?? "", out var presidenteDiputados);
object MapearPartidos(int categoriaId)
{
var partidosDeCamara = bancasPorAgrupacion
.Where(b => b.CategoriaId == categoriaId && b.BancasTotales > 0)
.Select(b => new { Bancas = b, Agrupacion = todasAgrupaciones[b.AgrupacionId] });
if (categoriaId == 6) // Diputados
partidosDeCamara = partidosDeCamara.OrderBy(b => b.Agrupacion.OrdenDiputados ?? 999)
.ThenByDescending(b => b.Bancas.BancasTotales);
else // Senadores
partidosDeCamara = partidosDeCamara.OrderBy(b => b.Agrupacion.OrdenSenadores ?? 999)
.ThenByDescending(b => b.Bancas.BancasTotales);
return partidosDeCamara
.Select(b => new
{
b.Agrupacion.Id,
b.Agrupacion.Nombre,
b.Agrupacion.NombreCorto,
b.Agrupacion.Color,
b.Bancas.BancasTotales
})
.ToList();
}
var diputados = new
{
CamaraNombre = "Cámara de Diputados",
TotalBancas = 92,
BancasEnJuego = 46,
Partidos = MapearPartidos(6),
PresidenteBancada = presidenteDiputados != null ? new { presidenteDiputados.Color } : null
};
var senadores = new
{
CamaraNombre = "Cámara de Senadores",
TotalBancas = 46,
BancasEnJuego = 23,
Partidos = MapearPartidos(5),
PresidenteBancada = presidenteSenadores != null ? new { presidenteSenadores.Color } : null
};
return Ok(new { Diputados = diputados, Senadores = senadores });
}
[HttpGet("bancadas-detalle")]
public async Task<IActionResult> GetBancadasConOcupantes()
{
var config = await _dbContext.Configuraciones.AsNoTracking().ToDictionaryAsync(c => c.Clave, c => c.Valor);
config.TryGetValue("MostrarOcupantes", out var mostrarOcupantesValue);
if (mostrarOcupantesValue != "true")
{
// Si la opción está desactivada, devolvemos un array vacío.
return Ok(new List<object>());
}
var bancadasConOcupantes = await _dbContext.Bancadas
.AsNoTracking()
.Include(b => b.Ocupante)
.Where(b => b.Ocupante != null) // Solo las que tienen un ocupante asignado
.Select(b => new
{
b.Id,
b.Camara,
b.AgrupacionPoliticaId,
Ocupante = b.Ocupante
})
.ToListAsync();
return Ok(bancadasConOcupantes);
}
}