Trabajo de ajuste en widgets y db para frontend
This commit is contained in:
		
							
								
								
									
										181
									
								
								Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | ||||
| // src/Elecciones.Api/Controllers/AdminController.cs | ||||
| using Elecciones.Database; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Elecciones.Core.DTOs.ApiRequests; | ||||
| using Elecciones.Database.Entities; | ||||
| using Microsoft.AspNetCore.Authorization; | ||||
| using Elecciones.Core.Enums; | ||||
|  | ||||
| namespace Elecciones.Api.Controllers; | ||||
|  | ||||
| [ApiController] | ||||
| [Route("api/[controller]")] | ||||
| [Authorize] | ||||
| public class AdminController : ControllerBase | ||||
| { | ||||
|   private readonly EleccionesDbContext _dbContext; | ||||
|   private readonly ILogger<AdminController> _logger; | ||||
|  | ||||
|   public AdminController(EleccionesDbContext dbContext, ILogger<AdminController> logger) | ||||
|   { | ||||
|     _dbContext = dbContext; | ||||
|     _logger = logger; | ||||
|   } | ||||
|  | ||||
|   // Endpoint para obtener todas las agrupaciones para el panel de admin | ||||
|   [HttpGet("agrupaciones")] | ||||
|   public async Task<IActionResult> GetAgrupaciones() | ||||
|   { | ||||
|     var agrupaciones = await _dbContext.AgrupacionesPoliticas | ||||
|         .AsNoTracking() | ||||
|         .OrderBy(a => a.Nombre) | ||||
|         .ToListAsync(); | ||||
|  | ||||
|     return Ok(agrupaciones); | ||||
|   } | ||||
|  | ||||
|   [HttpPut("agrupaciones/{id}")] | ||||
|   public async Task<IActionResult> UpdateAgrupacion(string id, [FromBody] UpdateAgrupacionDto agrupacionDto) | ||||
|   { | ||||
|     // Buscamos la agrupación en la base de datos por su ID. | ||||
|     var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(id); | ||||
|  | ||||
|     if (agrupacion == null) | ||||
|     { | ||||
|       // Si no existe, devolvemos un error 404 Not Found. | ||||
|       return NotFound(new { message = $"No se encontró la agrupación con ID {id}" }); | ||||
|     } | ||||
|  | ||||
|     // Actualizamos las propiedades de la entidad con los valores del DTO. | ||||
|     agrupacion.NombreCorto = agrupacionDto.NombreCorto; | ||||
|     agrupacion.Color = agrupacionDto.Color; | ||||
|     agrupacion.LogoUrl = agrupacionDto.LogoUrl; | ||||
|  | ||||
|     // Guardamos los cambios en la base de datos. | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     _logger.LogInformation("Se actualizó la agrupación: {Id}", id); | ||||
|  | ||||
|     // Devolvemos una respuesta 204 No Content, que es el estándar para un PUT exitoso sin devolver datos. | ||||
|     return NoContent(); | ||||
|   } | ||||
|  | ||||
|   [HttpPut("agrupaciones/orden-diputados")] | ||||
|   public async Task<IActionResult> UpdateDiputadosOrden([FromBody] List<string> idsAgrupacionesOrdenadas) | ||||
|   { | ||||
|     // Reseteamos solo el orden de diputados | ||||
|     await _dbContext.AgrupacionesPoliticas.ExecuteUpdateAsync(s => s.SetProperty(a => a.OrdenDiputados, (int?)null)); | ||||
|  | ||||
|     for (int i = 0; i < idsAgrupacionesOrdenadas.Count; i++) | ||||
|     { | ||||
|       var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(idsAgrupacionesOrdenadas[i]); | ||||
|       if (agrupacion != null) agrupacion.OrdenDiputados = i + 1; | ||||
|     } | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     return Ok(); | ||||
|   } | ||||
|  | ||||
|   [HttpPut("agrupaciones/orden-senadores")] | ||||
|   public async Task<IActionResult> UpdateSenadoresOrden([FromBody] List<string> idsAgrupacionesOrdenadas) | ||||
|   { | ||||
|     // Reseteamos solo el orden de senadores | ||||
|     await _dbContext.AgrupacionesPoliticas.ExecuteUpdateAsync(s => s.SetProperty(a => a.OrdenSenadores, (int?)null)); | ||||
|  | ||||
|     for (int i = 0; i < idsAgrupacionesOrdenadas.Count; i++) | ||||
|     { | ||||
|       var agrupacion = await _dbContext.AgrupacionesPoliticas.FindAsync(idsAgrupacionesOrdenadas[i]); | ||||
|       if (agrupacion != null) agrupacion.OrdenSenadores = i + 1; | ||||
|     } | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     return Ok(); | ||||
|   } | ||||
|  | ||||
|   // LEER todas las configuraciones | ||||
|   [HttpGet("configuracion")] | ||||
|   public async Task<IActionResult> GetConfiguracion() | ||||
|   { | ||||
|     var configs = await _dbContext.Configuraciones.AsNoTracking().ToListAsync(); | ||||
|     // Devolvemos un diccionario para que sea fácil de consumir en el frontend | ||||
|     return Ok(configs.ToDictionary(c => c.Clave, c => c.Valor)); | ||||
|   } | ||||
|  | ||||
|   // GUARDAR un conjunto de configuraciones | ||||
|   [HttpPut("configuracion")] | ||||
|   public async Task<IActionResult> UpdateConfiguracion([FromBody] Dictionary<string, string> nuevasConfiguraciones) | ||||
|   { | ||||
|     foreach (var kvp in nuevasConfiguraciones) | ||||
|     { | ||||
|       var config = await _dbContext.Configuraciones.FindAsync(kvp.Key); | ||||
|       if (config == null) | ||||
|       { | ||||
|         // Si no existe, la creamos | ||||
|         _dbContext.Configuraciones.Add(new Configuracion { Clave = kvp.Key, Valor = kvp.Value }); | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         // Si existe, la actualizamos | ||||
|         config.Valor = kvp.Value; | ||||
|       } | ||||
|     } | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     return NoContent(); | ||||
|   } | ||||
|  | ||||
|   // LEER: Obtener todas las bancadas para una cámara, con su partido y ocupante actual | ||||
|   [HttpGet("bancadas/{camara}")] | ||||
|   public async Task<IActionResult> GetBancadas(TipoCamara camara) | ||||
|   { | ||||
|     var bancadas = await _dbContext.Bancadas | ||||
|         .AsNoTracking() | ||||
|         .Include(b => b.AgrupacionPolitica) | ||||
|         .Include(b => b.Ocupante) | ||||
|         .Where(b => b.Camara == camara) | ||||
|         .OrderBy(b => b.Id) // Ordenar por ID para consistencia | ||||
|         .ToListAsync(); | ||||
|  | ||||
|     return Ok(bancadas); | ||||
|   } | ||||
|  | ||||
|   // ACTUALIZAR: Asignar un partido y/o un ocupante a una bancada específica | ||||
|   [HttpPut("bancadas/{bancadaId}")] | ||||
|   public async Task<IActionResult> UpdateBancada(int bancadaId, [FromBody] UpdateBancadaDto dto) | ||||
|   { | ||||
|     var bancada = await _dbContext.Bancadas | ||||
|         .Include(b => b.Ocupante) | ||||
|         .FirstOrDefaultAsync(b => b.Id == bancadaId); | ||||
|  | ||||
|     if (bancada == null) | ||||
|     { | ||||
|       return NotFound(new { message = "La bancada especificada no existe." }); | ||||
|     } | ||||
|  | ||||
|     // 1. Actualizar la agrupación de la bancada | ||||
|     bancada.AgrupacionPoliticaId = dto.AgrupacionPoliticaId; | ||||
|  | ||||
|     // 2. Lógica de "Upsert" (Update/Insert/Delete) para el ocupante | ||||
|     if (string.IsNullOrEmpty(dto.NombreOcupante)) | ||||
|     { | ||||
|       // Si no se envía nombre, se elimina el ocupante existente | ||||
|       if (bancada.Ocupante != null) | ||||
|       { | ||||
|         _dbContext.OcupantesBancas.Remove(bancada.Ocupante); | ||||
|       } | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|       // Si se envía un nombre, se crea o actualiza el ocupante | ||||
|       if (bancada.Ocupante == null) | ||||
|       { | ||||
|         bancada.Ocupante = new OcupanteBanca(); // Crea uno nuevo si no existía | ||||
|       } | ||||
|       bancada.Ocupante.NombreOcupante = dto.NombreOcupante; | ||||
|       bancada.Ocupante.FotoUrl = dto.FotoUrl; | ||||
|       bancada.Ocupante.Periodo = dto.Periodo; | ||||
|     } | ||||
|  | ||||
|     await _dbContext.SaveChangesAsync(); | ||||
|     _logger.LogInformation("Se actualizó la bancada con ID: {BancadaId}", bancadaId); | ||||
|  | ||||
|     return NoContent(); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,64 @@ | ||||
| using Elecciones.Core.Services; | ||||
| using Elecciones.Database; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Security.Claims; | ||||
| using System.Text; | ||||
|  | ||||
| namespace Elecciones.Api.Controllers; | ||||
|  | ||||
| [ApiController] | ||||
| [Route("api/[controller]")] | ||||
| public class AuthController : ControllerBase | ||||
| { | ||||
|     private readonly EleccionesDbContext _context; | ||||
|     private readonly IPasswordHasher _passwordHasher; | ||||
|     private readonly IConfiguration _configuration; | ||||
|  | ||||
|     public AuthController(EleccionesDbContext context, IPasswordHasher passwordHasher, IConfiguration configuration) | ||||
|     { | ||||
|         _context = context; | ||||
|         _passwordHasher = passwordHasher; | ||||
|         _configuration = configuration; | ||||
|     } | ||||
|  | ||||
|     public record LoginRequest(string Username, string Password); | ||||
|  | ||||
|     [HttpPost("login")] | ||||
|     public async Task<IActionResult> Login([FromBody] LoginRequest loginRequest) | ||||
|     { | ||||
|         var user = await _context.AdminUsers | ||||
|             .FirstOrDefaultAsync(u => u.Username == loginRequest.Username); | ||||
|  | ||||
|         if (user == null || !_passwordHasher.VerifyPassword(loginRequest.Password, user.PasswordHash, user.PasswordSalt)) | ||||
|         { | ||||
|             return Unauthorized(new { message = "Credenciales inválidas." }); | ||||
|         } | ||||
|  | ||||
|         var token = GenerateJwtToken(user); | ||||
|         return Ok(new { token }); | ||||
|     } | ||||
|  | ||||
|     private string GenerateJwtToken(Database.Entities.AdminUser user) | ||||
|     { | ||||
|         var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!)); | ||||
|         var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); | ||||
|  | ||||
|         var claims = new[] | ||||
|         { | ||||
|             new Claim(JwtRegisteredClaimNames.Sub, user.Username), | ||||
|             new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) | ||||
|         }; | ||||
|  | ||||
|         var token = new JwtSecurityToken( | ||||
|             issuer: _configuration["Jwt:Issuer"], | ||||
|             audience: _configuration["Jwt:Audience"], | ||||
|             claims: claims, | ||||
|             expires: DateTime.UtcNow.AddHours(8), // El token expira en 8 horas | ||||
|             signingCredentials: credentials); | ||||
|  | ||||
|         return new JwtSecurityTokenHandler().WriteToken(token); | ||||
|     } | ||||
| } | ||||
| @@ -2,8 +2,6 @@ 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; | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -1,9 +1,6 @@ | ||||
| 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; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user