2025-09-06 21:44:52 -03:00
//Elecciones.Api/Program.cs
2025-08-14 13:12:16 -03:00
using Elecciones.Database ;
using Microsoft.EntityFrameworkCore ;
2025-08-15 17:31:51 -03:00
using Serilog ;
2025-08-29 09:54:22 -03:00
using Elecciones.Core.Services ;
using Elecciones.Infrastructure.Services ;
using System.Text ;
using Microsoft.AspNetCore.Authentication.JwtBearer ;
using Microsoft.IdentityModel.Tokens ;
using Elecciones.Database.Entities ;
using System.Text.Json.Serialization ;
2025-09-03 17:54:49 -03:00
using Microsoft.AspNetCore.HttpOverrides ;
2025-09-28 19:04:09 -03:00
using Elecciones.Core.Enums ;
using Microsoft.OpenApi.Models ;
2025-08-14 13:12:16 -03:00
2025-08-15 17:31:51 -03:00
// Esta es la estructura estándar y recomendada.
2025-08-14 12:37:57 -03:00
var builder = WebApplication . CreateBuilder ( args ) ;
2025-09-06 21:44:52 -03:00
// 1. Registra el servicio del interruptor como un Singleton.
// Esto asegura que toda la aplicación comparta la MISMA instancia del interruptor.
builder . Services . AddSingleton < LoggingSwitchService > ( ) ;
builder . Host . UseSerilog ( ( context , services , configuration ) = >
{
// 2. Obtenemos la instancia del interruptor que acabamos de registrar.
var loggingSwitch = services . GetRequiredService < LoggingSwitchService > ( ) ;
configuration
. ReadFrom . Configuration ( context . Configuration )
. ReadFrom . Services ( services )
. Enrich . FromLogContext ( )
// 3. Establecemos el nivel mínimo de logging controlado por el interruptor.
. MinimumLevel . ControlledBy ( loggingSwitch . LevelSwitch )
. WriteTo . Console ( )
. WriteTo . File ( "logs/api-.log" , rollingInterval : RollingInterval . Day ) ; // o "logs/worker-.log"
} ) ;
2025-08-14 13:12:16 -03:00
2025-08-15 17:31:51 -03:00
// 2. Añadir servicios al contenedor.
2025-08-14 13:12:16 -03:00
var connectionString = builder . Configuration . GetConnectionString ( "DefaultConnection" ) ;
builder . Services . AddDbContext < EleccionesDbContext > ( options = >
options . UseSqlServer ( connectionString ) ) ;
2025-08-29 09:54:22 -03:00
builder . Services . AddScoped < IPasswordHasher , PasswordHasher > ( ) ;
2025-08-14 13:12:16 -03:00
2025-08-29 09:54:22 -03:00
builder . Services . AddControllers ( ) . AddJsonOptions ( options = >
{
// Esto le dice al serializador que maneje las referencias circulares
options . JsonSerializerOptions . ReferenceHandler = ReferenceHandler . IgnoreCycles ;
} ) ;
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins" ;
2025-08-14 13:12:16 -03:00
builder . Services . AddCors ( options = >
{
2025-08-29 09:54:22 -03:00
options . AddPolicy ( name : MyAllowSpecificOrigins ,
policy = >
{
policy . WithOrigins (
2025-09-03 18:56:01 -03:00
"http://localhost:5173" ,
2025-09-03 17:06:39 -03:00
"http://localhost:5174" ,
2025-09-03 18:56:01 -03:00
"http://192.168.5.128:8700" ,
2025-09-03 17:06:39 -03:00
"https://www.eldia.com" ,
2025-09-05 13:40:33 -03:00
"https://extras.eldia.com" ,
"https://eldia.mustang.cloud"
2025-08-30 11:31:45 -03:00
)
2025-08-29 09:54:22 -03:00
. AllowAnyHeader ( )
. AllowAnyMethod ( ) ;
} ) ;
} ) ;
builder . Services . AddAuthentication ( JwtBearerDefaults . AuthenticationScheme )
. AddJwtBearer ( options = >
2025-08-14 13:12:16 -03:00
{
2025-08-29 09:54:22 -03:00
options . TokenValidationParameters = new TokenValidationParameters
2025-08-14 15:51:19 -03:00
{
2025-08-29 09:54:22 -03:00
ValidateIssuer = true ,
ValidateAudience = true ,
ValidateLifetime = true ,
ValidateIssuerSigningKey = true ,
ValidIssuer = builder . Configuration [ "Jwt:Issuer" ] ,
ValidAudience = builder . Configuration [ "Jwt:Audience" ] ,
IssuerSigningKey = new SymmetricSecurityKey ( Encoding . UTF8 . GetBytes ( builder . Configuration [ "Jwt:Key" ] ! ) )
} ;
2025-08-14 13:12:16 -03:00
} ) ;
builder . Services . AddEndpointsApiExplorer ( ) ;
2025-09-28 19:04:09 -03:00
//builder.Services.AddSwaggerGen();
builder . Services . AddSwaggerGen ( options = >
{
// 1. Definir el esquema de seguridad que usaremos (Bearer Token)
options . AddSecurityDefinition ( "Bearer" , new OpenApiSecurityScheme
{
Description = "Autorización JWT usando el esquema Bearer. Ingresa 'Bearer' [espacio] y luego tu token. Ejemplo: 'Bearer 12345abcdef'" ,
Name = "Authorization" , // El nombre del header HTTP
In = ParameterLocation . Header , // Dónde se ubicará el token (en el header)
Type = SecuritySchemeType . ApiKey , // El tipo de esquema
Scheme = "Bearer" // El nombre del esquema
} ) ;
// 2. Aplicar este requisito de seguridad a todos los endpoints que lo necesiten
options . AddSecurityRequirement ( new OpenApiSecurityRequirement ( )
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType . SecurityScheme ,
Id = "Bearer" // Debe coincidir con el nombre que le dimos en AddSecurityDefinition
} ,
Scheme = "oauth2" ,
Name = "Bearer" ,
In = ParameterLocation . Header ,
} ,
new List < string > ( )
}
} ) ;
} ) ;
2025-08-14 12:37:57 -03:00
2025-09-03 17:54:49 -03:00
builder . Services . Configure < ForwardedHeadersOptions > ( options = >
{
options . ForwardedHeaders =
ForwardedHeaders . XForwardedFor | ForwardedHeaders . XForwardedProto ;
options . KnownNetworks . Clear ( ) ;
options . KnownProxies . Clear ( ) ;
} ) ;
2025-08-15 17:31:51 -03:00
// 3. Construir la aplicación.
2025-08-14 12:37:57 -03:00
var app = builder . Build ( ) ;
2025-09-06 21:44:52 -03:00
// --- LÓGICA PARA LEER EL NIVEL DE LOGGING AL INICIO ---
// Creamos un scope temporal para leer la configuración de la BD
using ( var scope = app . Services . CreateScope ( ) ) // O 'host.Services.CreateScope()'
{
var services = scope . ServiceProvider ;
try
{
// El resto de la lógica no cambia
var dbContext = services . GetRequiredService < EleccionesDbContext > ( ) ;
var loggingSwitchService = services . GetRequiredService < LoggingSwitchService > ( ) ;
var logLevelConfig = await dbContext . Configuraciones
. AsNoTracking ( )
. FirstOrDefaultAsync ( c = > c . Clave = = "Logging_Level" ) ;
if ( logLevelConfig ! = null )
{
loggingSwitchService . SetLoggingLevel ( logLevelConfig . Valor ) ;
Console . WriteLine ( $"--> Nivel de logging inicial establecido desde la BD a: {logLevelConfig.Valor}" ) ;
}
}
catch ( Exception ex )
{
// Si hay un error (ej. la BD no está disponible al arrancar), se usará el nivel por defecto 'Information'.
Console . WriteLine ( $"--> No se pudo establecer el nivel de logging desde la BD: {ex.Message}" ) ;
}
}
2025-09-03 17:54:49 -03:00
app . UseForwardedHeaders ( ) ;
2025-10-01 10:03:01 -03:00
// --- INICIO DEL BLOQUE DE SEEDERS UNIFICADO Y CORREGIDO ---
2025-08-29 09:54:22 -03:00
using ( var scope = app . Services . CreateScope ( ) )
{
var services = scope . ServiceProvider ;
2025-10-01 10:03:01 -03:00
var context = services . GetRequiredService < EleccionesDbContext > ( ) ;
var logger = services . GetRequiredService < ILogger < Program > > ( ) ;
var hasher = services . GetRequiredService < IPasswordHasher > ( ) ;
// --- SEEDER 1: DATOS ESTRUCTURALES BÁSICOS (se ejecutan una sola vez si la BD está vacía) ---
// Estos son los datos maestros que NUNCA cambian.
// Usuario Admin
if ( ! await context . AdminUsers . AnyAsync ( ) )
2025-08-29 09:54:22 -03:00
{
2025-10-01 10:03:01 -03:00
var ( hash , salt ) = hasher . HashPassword ( "PTP847elec" ) ;
context . AdminUsers . Add ( new AdminUser { Username = "admin" , PasswordHash = hash , PasswordSalt = salt } ) ;
await context . SaveChangesAsync ( ) ;
logger . LogInformation ( "--> Admin user seeded." ) ;
2025-08-29 09:54:22 -03:00
}
2025-10-01 10:03:01 -03:00
// Elecciones
if ( ! await context . Elecciones . AnyAsync ( ) )
2025-09-28 19:04:09 -03:00
{
context . Elecciones . AddRange (
new Eleccion { Id = 1 , Nombre = "Elecciones Provinciales 2025" , Nivel = "Provincial" , DistritoId = "02" , Fecha = new DateOnly ( 2025 , 10 , 26 ) } ,
new Eleccion { Id = 2 , Nombre = "Elecciones Nacionales 2025" , Nivel = "Nacional" , DistritoId = "00" , Fecha = new DateOnly ( 2025 , 10 , 26 ) }
) ;
2025-10-01 10:03:01 -03:00
await context . SaveChangesAsync ( ) ;
logger . LogInformation ( "--> Seeded Eleccion entities." ) ;
2025-09-28 19:04:09 -03:00
}
2025-10-01 10:03:01 -03:00
// Bancas Físicas
if ( ! await context . Bancadas . AnyAsync ( ) )
2025-08-29 09:54:22 -03:00
{
var bancas = new List < Bancada > ( ) ;
2025-10-01 10:03:01 -03:00
for ( int i = 1 ; i < = 92 ; i + + ) { bancas . Add ( new Bancada { EleccionId = 1 , Camara = TipoCamara . Diputados , NumeroBanca = i } ) ; }
for ( int i = 1 ; i < = 46 ; i + + ) { bancas . Add ( new Bancada { EleccionId = 1 , Camara = TipoCamara . Senadores , NumeroBanca = i } ) ; }
for ( int i = 1 ; i < = 257 ; i + + ) { bancas . Add ( new Bancada { EleccionId = 2 , Camara = TipoCamara . Diputados , NumeroBanca = i } ) ; }
for ( int i = 1 ; i < = 72 ; i + + ) { bancas . Add ( new Bancada { EleccionId = 2 , Camara = TipoCamara . Senadores , NumeroBanca = i } ) ; }
await context . Bancadas . AddRangeAsync ( bancas ) ;
await context . SaveChangesAsync ( ) ;
logger . LogInformation ( "--> Seeded {Count} bancas físicas para ambas elecciones." , bancas . Count ) ;
2025-08-29 09:54:22 -03:00
}
2025-10-01 10:03:01 -03:00
// Configuraciones por Defecto
var defaultConfiguraciones = new Dictionary < string , string > {
{ "MostrarOcupantes" , "true" } , { "TickerResultadosCantidad" , "3" } , { "ConcejalesResultadosCantidad" , "5" } ,
{ "Worker_Resultados_Activado" , "false" } , { "Worker_Bajas_Activado" , "false" } , { "Worker_Prioridad" , "Resultados" } ,
{ "Logging_Level" , "Information" } , { "PresidenciaDiputadosNacional" , "" } , { "PresidenciaDiputadosNacional_TipoBanca" , "ganada" } ,
2025-09-28 19:04:09 -03:00
{ "PresidenciaSenadoNacional_TipoBanca" , "ganada" }
2025-09-06 21:44:52 -03:00
} ;
foreach ( var config in defaultConfiguraciones )
2025-09-01 14:04:40 -03:00
{
2025-10-01 10:03:01 -03:00
if ( ! await context . Configuraciones . AnyAsync ( c = > c . Clave = = config . Key ) )
2025-09-06 21:44:52 -03:00
context . Configuraciones . Add ( new Configuracion { Clave = config . Key , Valor = config . Value } ) ;
2025-09-01 14:04:40 -03:00
}
2025-10-01 10:03:01 -03:00
await context . SaveChangesAsync ( ) ;
logger . LogInformation ( "--> Default configurations verified/seeded." ) ;
2025-09-06 21:44:52 -03:00
2025-09-17 11:31:17 -03:00
2025-10-01 10:03:01 -03:00
// --- SEEDER 2: DATOS DE EJEMPLO PARA ELECCIÓN NACIONAL (se ejecuta solo si faltan sus votos) ---
2025-09-17 11:31:17 -03:00
const int eleccionNacionalId = 2 ;
2025-10-01 10:03:01 -03:00
if ( ! await context . ResultadosVotos . AnyAsync ( r = > r . EleccionId = = eleccionNacionalId ) )
2025-09-17 11:31:17 -03:00
{
2025-10-01 10:03:01 -03:00
logger . LogInformation ( "--> No se encontraron datos de votos para la elección nacional ID {EleccionId}. Generando datos de simulación..." , eleccionNacionalId ) ;
// PASO A: VERIFICAR/CREAR DEPENDENCIAS (Ámbitos, Categorías)
if ( ! await context . CategoriasElectorales . AnyAsync ( c = > c . Id = = 1 ) )
context . CategoriasElectorales . Add ( new CategoriaElectoral { Id = 1 , Nombre = "SENADORES NACIONALES" , Orden = 2 } ) ;
if ( ! await context . CategoriasElectorales . AnyAsync ( c = > c . Id = = 2 ) )
context . CategoriasElectorales . Add ( new CategoriaElectoral { Id = 2 , Nombre = "DIPUTADOS NACIONALES" , Orden = 3 } ) ;
var provinciasMaestras = new Dictionary < string , string > {
{ "01" , "CIUDAD AUTONOMA DE BUENOS AIRES" } , { "02" , "BUENOS AIRES" } , { "03" , "CATAMARCA" } , { "04" , "CORDOBA" } , { "05" , "CORRIENTES" } ,
{ "06" , "CHACO" } , { "07" , "CHUBUT" } , { "08" , "ENTRE RIOS" } , { "09" , "FORMOSA" } , { "10" , "JUJUY" } , { "11" , "LA PAMPA" } ,
{ "12" , "LA RIOJA" } , { "13" , "MENDOZA" } , { "14" , "MISIONES" } , { "15" , "NEUQUEN" } , { "16" , "RIO NEGRO" } , { "17" , "SALTA" } ,
{ "18" , "SAN JUAN" } , { "19" , "SAN LUIS" } , { "20" , "SANTA CRUZ" } , { "21" , "SANTA FE" } , { "22" , "SANTIAGO DEL ESTERO" } ,
{ "23" , "TIERRA DEL FUEGO" } , { "24" , "TUCUMAN" }
2025-09-17 11:31:17 -03:00
} ;
foreach ( var p in provinciasMaestras )
{
if ( ! await context . AmbitosGeograficos . AnyAsync ( a = > a . NivelId = = 10 & & a . DistritoId = = p . Key ) )
context . AmbitosGeograficos . Add ( new AmbitoGeografico { Nombre = p . Value , NivelId = 10 , DistritoId = p . Key } ) ;
}
await context . SaveChangesAsync ( ) ;
var provinciasEnDb = await context . AmbitosGeograficos . AsNoTracking ( ) . Where ( a = > a . NivelId = = 10 ) . ToListAsync ( ) ;
foreach ( var provincia in provinciasEnDb )
{
2025-09-22 17:56:04 -03:00
if ( ! await context . AmbitosGeograficos . AnyAsync ( a = > a . NivelId = = 30 & & a . DistritoId = = provincia . DistritoId ) )
2025-09-17 11:31:17 -03:00
{
for ( int i = 1 ; i < = 5 ; i + + )
2025-09-22 17:56:04 -03:00
context . AmbitosGeograficos . Add ( new AmbitoGeografico { Nombre = $"{provincia.Nombre} - Depto. {i}" , NivelId = 30 , DistritoId = provincia . DistritoId } ) ;
2025-09-17 11:31:17 -03:00
}
}
await context . SaveChangesAsync ( ) ;
2025-10-01 10:03:01 -03:00
logger . LogInformation ( "--> Datos maestros para Elección Nacional (Ámbitos, Categorías) verificados/creados." ) ;
2025-09-17 11:31:17 -03:00
2025-10-01 10:03:01 -03:00
// PASO B: GENERAR DATOS TRANSACCIONALES (Votos, Recuentos, etc.)
2025-09-17 11:31:17 -03:00
var todosLosPartidos = await context . AgrupacionesPoliticas . Take ( 5 ) . ToListAsync ( ) ;
2025-09-28 19:04:09 -03:00
if ( ! todosLosPartidos . Any ( ) )
{
2025-10-01 10:03:01 -03:00
logger . LogWarning ( "--> No hay partidos en la BD, no se pueden generar votos de ejemplo." ) ;
return ; // Salir si no hay partidos para evitar errores
2025-09-17 11:31:17 -03:00
}
2025-10-01 10:03:01 -03:00
// (La lógica interna de generación de votos y recuentos que ya tenías y funcionaba)
// ... (el código de generación de `nuevosResultados` y `nuevosEstados` va aquí, sin cambios)
2025-09-17 11:31:17 -03:00
var nuevosResultados = new List < ResultadoVoto > ( ) ;
2025-09-22 17:56:04 -03:00
var nuevosEstados = new List < EstadoRecuentoGeneral > ( ) ;
2025-09-17 11:31:17 -03:00
var rand = new Random ( ) ;
2025-10-01 10:03:01 -03:00
var provinciasQueRenuevanSenadores = new HashSet < string > { "01" , "06" , "08" , "15" , "16" , "17" , "22" , "23" } ;
var categoriaDiputadosNac = await context . CategoriasElectorales . FindAsync ( 2 ) ;
var categoriaSenadoresNac = await context . CategoriasElectorales . FindAsync ( 1 ) ;
2025-09-28 19:04:09 -03:00
2025-10-01 10:03:01 -03:00
long totalVotosNacionalDip = 0 , totalVotosNacionalSen = 0 ;
int totalMesasNacionalDip = 0 , totalMesasNacionalSen = 0 ;
int totalMesasEscrutadasNacionalDip = 0 , totalMesasEscrutadasNacionalSen = 0 ;
2025-09-22 17:56:04 -03:00
2025-09-17 11:31:17 -03:00
foreach ( var provincia in provinciasEnDb )
{
2025-09-22 17:56:04 -03:00
var municipiosDeProvincia = await context . AmbitosGeograficos . AsNoTracking ( ) . Where ( a = > a . NivelId = = 30 & & a . DistritoId = = provincia . DistritoId ) . ToListAsync ( ) ;
2025-09-17 11:31:17 -03:00
if ( ! municipiosDeProvincia . Any ( ) ) continue ;
2025-09-28 19:04:09 -03:00
2025-10-01 10:03:01 -03:00
var categoriasParaProcesar = new List < CategoriaElectoral > { categoriaDiputadosNac ! } ;
if ( provinciasQueRenuevanSenadores . Contains ( provincia . DistritoId ! ) )
categoriasParaProcesar . Add ( categoriaSenadoresNac ! ) ;
2025-09-28 19:04:09 -03:00
2025-10-01 10:03:01 -03:00
foreach ( var categoria in categoriasParaProcesar )
2025-09-17 11:31:17 -03:00
{
2025-10-01 10:03:01 -03:00
long totalVotosProvinciaCategoria = 0 ;
int partidoIndex = rand . Next ( todosLosPartidos . Count ) ;
foreach ( var municipio in municipiosDeProvincia )
{
var partidoGanador = todosLosPartidos [ partidoIndex + + % todosLosPartidos . Count ] ;
var votosGanador = rand . Next ( 25000 , 70000 ) ;
nuevosResultados . Add ( new ResultadoVoto { EleccionId = eleccionNacionalId , AmbitoGeograficoId = municipio . Id , CategoriaId = categoria . Id , AgrupacionPoliticaId = partidoGanador . Id , CantidadVotos = votosGanador } ) ;
totalVotosProvinciaCategoria + = votosGanador ;
var otrosPartidos = todosLosPartidos . Where ( p = > p . Id ! = partidoGanador . Id ) . OrderBy ( p = > rand . Next ( ) ) . Take ( rand . Next ( 3 , todosLosPartidos . Count ) ) ;
foreach ( var competidor in otrosPartidos )
{
var votosCompetidor = rand . Next ( 1000 , 24000 ) ;
nuevosResultados . Add ( new ResultadoVoto { EleccionId = eleccionNacionalId , AmbitoGeograficoId = municipio . Id , CategoriaId = categoria . Id , AgrupacionPoliticaId = competidor . Id , CantidadVotos = votosCompetidor } ) ;
totalVotosProvinciaCategoria + = votosCompetidor ;
}
}
var mesasEsperadasProvincia = municipiosDeProvincia . Count * rand . Next ( 15 , 30 ) ;
var mesasTotalizadasProvincia = ( int ) ( mesasEsperadasProvincia * ( rand . Next ( 75 , 99 ) / 100.0 ) ) ;
var cantidadElectoresProvincia = mesasEsperadasProvincia * 350 ;
var participacionProvincia = ( decimal ) ( rand . Next ( 65 , 85 ) / 100.0 ) ;
nuevosEstados . Add ( new EstadoRecuentoGeneral
{
EleccionId = eleccionNacionalId ,
AmbitoGeograficoId = provincia . Id ,
CategoriaId = categoria . Id ,
FechaTotalizacion = DateTime . UtcNow ,
MesasEsperadas = mesasEsperadasProvincia ,
MesasTotalizadas = mesasTotalizadasProvincia ,
MesasTotalizadasPorcentaje = mesasEsperadasProvincia > 0 ? ( decimal ) mesasTotalizadasProvincia * 100 / mesasEsperadasProvincia : 0 ,
CantidadElectores = cantidadElectoresProvincia ,
CantidadVotantes = ( int ) ( cantidadElectoresProvincia * participacionProvincia ) ,
ParticipacionPorcentaje = participacionProvincia * 100
} ) ;
if ( categoriaDiputadosNac ! = null & & categoria . Id = = categoriaDiputadosNac . Id )
{
totalVotosNacionalDip + = totalVotosProvinciaCategoria ; totalMesasNacionalDip + = mesasEsperadasProvincia ; totalMesasEscrutadasNacionalDip + = mesasTotalizadasProvincia ;
}
else
2025-09-28 19:04:09 -03:00
{
2025-10-01 10:03:01 -03:00
totalVotosNacionalSen + = totalVotosProvinciaCategoria ; totalMesasNacionalSen + = mesasEsperadasProvincia ; totalMesasEscrutadasNacionalSen + = mesasTotalizadasProvincia ;
2025-09-17 11:31:17 -03:00
}
}
2025-10-01 10:03:01 -03:00
}
var ambitoNacional = await context . AmbitosGeograficos . AsNoTracking ( ) . FirstOrDefaultAsync ( a = > a . NivelId = = 0 ) ;
if ( ambitoNacional ! = null & & categoriaDiputadosNac ! = null & & categoriaSenadoresNac ! = null )
{
var participacionNacionalDip = ( decimal ) ( rand . Next ( 70 , 88 ) / 100.0 ) ;
2025-09-28 19:04:09 -03:00
nuevosEstados . Add ( new EstadoRecuentoGeneral
{
EleccionId = eleccionNacionalId ,
2025-10-01 10:03:01 -03:00
AmbitoGeograficoId = ambitoNacional . Id ,
2025-09-28 19:04:09 -03:00
CategoriaId = categoriaDiputadosNac . Id ,
2025-09-22 17:56:04 -03:00
FechaTotalizacion = DateTime . UtcNow ,
2025-10-01 10:03:01 -03:00
MesasEsperadas = totalMesasNacionalDip ,
MesasTotalizadas = totalMesasEscrutadasNacionalDip ,
MesasTotalizadasPorcentaje = totalMesasNacionalDip > 0 ? ( decimal ) totalMesasEscrutadasNacionalDip * 100 / totalMesasNacionalDip : 0 ,
CantidadElectores = totalMesasNacionalDip * 350 ,
CantidadVotantes = ( int ) ( ( totalMesasNacionalDip * 350 ) * participacionNacionalDip ) ,
ParticipacionPorcentaje = participacionNacionalDip * 100
} ) ;
var participacionNacionalSen = ( decimal ) ( rand . Next ( 70 , 88 ) / 100.0 ) ;
nuevosEstados . Add ( new EstadoRecuentoGeneral
{
EleccionId = eleccionNacionalId ,
AmbitoGeograficoId = ambitoNacional . Id ,
CategoriaId = categoriaSenadoresNac . Id ,
FechaTotalizacion = DateTime . UtcNow ,
MesasEsperadas = totalMesasNacionalSen ,
MesasTotalizadas = totalMesasEscrutadasNacionalSen ,
MesasTotalizadasPorcentaje = totalMesasNacionalSen > 0 ? ( decimal ) totalMesasEscrutadasNacionalSen * 100 / totalMesasNacionalSen : 0 ,
CantidadElectores = totalMesasNacionalSen * 350 ,
CantidadVotantes = ( int ) ( ( totalMesasNacionalSen * 350 ) * participacionNacionalSen ) ,
ParticipacionPorcentaje = participacionNacionalSen * 100
2025-09-22 17:56:04 -03:00
} ) ;
2025-09-17 11:31:17 -03:00
}
2025-10-01 10:03:01 -03:00
else
2025-09-28 19:04:09 -03:00
{
2025-10-01 10:03:01 -03:00
logger . LogWarning ( "--> No se encontró el ámbito nacional (NivelId == 0) o las categorías electorales nacionales. No se agregaron estados nacionales." ) ;
2025-09-22 17:56:04 -03:00
}
2025-09-28 19:04:09 -03:00
if ( nuevosResultados . Any ( ) )
{
2025-09-17 11:31:17 -03:00
await context . ResultadosVotos . AddRangeAsync ( nuevosResultados ) ;
2025-09-22 17:56:04 -03:00
await context . EstadosRecuentosGenerales . AddRangeAsync ( nuevosEstados ) ;
2025-09-17 11:31:17 -03:00
await context . SaveChangesAsync ( ) ;
2025-09-22 17:56:04 -03:00
logger . LogInformation ( "--> Se generaron {Votos} registros de votos y {Estados} de estados de recuento." , nuevosResultados . Count , nuevosEstados . Count ) ;
2025-09-28 19:04:09 -03:00
}
2025-09-17 11:31:17 -03:00
2025-10-01 10:03:01 -03:00
// PASO C: GENERAR BANCAS PREVIAS Y PROYECCIONES
if ( ! await context . BancasPrevias . AnyAsync ( b = > b . EleccionId = = eleccionNacionalId ) )
2025-09-28 19:04:09 -03:00
{
2025-10-01 10:03:01 -03:00
var bancasPrevias = new List < BancaPrevia > {
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Diputados , AgrupacionPoliticaId = todosLosPartidos [ 0 ] . Id , Cantidad = 40 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Diputados , AgrupacionPoliticaId = todosLosPartidos [ 1 ] . Id , Cantidad = 35 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Diputados , AgrupacionPoliticaId = todosLosPartidos [ 2 ] . Id , Cantidad = 30 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Diputados , AgrupacionPoliticaId = todosLosPartidos [ 3 ] . Id , Cantidad = 15 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Diputados , AgrupacionPoliticaId = todosLosPartidos [ 4 ] . Id , Cantidad = 10 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Senadores , AgrupacionPoliticaId = todosLosPartidos [ 0 ] . Id , Cantidad = 18 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Senadores , AgrupacionPoliticaId = todosLosPartidos [ 1 ] . Id , Cantidad = 15 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Senadores , AgrupacionPoliticaId = todosLosPartidos [ 2 ] . Id , Cantidad = 8 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Senadores , AgrupacionPoliticaId = todosLosPartidos [ 3 ] . Id , Cantidad = 4 } ,
new ( ) { EleccionId = eleccionNacionalId , Camara = TipoCamara . Senadores , AgrupacionPoliticaId = todosLosPartidos [ 4 ] . Id , Cantidad = 3 } ,
2025-09-28 19:04:09 -03:00
} ;
await context . BancasPrevias . AddRangeAsync ( bancasPrevias ) ;
await context . SaveChangesAsync ( ) ;
2025-10-01 10:03:01 -03:00
logger . LogInformation ( "--> Seeded Bancas Previas para la Elección Nacional." ) ;
2025-09-28 19:04:09 -03:00
}
}
}
2025-10-01 10:03:01 -03:00
// --- FIN DEL BLOQUE DE SEEDERS UNIFICADO ---
2025-09-28 19:04:09 -03:00
2025-08-29 09:54:22 -03:00
// Configurar el pipeline de peticiones HTTP.
2025-08-15 17:31:51 -03:00
// Añadimos el logging de peticiones de Serilog aquí.
app . UseSerilogRequestLogging ( ) ;
2025-08-14 13:12:16 -03:00
2025-08-14 12:37:57 -03:00
if ( app . Environment . IsDevelopment ( ) )
{
2025-08-14 13:12:16 -03:00
app . UseSwagger ( ) ;
app . UseSwaggerUI ( ) ;
2025-08-14 12:37:57 -03:00
}
2025-08-29 09:54:22 -03:00
// 1. Redirección a HTTPS (si se usa)
//app.UseHttpsRedirection();
// 2. Middleware de Enrutamiento. ¡CLAVE!
// Determina a qué endpoint irá la petición.
app . UseRouting ( ) ;
// 3. Middleware de CORS.
// Ahora se ejecuta sabiendo a qué endpoint se dirige la petición.
app . UseCors ( MyAllowSpecificOrigins ) ;
// 4. Middleware de Autenticación.
// Identifica quién es el usuario a partir del token.
app . UseAuthentication ( ) ;
// 5. Middleware de Autorización.
// Verifica si el usuario identificado tiene permiso para acceder al endpoint.
2025-08-14 13:12:16 -03:00
app . UseAuthorization ( ) ;
2025-08-29 09:54:22 -03:00
// 6. Mapea los controladores a los endpoints que el enrutador descubrió.
2025-08-14 13:12:16 -03:00
app . MapControllers ( ) ;
2025-08-15 17:31:51 -03:00
// 5. Ejecutar la aplicación.
2025-08-14 13:12:16 -03:00
app . Run ( ) ;