Feat Workers Prioridades y Nivel Serilog
This commit is contained in:
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -7,6 +7,7 @@ import { ConfiguracionGeneral } from './ConfiguracionGeneral';
|
||||
import { BancasManager } from './BancasManager';
|
||||
import { LogoOverridesManager } from './LogoOverridesManager';
|
||||
import { CandidatoOverridesManager } from './CandidatoOverridesManager';
|
||||
import { WorkerManager } from './WorkerManager';
|
||||
|
||||
export const DashboardPage = () => {
|
||||
const { logout } = useAuth();
|
||||
@@ -35,6 +36,8 @@ export const DashboardPage = () => {
|
||||
</div>
|
||||
<ConfiguracionGeneral />
|
||||
<BancasManager />
|
||||
<hr style={{ margin: '2rem 0' }}/>
|
||||
<WorkerManager />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
140
Elecciones-Web/frontend-admin/src/components/WorkerManager.tsx
Normal file
140
Elecciones-Web/frontend-admin/src/components/WorkerManager.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { getConfiguracion, updateConfiguracion, updateLoggingLevel } from '../services/apiService';
|
||||
import type { ConfiguracionResponse } from '../services/apiService';
|
||||
|
||||
// --- Componente de Switch reutilizable para la UI ---
|
||||
const Switch = ({ label, isChecked, onChange }: { label: string, isChecked: boolean, onChange: (checked: boolean) => void }) => (
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}>
|
||||
<input type="checkbox" checked={isChecked} onChange={e => onChange(e.target.checked)} />
|
||||
{label}
|
||||
</label>
|
||||
);
|
||||
|
||||
export const WorkerManager = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Estados locales para manejar los valores de la UI
|
||||
const [resultadosActivado, setResultadosActivado] = useState(true);
|
||||
const [bajasActivado, setBajasActivado] = useState(true);
|
||||
const [prioridad, setPrioridad] = useState('Resultados');
|
||||
const [loggingLevel, setLoggingLevel] = useState('Information');
|
||||
|
||||
// Query para obtener la configuración actual desde la API
|
||||
const { data: configData, isLoading } = useQuery<ConfiguracionResponse>({
|
||||
queryKey: ['configuracion'],
|
||||
queryFn: getConfiguracion,
|
||||
});
|
||||
|
||||
// useEffect para sincronizar el estado local con los datos de la API una vez cargados
|
||||
useEffect(() => {
|
||||
if (configData) {
|
||||
setResultadosActivado(configData.Worker_Resultados_Activado === 'true');
|
||||
setBajasActivado(configData.Worker_Bajas_Activado === 'true');
|
||||
setPrioridad(configData.Worker_Prioridad || 'Resultados');
|
||||
setLoggingLevel(configData.Logging_Level || 'Information');
|
||||
}
|
||||
}, [configData]);
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// Creamos dos promesas separadas, una para la config general y otra para el logging
|
||||
const configPromise = updateConfiguracion({
|
||||
...configData,
|
||||
'Worker_Resultados_Activado': resultadosActivado.toString(),
|
||||
'Worker_Bajas_Activado': bajasActivado.toString(),
|
||||
'Worker_Prioridad': prioridad,
|
||||
'Logging_Level': loggingLevel,
|
||||
});
|
||||
|
||||
// La llamada al endpoint de logging-level es la que cambia el nivel EN VIVO.
|
||||
const loggingPromise = updateLoggingLevel({ level: loggingLevel });
|
||||
|
||||
// Ejecutamos ambas en paralelo
|
||||
await Promise.all([configPromise, loggingPromise]);
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ['configuracion'] });
|
||||
alert('Configuración de Workers y Logging guardada.');
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error al guardar la configuración:", error);
|
||||
alert('Error al guardar la configuración.');
|
||||
}
|
||||
};
|
||||
|
||||
const isPrioridadDisabled = !resultadosActivado || !bajasActivado;
|
||||
|
||||
if (isLoading) {
|
||||
return <div className="admin-module"><h3>Gestión de Workers</h3><p>Cargando configuración...</p></div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="admin-module">
|
||||
<h3>Gestión de Workers</h3>
|
||||
<p>Controla el comportamiento de los procesos de captura de datos.</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', borderTop: '1px solid #eee', paddingTop: '1rem' }}>
|
||||
|
||||
{/* --- Switches On/Off --- */}
|
||||
<div style={{ display: 'flex', alignSelf: 'center', gap: '2rem' }}>
|
||||
<Switch
|
||||
label="Activar Worker de Resultados"
|
||||
isChecked={resultadosActivado}
|
||||
onChange={setResultadosActivado}
|
||||
/>
|
||||
<Switch
|
||||
label="Activar Worker de Bancas/Telegramas"
|
||||
isChecked={bajasActivado}
|
||||
onChange={setBajasActivado}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* --- Contenedor para Selectores --- */}
|
||||
<div style={{ display: 'flex', gap: '2rem', alignSelf:'center', alignItems: 'flex-start' }}>
|
||||
{/* --- Selector de Prioridad --- */}
|
||||
<div>
|
||||
<label htmlFor="prioridad-select" style={{ display: 'block', marginBottom: '0.5rem', fontWeight: 500 }}>
|
||||
Prioridad (si ambos están activos)
|
||||
</label>
|
||||
<select
|
||||
id="prioridad-select"
|
||||
value={prioridad}
|
||||
onChange={e => setPrioridad(e.target.value)}
|
||||
disabled={isPrioridadDisabled}
|
||||
style={{ padding: '0.5rem', minWidth: '200px' }}
|
||||
>
|
||||
<option value="Resultados">Resultados (Noche Electoral)</option>
|
||||
<option value="Telegramas">Telegramas (Post-Escrutinio)</option>
|
||||
</select>
|
||||
{isPrioridadDisabled && <small style={{ display: 'block', marginTop: '0.5rem', color: '#666' }}>Activar ambos workers para elegir prioridad.</small>}
|
||||
</div>
|
||||
|
||||
{/* --- NUEVO: Selector de Nivel de Logging --- */}
|
||||
<div>
|
||||
<label htmlFor="logging-select" style={{ display: 'block', marginBottom: '0.5rem', fontWeight: 500 }}>
|
||||
Nivel de Logging (En vivo)
|
||||
</label>
|
||||
<select
|
||||
id="logging-select"
|
||||
value={loggingLevel}
|
||||
onChange={e => setLoggingLevel(e.target.value)}
|
||||
style={{ padding: '0.5rem', minWidth: '200px' }}
|
||||
>
|
||||
<option value="Verbose">Verbose (Máximo detalle)</option>
|
||||
<option value="Debug">Debug</option>
|
||||
<option value="Information">Information (Normal)</option>
|
||||
<option value="Warning">Warning (Advertencias)</option>
|
||||
<option value="Error">Error</option>
|
||||
<option value="Fatal">Fatal (Críticos)</option>
|
||||
</select>
|
||||
<small style={{ display: 'block', marginTop: '0.5rem', color: '#666' }}>Cambia el nivel de log en tiempo real.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* --- Botón de Guardar --- */}
|
||||
<div style={{ marginTop: '1rem' }}>
|
||||
<button onClick={handleSave}>Guardar Toda la Configuración</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -121,10 +121,10 @@ export const updateLogos = async (data: LogoAgrupacionCategoria[]): Promise<void
|
||||
};
|
||||
|
||||
export const getMunicipiosForAdmin = async (): Promise<MunicipioSimple[]> => {
|
||||
// Ahora usa adminApiClient, que apunta a /api/admin/
|
||||
// La URL final será /api/admin/catalogos/municipios
|
||||
const response = await adminApiClient.get('/catalogos/municipios');
|
||||
return response.data;
|
||||
// Ahora usa adminApiClient, que apunta a /api/admin/
|
||||
// La URL final será /api/admin/catalogos/municipios
|
||||
const response = await adminApiClient.get('/catalogos/municipios');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// 6. Overrides de Candidatos
|
||||
@@ -135,4 +135,14 @@ export const getCandidatos = async (): Promise<CandidatoOverride[]> => {
|
||||
|
||||
export const updateCandidatos = async (data: CandidatoOverride[]): Promise<void> => {
|
||||
await adminApiClient.put('/candidatos', data);
|
||||
};
|
||||
|
||||
// 7. Gestión de Logging
|
||||
export interface UpdateLoggingLevelData {
|
||||
level: string;
|
||||
}
|
||||
|
||||
export const updateLoggingLevel = async (data: UpdateLoggingLevelData): Promise<void> => {
|
||||
// Este endpoint es específico, no es parte de la configuración general
|
||||
await adminApiClient.put(`/logging-level`, data);
|
||||
};
|
||||
@@ -6,6 +6,7 @@ using Elecciones.Core.DTOs.ApiRequests;
|
||||
using Elecciones.Database.Entities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Elecciones.Core.Enums;
|
||||
using Elecciones.Infrastructure.Services;
|
||||
|
||||
namespace Elecciones.Api.Controllers;
|
||||
|
||||
@@ -16,11 +17,13 @@ public class AdminController : ControllerBase
|
||||
{
|
||||
private readonly EleccionesDbContext _dbContext;
|
||||
private readonly ILogger<AdminController> _logger;
|
||||
private readonly LoggingSwitchService _loggingSwitchService;
|
||||
|
||||
public AdminController(EleccionesDbContext dbContext, ILogger<AdminController> logger)
|
||||
public AdminController(EleccionesDbContext dbContext, ILogger<AdminController> logger, LoggingSwitchService loggingSwitchService)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_logger = logger;
|
||||
_loggingSwitchService = loggingSwitchService;
|
||||
}
|
||||
|
||||
// Endpoint para obtener todas las agrupaciones para el panel de admin
|
||||
@@ -297,4 +300,41 @@ public class AdminController : ControllerBase
|
||||
await _dbContext.SaveChangesAsync();
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actualiza el nivel mínimo de logging en tiempo real y guarda la configuración en la BD.
|
||||
/// </summary>
|
||||
/// <param name="request">Un objeto que contiene el nuevo nivel de logging.</param>
|
||||
[HttpPut("logging-level")]
|
||||
public async Task<IActionResult> UpdateLoggingLevel([FromBody] UpdateLoggingLevelRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Level))
|
||||
{
|
||||
return BadRequest("El nivel de logging no puede estar vacío.");
|
||||
}
|
||||
|
||||
// 1. Intentamos actualizar el interruptor de Serilog en memoria.
|
||||
bool success = _loggingSwitchService.SetLoggingLevel(request.Level);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
return BadRequest($"El nivel de logging '{request.Level}' no es válido. Los valores posibles son: Verbose, Debug, Information, Warning, Error, Fatal.");
|
||||
}
|
||||
|
||||
// 2. Si el cambio fue exitoso, guardamos el nuevo valor en la base de datos.
|
||||
var config = await _dbContext.Configuraciones.FindAsync("Logging_Level");
|
||||
if (config == null)
|
||||
{
|
||||
_dbContext.Configuraciones.Add(new Configuracion { Clave = "Logging_Level", Valor = request.Level });
|
||||
}
|
||||
else
|
||||
{
|
||||
config.Valor = request.Level;
|
||||
}
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
_logger.LogWarning("El nivel de logging ha sido cambiado a: {Level}", request.Level);
|
||||
return Ok(new { message = $"Nivel de logging actualizado a '{request.Level}'." });
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
//Elecciones.Api/Program.cs
|
||||
using Elecciones.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Serilog;
|
||||
@@ -13,13 +14,24 @@ using Microsoft.AspNetCore.HttpOverrides;
|
||||
// Esta es la estructura estándar y recomendada.
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 1. Configurar Serilog. Esta es la forma correcta de integrarlo.
|
||||
builder.Host.UseSerilog((context, services, configuration) => configuration
|
||||
.ReadFrom.Configuration(context.Configuration)
|
||||
.ReadFrom.Services(services)
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File("logs/api-.log", rollingInterval: RollingInterval.Day));
|
||||
// 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"
|
||||
});
|
||||
|
||||
// 2. Añadir servicios al contenedor.
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||
@@ -83,6 +95,34 @@ builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
// 3. Construir la aplicación.
|
||||
var app = builder.Build();
|
||||
|
||||
// --- 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}");
|
||||
}
|
||||
}
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
|
||||
// Seeder para el usuario admin
|
||||
@@ -150,19 +190,29 @@ using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
var context = services.GetRequiredService<EleccionesDbContext>();
|
||||
if (!context.Configuraciones.Any(c => c.Clave == "MostrarOcupantes"))
|
||||
|
||||
// Lista de configuraciones por defecto a asegurar
|
||||
var defaultConfiguraciones = new Dictionary<string, string>
|
||||
{
|
||||
context.Configuraciones.Add(new Configuracion { Clave = "MostrarOcupantes", Valor = "true" });
|
||||
context.SaveChanges();
|
||||
Console.WriteLine("--> Seeded default configuration 'MostrarOcupantes'.");
|
||||
}
|
||||
if (!context.Configuraciones.Any(c => c.Clave == "TickerResultadosCantidad"))
|
||||
{ "MostrarOcupantes", "true" },
|
||||
{ "TickerResultadosCantidad", "3" },
|
||||
{ "ConcejalesResultadosCantidad", "5" },
|
||||
{ "Worker_Resultados_Activado", "false" },
|
||||
{ "Worker_Bajas_Activado", "false" },
|
||||
{ "Worker_Prioridad", "Resultados" },
|
||||
{ "Logging_Level", "Information" }
|
||||
};
|
||||
|
||||
foreach (var config in defaultConfiguraciones)
|
||||
{
|
||||
context.Configuraciones.Add(new Configuracion { Clave = "TickerResultadosCantidad", Valor = "3" });
|
||||
context.Configuraciones.Add(new Configuracion { Clave = "ConcejalesResultadosCantidad", Valor = "5" });
|
||||
context.SaveChanges();
|
||||
Console.WriteLine("--> Seeded default configuration 'TickerResultadosCantidad'.");
|
||||
if (!context.Configuraciones.Any(c => c.Clave == config.Key))
|
||||
{
|
||||
context.Configuraciones.Add(new Configuracion { Clave = config.Key, Valor = config.Value });
|
||||
}
|
||||
}
|
||||
|
||||
context.SaveChanges();
|
||||
Console.WriteLine("--> Seeded default configurations.");
|
||||
}
|
||||
|
||||
// Configurar el pipeline de peticiones HTTP.
|
||||
|
||||
@@ -859,17 +859,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog/4.2.0": {
|
||||
"Serilog/4.3.0": {
|
||||
"runtime": {
|
||||
"lib/net9.0/Serilog.dll": {
|
||||
"assemblyVersion": "4.2.0.0",
|
||||
"fileVersion": "4.2.0.0"
|
||||
"assemblyVersion": "4.3.0.0",
|
||||
"fileVersion": "4.3.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog.AspNetCore/9.0.0": {
|
||||
"dependencies": {
|
||||
"Serilog": "4.2.0",
|
||||
"Serilog": "4.3.0",
|
||||
"Serilog.Extensions.Hosting": "9.0.0",
|
||||
"Serilog.Formatting.Compact": "3.0.0",
|
||||
"Serilog.Settings.Configuration": "9.0.0",
|
||||
@@ -889,7 +889,7 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Hosting.Abstractions": "9.0.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
|
||||
"Serilog": "4.2.0",
|
||||
"Serilog": "4.3.0",
|
||||
"Serilog.Extensions.Logging": "9.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
@@ -902,7 +902,7 @@
|
||||
"Serilog.Extensions.Logging/9.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "9.0.8",
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Serilog.Extensions.Logging.dll": {
|
||||
@@ -913,7 +913,7 @@
|
||||
},
|
||||
"Serilog.Formatting.Compact/3.0.0": {
|
||||
"dependencies": {
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Serilog.Formatting.Compact.dll": {
|
||||
@@ -926,7 +926,7 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
|
||||
"Microsoft.Extensions.DependencyModel": "9.0.8",
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Serilog.Settings.Configuration.dll": {
|
||||
@@ -937,7 +937,7 @@
|
||||
},
|
||||
"Serilog.Sinks.Console/6.0.0": {
|
||||
"dependencies": {
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Serilog.Sinks.Console.dll": {
|
||||
@@ -948,7 +948,7 @@
|
||||
},
|
||||
"Serilog.Sinks.Debug/3.0.0": {
|
||||
"dependencies": {
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Serilog.Sinks.Debug.dll": {
|
||||
@@ -959,7 +959,7 @@
|
||||
},
|
||||
"Serilog.Sinks.File/7.0.0": {
|
||||
"dependencies": {
|
||||
"Serilog": "4.2.0"
|
||||
"Serilog": "4.3.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Serilog.Sinks.File.dll": {
|
||||
@@ -1294,8 +1294,10 @@
|
||||
"Elecciones.Infrastructure/1.0.0": {
|
||||
"dependencies": {
|
||||
"Elecciones.Core": "1.0.0",
|
||||
"Elecciones.Database": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Http": "9.0.8",
|
||||
"Serilog": "4.3.0",
|
||||
"System.Threading.RateLimiting": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
@@ -1691,12 +1693,12 @@
|
||||
"path": "mono.texttemplating/3.0.0",
|
||||
"hashPath": "mono.texttemplating.3.0.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog/4.2.0": {
|
||||
"Serilog/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA==",
|
||||
"path": "serilog/4.2.0",
|
||||
"hashPath": "serilog.4.2.0.nupkg.sha512"
|
||||
"sha512": "sha512-+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==",
|
||||
"path": "serilog/4.3.0",
|
||||
"hashPath": "serilog.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog.AspNetCore/9.0.0": {
|
||||
"type": "package",
|
||||
|
||||
@@ -14,7 +14,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f384a640f36be1289d652dc85e78ebdcef30968a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Kt4ImnGs0wklEJp/6NxrhrTvGLQxPfYUAB5LMWAnz10=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v1SBeIVg8rE3EddYwnvF/EsPYr2F5GAppt/Egvdtr/0="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","UucupTplk47jbYuQLLfpsVglReDmh1hUE6oD0OEv\u002BsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","nor5YuoHu4p8qlladsJ2COw4pycCja0XN1sckUrKV/w="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +1 @@
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Kt4ImnGs0wklEJp/6NxrhrTvGLQxPfYUAB5LMWAnz10=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v1SBeIVg8rE3EddYwnvF/EsPYr2F5GAppt/Egvdtr/0="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["TyIJk/eQMWjmB5LsDE\u002BZIJC9P9ciVxd7bnzRiTZsGt4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","/GfbpJthEWmsuz0uFx1QLHM7gyM1wLLeQgAIl4SzUD4=","i5\u002B5LcfxQD8meRAkQbVf4wMvjxSE4\u002BjCd2/FdPtMpms=","AvSkxVPIg0GjnB1RJ4hDNyo9p9GONrzDs8uVuixH\u002BOE=","IgT9pOgRnK37qfILj2QcjFoBZ180HMt\u002BScgje2iYOo4=","ezUlOMzNZmyKDIe1wwXqvX\u002BvhwfB992xNVts7r2zcTc=","y2BV4WpkQuLfqQhfOQBtmuzh940c3s4LAopGKfztfTE=","lHTUEsMkDu8nqXtfTwl7FRfgocyyc7RI5O/edTHN1\u002B0=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","UucupTplk47jbYuQLLfpsVglReDmh1hUE6oD0OEv\u002BsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","nor5YuoHu4p8qlladsJ2COw4pycCja0XN1sckUrKV/w="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -287,6 +287,9 @@
|
||||
"projectReferences": {
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj"
|
||||
},
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -315,6 +318,10 @@
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
},
|
||||
"Serilog": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
},
|
||||
"System.Threading.RateLimiting": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class UpdateLoggingLevelRequest
|
||||
{
|
||||
[Required]
|
||||
public string Level { get; set; } = null!;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f384a640f36be1289d652dc85e78ebdcef30968a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f384a640f36be1289d652dc85e78ebdcef30968a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Elecciones.Core\Elecciones.Core.csproj" />
|
||||
<ProjectReference Include="..\Elecciones.Database\Elecciones.Database.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
|
||||
<PackageReference Include="Serilog" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Elecciones.Infrastructure.Services;
|
||||
|
||||
public class LoggingSwitchService
|
||||
{
|
||||
// El interruptor de nivel de logging de Serilog.
|
||||
// Lo inicializamos en 'Information' por defecto.
|
||||
public LoggingLevelSwitch LevelSwitch { get; } = new(LogEventLevel.Information);
|
||||
|
||||
/// <summary>
|
||||
/// Cambia el nivel mínimo de logging dinámicamente.
|
||||
/// </summary>
|
||||
/// <param name="level">El nuevo nivel de logging como string (ej. "Information", "Warning", "Verbose").</param>
|
||||
/// <returns>True si el nivel se cambió con éxito, false si el string no es válido.</returns>
|
||||
public bool SetLoggingLevel(string level)
|
||||
{
|
||||
// Usamos Enum.TryParse para convertir el string a un valor del enum LogEventLevel.
|
||||
// El 'true' ignora mayúsculas/minúsculas.
|
||||
if (Enum.TryParse<LogEventLevel>(level, true, out var newLevel))
|
||||
{
|
||||
// Si la conversión es exitosa, actualizamos el interruptor.
|
||||
LevelSwitch.MinimumLevel = newLevel;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Si el string no corresponde a ningún nivel válido, no hacemos nada y devolvemos false.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Elecciones.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elecciones.Infrastructure.Services;
|
||||
|
||||
public class WorkerSettings
|
||||
{
|
||||
public bool ResultadosActivado { get; set; } = true;
|
||||
public bool BajasActivado { get; set; } = true;
|
||||
public string Prioridad { get; set; } = "Resultados";
|
||||
}
|
||||
|
||||
public class WorkerConfigService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly ILogger<WorkerConfigService> _logger;
|
||||
private WorkerSettings _cachedSettings = new();
|
||||
private DateTime _lastFetchTime = DateTime.MinValue;
|
||||
private readonly TimeSpan _cacheDuration = TimeSpan.FromSeconds(25); // Cachear por 25 segundos
|
||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||
|
||||
public WorkerConfigService(IServiceProvider serviceProvider, ILogger<WorkerConfigService> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<WorkerSettings> GetSettingsAsync()
|
||||
{
|
||||
// Si el caché es válido, lo devolvemos inmediatamente.
|
||||
if (DateTime.UtcNow < _lastFetchTime + _cacheDuration)
|
||||
{
|
||||
return _cachedSettings;
|
||||
}
|
||||
|
||||
// Si el caché ha expirado, intentamos obtener el control para actualizarlo.
|
||||
await _semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
// Volvemos a comprobar por si otra tarea ya actualizó el caché mientras esperábamos.
|
||||
if (DateTime.UtcNow < _lastFetchTime + _cacheDuration)
|
||||
{
|
||||
return _cachedSettings;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Caché de configuración del worker expirado. Actualizando desde la BD...");
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
var configMap = await dbContext.Configuraciones.AsNoTracking().ToDictionaryAsync(c => c.Clave, c => c.Valor);
|
||||
|
||||
_cachedSettings = new WorkerSettings
|
||||
{
|
||||
ResultadosActivado = configMap.GetValueOrDefault("Worker_Resultados_Activado", "true") == "true",
|
||||
BajasActivado = configMap.GetValueOrDefault("Worker_Bajas_Activado", "true") == "true",
|
||||
Prioridad = configMap.GetValueOrDefault("Worker_Prioridad", "Resultados") ?? "Resultados"
|
||||
};
|
||||
|
||||
_lastFetchTime = DateTime.UtcNow;
|
||||
_logger.LogInformation("Configuración del worker actualizada: Resultados={res}, Bajas={bajas}, Prioridad={prio}",
|
||||
_cachedSettings.ResultadosActivado, _cachedSettings.BajasActivado, _cachedSettings.Prioridad);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "No se pudo actualizar la configuración del worker desde la BD. Usando la última configuración cacheada.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
return _cachedSettings;
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,201 @@
|
||||
"Elecciones.Infrastructure/1.0.0": {
|
||||
"dependencies": {
|
||||
"Elecciones.Core": "1.0.0",
|
||||
"Elecciones.Database": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Http": "9.0.8",
|
||||
"Serilog": "4.3.0",
|
||||
"System.Threading.RateLimiting": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"Elecciones.Infrastructure.dll": {}
|
||||
}
|
||||
},
|
||||
"Azure.Core/1.38.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "1.1.1",
|
||||
"System.ClientModel": "1.0.0",
|
||||
"System.Diagnostics.DiagnosticSource": "6.0.1",
|
||||
"System.Memory.Data": "1.0.2",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Text.Encodings.Web": "6.0.0",
|
||||
"System.Text.Json": "9.0.8",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Azure.Core.dll": {
|
||||
"assemblyVersion": "1.38.0.0",
|
||||
"fileVersion": "1.3800.24.12602"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Azure.Identity/1.11.4": {
|
||||
"dependencies": {
|
||||
"Azure.Core": "1.38.0",
|
||||
"Microsoft.Identity.Client": "4.61.3",
|
||||
"Microsoft.Identity.Client.Extensions.Msal": "4.61.3",
|
||||
"System.Memory": "4.5.4",
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0",
|
||||
"System.Text.Json": "9.0.8",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Azure.Identity.dll": {
|
||||
"assemblyVersion": "1.11.4.0",
|
||||
"fileVersion": "1.1100.424.31005"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces/1.1.1": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "4.700.20.21406"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.CSharp/4.5.0": {},
|
||||
"Microsoft.Data.SqlClient/5.1.6": {
|
||||
"dependencies": {
|
||||
"Azure.Identity": "1.11.4",
|
||||
"Microsoft.Data.SqlClient.SNI.runtime": "5.1.1",
|
||||
"Microsoft.Identity.Client": "4.61.3",
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "6.35.0",
|
||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0",
|
||||
"Microsoft.SqlServer.Server": "1.0.0",
|
||||
"System.Configuration.ConfigurationManager": "6.0.1",
|
||||
"System.Diagnostics.DiagnosticSource": "6.0.1",
|
||||
"System.Runtime.Caching": "6.0.0",
|
||||
"System.Security.Cryptography.Cng": "5.0.0",
|
||||
"System.Security.Principal.Windows": "5.0.0",
|
||||
"System.Text.Encoding.CodePages": "6.0.0",
|
||||
"System.Text.Encodings.Web": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.Data.SqlClient.dll": {
|
||||
"assemblyVersion": "5.0.0.0",
|
||||
"fileVersion": "5.16.24240.5"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/unix/lib/net6.0/Microsoft.Data.SqlClient.dll": {
|
||||
"rid": "unix",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "5.0.0.0",
|
||||
"fileVersion": "5.16.24240.5"
|
||||
},
|
||||
"runtimes/win/lib/net6.0/Microsoft.Data.SqlClient.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "5.0.0.0",
|
||||
"fileVersion": "5.16.24240.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.SqlClient.SNI.runtime/5.1.1": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm/native/Microsoft.Data.SqlClient.SNI.dll": {
|
||||
"rid": "win-arm",
|
||||
"assetType": "native",
|
||||
"fileVersion": "5.1.1.0"
|
||||
},
|
||||
"runtimes/win-arm64/native/Microsoft.Data.SqlClient.SNI.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "5.1.1.0"
|
||||
},
|
||||
"runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll": {
|
||||
"rid": "win-x64",
|
||||
"assetType": "native",
|
||||
"fileVersion": "5.1.1.0"
|
||||
},
|
||||
"runtimes/win-x86/native/Microsoft.Data.SqlClient.SNI.dll": {
|
||||
"rid": "win-x86",
|
||||
"assetType": "native",
|
||||
"fileVersion": "5.1.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore.Abstractions": "9.0.8",
|
||||
"Microsoft.EntityFrameworkCore.Analyzers": "9.0.8",
|
||||
"Microsoft.Extensions.Caching.Memory": "9.0.8",
|
||||
"Microsoft.Extensions.Logging": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.EntityFrameworkCore.dll": {
|
||||
"assemblyVersion": "9.0.8.0",
|
||||
"fileVersion": "9.0.825.36802"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Abstractions/9.0.8": {
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {
|
||||
"assemblyVersion": "9.0.8.0",
|
||||
"fileVersion": "9.0.825.36802"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Analyzers/9.0.8": {},
|
||||
"Microsoft.EntityFrameworkCore.Relational/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore": "9.0.8",
|
||||
"Microsoft.Extensions.Caching.Memory": "9.0.8",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Logging": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll": {
|
||||
"assemblyVersion": "9.0.8.0",
|
||||
"fileVersion": "9.0.825.36802"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.SqlServer/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.Data.SqlClient": "5.1.6",
|
||||
"Microsoft.EntityFrameworkCore.Relational": "9.0.8",
|
||||
"Microsoft.Extensions.Caching.Memory": "9.0.8",
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Logging": "9.0.8",
|
||||
"System.Formats.Asn1": "9.0.8",
|
||||
"System.Text.Json": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.EntityFrameworkCore.SqlServer.dll": {
|
||||
"assemblyVersion": "9.0.8.0",
|
||||
"fileVersion": "9.0.825.36802"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.825.36511"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Caching.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
|
||||
"Microsoft.Extensions.Options": "9.0.8",
|
||||
"Microsoft.Extensions.Primitives": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net9.0/Microsoft.Extensions.Caching.Memory.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.825.36511"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/9.0.8": {
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
|
||||
@@ -170,6 +357,308 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Identity.Client/4.61.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "6.35.0",
|
||||
"System.Diagnostics.DiagnosticSource": "6.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.Identity.Client.dll": {
|
||||
"assemblyVersion": "4.61.3.0",
|
||||
"fileVersion": "4.61.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Identity.Client.Extensions.Msal/4.61.3": {
|
||||
"dependencies": {
|
||||
"Microsoft.Identity.Client": "4.61.3",
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.Identity.Client.Extensions.Msal.dll": {
|
||||
"assemblyVersion": "4.61.3.0",
|
||||
"fileVersion": "4.61.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/6.35.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.Abstractions.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Tokens": "6.35.0",
|
||||
"System.Text.Encoding": "4.3.0",
|
||||
"System.Text.Encodings.Web": "6.0.0",
|
||||
"System.Text.Json": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Abstractions": "6.35.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.Logging.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Logging": "6.35.0",
|
||||
"Microsoft.IdentityModel.Tokens": "6.35.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.Protocols.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.Protocols": "6.35.0",
|
||||
"System.IdentityModel.Tokens.Jwt": "6.35.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.CSharp": "4.5.0",
|
||||
"Microsoft.IdentityModel.Logging": "6.35.0",
|
||||
"System.Security.Cryptography.Cng": "5.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.IdentityModel.Tokens.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {},
|
||||
"Microsoft.NETCore.Targets/1.1.0": {},
|
||||
"Microsoft.SqlServer.Server/1.0.0": {
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/Microsoft.SqlServer.Server.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents/6.0.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/Microsoft.Win32.SystemEvents.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/win/lib/net6.0/Microsoft.Win32.SystemEvents.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog/4.3.0": {
|
||||
"runtime": {
|
||||
"lib/net9.0/Serilog.dll": {
|
||||
"assemblyVersion": "4.3.0.0",
|
||||
"fileVersion": "4.3.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.ClientModel/1.0.0": {
|
||||
"dependencies": {
|
||||
"System.Memory.Data": "1.0.2",
|
||||
"System.Text.Json": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.ClientModel.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.24.5302"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Configuration.ConfigurationManager/6.0.1": {
|
||||
"dependencies": {
|
||||
"System.Security.Cryptography.ProtectedData": "6.0.0",
|
||||
"System.Security.Permissions": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Configuration.ConfigurationManager.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.922.41905"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.DiagnosticSource/6.0.1": {
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common/6.0.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Drawing.Common.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/unix/lib/net6.0/System.Drawing.Common.dll": {
|
||||
"rid": "unix",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
},
|
||||
"runtimes/win/lib/net6.0/System.Drawing.Common.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Formats.Asn1/9.0.8": {
|
||||
"runtime": {
|
||||
"lib/net9.0/System.Formats.Asn1.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.825.36511"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/6.35.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.IdentityModel.JsonWebTokens": "6.35.0",
|
||||
"Microsoft.IdentityModel.Tokens": "6.35.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.IdentityModel.Tokens.Jwt.dll": {
|
||||
"assemblyVersion": "6.35.0.0",
|
||||
"fileVersion": "6.35.0.41201"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Memory/4.5.4": {},
|
||||
"System.Memory.Data/1.0.2": {
|
||||
"dependencies": {
|
||||
"System.Text.Encodings.Web": "6.0.0",
|
||||
"System.Text.Json": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/netstandard2.0/System.Memory.Data.dll": {
|
||||
"assemblyVersion": "1.0.2.0",
|
||||
"fileVersion": "1.0.221.20802"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors/4.5.0": {},
|
||||
"System.Runtime/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Runtime.Caching/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Configuration.ConfigurationManager": "6.0.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Runtime.Caching.dll": {
|
||||
"assemblyVersion": "4.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/win/lib/net6.0/System.Runtime.Caching.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "4.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
|
||||
"System.Security.AccessControl/6.0.0": {},
|
||||
"System.Security.Cryptography.Cng/5.0.0": {
|
||||
"dependencies": {
|
||||
"System.Formats.Asn1": "9.0.8"
|
||||
}
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData/6.0.0": {
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Security.Cryptography.ProtectedData.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/win/lib/net6.0/System.Security.Cryptography.ProtectedData.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Security.Permissions/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Security.AccessControl": "6.0.0",
|
||||
"System.Windows.Extensions": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Security.Permissions.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Security.Principal.Windows/5.0.0": {},
|
||||
"System.Text.Encoding/4.3.0": {
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0",
|
||||
"Microsoft.NETCore.Targets": "1.1.0",
|
||||
"System.Runtime": "4.3.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Encodings.Web/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Text.Json/9.0.8": {
|
||||
"runtime": {
|
||||
"lib/net9.0/System.Text.Json.dll": {
|
||||
"assemblyVersion": "9.0.0.0",
|
||||
"fileVersion": "9.0.825.36511"
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Threading.RateLimiting/9.0.8": {
|
||||
"runtime": {
|
||||
"lib/net9.0/System.Threading.RateLimiting.dll": {
|
||||
@@ -178,6 +667,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions/4.5.4": {},
|
||||
"System.Windows.Extensions/6.0.0": {
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "6.0.0"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net6.0/System.Windows.Extensions.dll": {
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
},
|
||||
"runtimeTargets": {
|
||||
"runtimes/win/lib/net6.0/System.Windows.Extensions.dll": {
|
||||
"rid": "win",
|
||||
"assetType": "runtime",
|
||||
"assemblyVersion": "6.0.0.0",
|
||||
"fileVersion": "6.0.21.52210"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Elecciones.Core/1.0.0": {
|
||||
"runtime": {
|
||||
"Elecciones.Core.dll": {
|
||||
@@ -185,6 +694,18 @@
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Elecciones.Database/1.0.0": {
|
||||
"dependencies": {
|
||||
"Elecciones.Core": "1.0.0",
|
||||
"Microsoft.EntityFrameworkCore.SqlServer": "9.0.8"
|
||||
},
|
||||
"runtime": {
|
||||
"Elecciones.Database.dll": {
|
||||
"assemblyVersion": "1.0.0.0",
|
||||
"fileVersion": "1.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -194,6 +715,97 @@
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Azure.Core/1.38.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-IuEgCoVA0ef7E4pQtpC3+TkPbzaoQfa77HlfJDmfuaJUCVJmn7fT0izamZiryW5sYUFKizsftIxMkXKbgIcPMQ==",
|
||||
"path": "azure.core/1.38.0",
|
||||
"hashPath": "azure.core.1.38.0.nupkg.sha512"
|
||||
},
|
||||
"Azure.Identity/1.11.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Sf4BoE6Q3jTgFkgBkx7qztYOFELBCo+wQgpYDwal/qJ1unBH73ywPztIJKXBXORRzAeNijsuxhk94h0TIMvfYg==",
|
||||
"path": "azure.identity/1.11.4",
|
||||
"hashPath": "azure.identity.1.11.4.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces/1.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-yuvf07qFWFqtK3P/MRkEKLhn5r2UbSpVueRziSqj0yJQIKFwG1pq9mOayK3zE5qZCTs0CbrwL9M6R8VwqyGy2w==",
|
||||
"path": "microsoft.bcl.asyncinterfaces/1.1.1",
|
||||
"hashPath": "microsoft.bcl.asyncinterfaces.1.1.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.CSharp/4.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==",
|
||||
"path": "microsoft.csharp/4.5.0",
|
||||
"hashPath": "microsoft.csharp.4.5.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.SqlClient/5.1.6": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-+pz7gIPh5ydsBcQvivt4R98PwJXer86fyQBBToIBLxZ5kuhW4N13Ijz87s9WpuPtF1vh4JesYCgpDPAOgkMhdg==",
|
||||
"path": "microsoft.data.sqlclient/5.1.6",
|
||||
"hashPath": "microsoft.data.sqlclient.5.1.6.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Data.SqlClient.SNI.runtime/5.1.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-wNGM5ZTQCa2blc9ikXQouybGiyMd6IHPVJvAlBEPtr6JepZEOYeDxGyprYvFVeOxlCXs7avridZQ0nYkHzQWCQ==",
|
||||
"path": "microsoft.data.sqlclient.sni.runtime/5.1.1",
|
||||
"hashPath": "microsoft.data.sqlclient.sni.runtime.5.1.1.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-bNGdPhN762+BIIO5MFYLjafRqkSS1MqLOc/erd55InvLnFxt9H3N5JNsuag1ZHyBor1VtD42U0CHpgqkWeAYgQ==",
|
||||
"path": "microsoft.entityframeworkcore/9.0.8",
|
||||
"hashPath": "microsoft.entityframeworkcore.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Abstractions/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-B2yfAIQRRAQ4zvvWqh+HudD+juV3YoLlpXnrog3tU0PM9AFpuq6xo0+mEglN1P43WgdcUiF+65CWBcZe35s15Q==",
|
||||
"path": "microsoft.entityframeworkcore.abstractions/9.0.8",
|
||||
"hashPath": "microsoft.entityframeworkcore.abstractions.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Analyzers/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-2EYStCXt4Hi9p3J3EYMQbItJDtASJd064Kcs8C8hj8Jt5srILrR9qlaL0Ryvk8NrWQoCQvIELsmiuqLEZMLvGA==",
|
||||
"path": "microsoft.entityframeworkcore.analyzers/9.0.8",
|
||||
"hashPath": "microsoft.entityframeworkcore.analyzers.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.Relational/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-OVhfyxiHxMvYpwQ8Jy3YZi4koy6TK5/Q7C1oq3z6db+HEGuu6x9L1BX5zDIdJxxlRePMyO4D8ORiXj/D7+MUqw==",
|
||||
"path": "microsoft.entityframeworkcore.relational/9.0.8",
|
||||
"hashPath": "microsoft.entityframeworkcore.relational.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.SqlServer/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-yNZJIdLQTTHj6FTv9+IUQwmQvOwvUanTBOG1ibeTaaB1zfTtOqrSFQnjMOkcKOgxu+ofsBEDcuctb/f5xj/Oog==",
|
||||
"path": "microsoft.entityframeworkcore.sqlserver/9.0.8",
|
||||
"hashPath": "microsoft.entityframeworkcore.sqlserver.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Abstractions/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-4h7bsVoKoiK+SlPM+euX/ayGnKZhl47pPCidLTiio9xyG+vgVVfcYxcYQgjm0SCrdSxjG0EGIAKF8EFr3G8Ifw==",
|
||||
"path": "microsoft.extensions.caching.abstractions/9.0.8",
|
||||
"hashPath": "microsoft.extensions.caching.abstractions.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Caching.Memory/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-grR+oPyj8HVn4DT8CFUUdSw2pZZKS13KjytFe4txpHQliGM1GEDotohmjgvyl3hm7RFB3FRqvbouEX3/1ewp5A==",
|
||||
"path": "microsoft.extensions.caching.memory/9.0.8",
|
||||
"hashPath": "microsoft.extensions.caching.memory.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Extensions.Configuration/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
@@ -285,6 +897,244 @@
|
||||
"path": "microsoft.extensions.primitives/9.0.8",
|
||||
"hashPath": "microsoft.extensions.primitives.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Identity.Client/4.61.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-naJo/Qm35Caaoxp5utcw+R8eU8ZtLz2ALh8S+gkekOYQ1oazfCQMWVT4NJ/FnHzdIJlm8dMz0oMpMGCabx5odA==",
|
||||
"path": "microsoft.identity.client/4.61.3",
|
||||
"hashPath": "microsoft.identity.client.4.61.3.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Identity.Client.Extensions.Msal/4.61.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-PWnJcznrSGr25MN8ajlc2XIDW4zCFu0U6FkpaNLEWLgd1NgFCp5uDY3mqLDgM8zCN8hqj8yo5wHYfLB2HjcdGw==",
|
||||
"path": "microsoft.identity.client.extensions.msal/4.61.3",
|
||||
"hashPath": "microsoft.identity.client.extensions.msal.4.61.3.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Abstractions/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-xuR8E4Rd96M41CnUSCiOJ2DBh+z+zQSmyrYHdYhD6K4fXBcQGVnRCFQ0efROUYpP+p0zC1BLKr0JRpVuujTZSg==",
|
||||
"path": "microsoft.identitymodel.abstractions/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.abstractions.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.JsonWebTokens/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-9wxai3hKgZUb4/NjdRKfQd0QJvtXKDlvmGMYACbEC8DFaicMFCFhQFZq9ZET1kJLwZahf2lfY5Gtcpsx8zYzbg==",
|
||||
"path": "microsoft.identitymodel.jsonwebtokens/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.jsonwebtokens.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Logging/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-jePrSfGAmqT81JDCNSY+fxVWoGuJKt9e6eJ+vT7+quVS55nWl//jGjUQn4eFtVKt4rt5dXaleZdHRB9J9AJZ7Q==",
|
||||
"path": "microsoft.identitymodel.logging/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.logging.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-BPQhlDzdFvv1PzaUxNSk+VEPwezlDEVADIKmyxubw7IiELK18uJ06RQ9QKKkds30XI+gDu9n8j24XQ8w7fjWcg==",
|
||||
"path": "microsoft.identitymodel.protocols/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.protocols.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-LMtVqnECCCdSmyFoCOxIE5tXQqkOLrvGrL7OxHg41DIm1bpWtaCdGyVcTAfOQpJXvzND9zUKIN/lhngPkYR8vg==",
|
||||
"path": "microsoft.identitymodel.protocols.openidconnect/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.protocols.openidconnect.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.IdentityModel.Tokens/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-RN7lvp7s3Boucg1NaNAbqDbxtlLj5Qeb+4uSS1TeK5FSBVM40P4DKaTKChT43sHyKfh7V0zkrMph6DdHvyA4bg==",
|
||||
"path": "microsoft.identitymodel.tokens/6.35.0",
|
||||
"hashPath": "microsoft.identitymodel.tokens.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.NETCore.Platforms/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==",
|
||||
"path": "microsoft.netcore.platforms/1.1.0",
|
||||
"hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.NETCore.Targets/1.1.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==",
|
||||
"path": "microsoft.netcore.targets/1.1.0",
|
||||
"hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.SqlServer.Server/1.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==",
|
||||
"path": "microsoft.sqlserver.server/1.0.0",
|
||||
"hashPath": "microsoft.sqlserver.server.1.0.0.nupkg.sha512"
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A==",
|
||||
"path": "microsoft.win32.systemevents/6.0.0",
|
||||
"hashPath": "microsoft.win32.systemevents.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"Serilog/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-+cDryFR0GRhsGOnZSKwaDzRRl4MupvJ42FhCE4zhQRVanX0Jpg6WuCBk59OVhVDPmab1bB+nRykAnykYELA9qQ==",
|
||||
"path": "serilog/4.3.0",
|
||||
"hashPath": "serilog.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.ClientModel/1.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-I3CVkvxeqFYjIVEP59DnjbeoGNfo/+SZrCLpRz2v/g0gpCHaEMPtWSY0s9k/7jR1rAsLNg2z2u1JRB76tPjnIw==",
|
||||
"path": "system.clientmodel/1.0.0",
|
||||
"hashPath": "system.clientmodel.1.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Configuration.ConfigurationManager/6.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==",
|
||||
"path": "system.configuration.configurationmanager/6.0.1",
|
||||
"hashPath": "system.configuration.configurationmanager.6.0.1.nupkg.sha512"
|
||||
},
|
||||
"System.Diagnostics.DiagnosticSource/6.0.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==",
|
||||
"path": "system.diagnostics.diagnosticsource/6.0.1",
|
||||
"hashPath": "system.diagnostics.diagnosticsource.6.0.1.nupkg.sha512"
|
||||
},
|
||||
"System.Drawing.Common/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
|
||||
"path": "system.drawing.common/6.0.0",
|
||||
"hashPath": "system.drawing.common.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Formats.Asn1/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-gGL0gt2nAArsF2oOMFzClll6QN2FhtooTxEQ+K26uer4lrhahnYIo/qOn5HUSfjHlM91646L5/7dYIMJ86fHkQ==",
|
||||
"path": "system.formats.asn1/9.0.8",
|
||||
"hashPath": "system.formats.asn1.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"System.IdentityModel.Tokens.Jwt/6.35.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-yxGIQd3BFK7F6S62/7RdZk3C/mfwyVxvh6ngd1VYMBmbJ1YZZA9+Ku6suylVtso0FjI0wbElpJ0d27CdsyLpBQ==",
|
||||
"path": "system.identitymodel.tokens.jwt/6.35.0",
|
||||
"hashPath": "system.identitymodel.tokens.jwt.6.35.0.nupkg.sha512"
|
||||
},
|
||||
"System.Memory/4.5.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==",
|
||||
"path": "system.memory/4.5.4",
|
||||
"hashPath": "system.memory.4.5.4.nupkg.sha512"
|
||||
},
|
||||
"System.Memory.Data/1.0.2": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==",
|
||||
"path": "system.memory.data/1.0.2",
|
||||
"hashPath": "system.memory.data.1.0.2.nupkg.sha512"
|
||||
},
|
||||
"System.Numerics.Vectors/4.5.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==",
|
||||
"path": "system.numerics.vectors/4.5.0",
|
||||
"hashPath": "system.numerics.vectors.4.5.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
|
||||
"path": "system.runtime/4.3.0",
|
||||
"hashPath": "system.runtime.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.Caching/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-E0e03kUp5X2k+UAoVl6efmI7uU7JRBWi5EIdlQ7cr0NpBGjHG4fWII35PgsBY9T4fJQ8E4QPsL0rKksU9gcL5A==",
|
||||
"path": "system.runtime.caching/6.0.0",
|
||||
"hashPath": "system.runtime.caching.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
|
||||
"path": "system.runtime.compilerservices.unsafe/6.0.0",
|
||||
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.AccessControl/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==",
|
||||
"path": "system.security.accesscontrol/6.0.0",
|
||||
"hashPath": "system.security.accesscontrol.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.Cryptography.Cng/5.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-jIMXsKn94T9JY7PvPq/tMfqa6GAaHpElRDpmG+SuL+D3+sTw2M8VhnibKnN8Tq+4JqbPJ/f+BwtLeDMEnzAvRg==",
|
||||
"path": "system.security.cryptography.cng/5.0.0",
|
||||
"hashPath": "system.security.cryptography.cng.5.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.Cryptography.ProtectedData/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==",
|
||||
"path": "system.security.cryptography.protecteddata/6.0.0",
|
||||
"hashPath": "system.security.cryptography.protecteddata.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.Permissions/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
|
||||
"path": "system.security.permissions/6.0.0",
|
||||
"hashPath": "system.security.permissions.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Security.Principal.Windows/5.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
|
||||
"path": "system.security.principal.windows/5.0.0",
|
||||
"hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding/4.3.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
|
||||
"path": "system.text.encoding/4.3.0",
|
||||
"hashPath": "system.text.encoding.4.3.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encoding.CodePages/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
|
||||
"path": "system.text.encoding.codepages/6.0.0",
|
||||
"hashPath": "system.text.encoding.codepages.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Encodings.Web/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
|
||||
"path": "system.text.encodings.web/6.0.0",
|
||||
"hashPath": "system.text.encodings.web.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"System.Text.Json/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-mIQir9jBqk0V7X0Nw5hzPJZC8DuGdf+2DS3jAVsr6rq5+/VyH5rza0XGcONJUWBrZ+G6BCwNyjWYd9lncBu48A==",
|
||||
"path": "system.text.json/9.0.8",
|
||||
"hashPath": "system.text.json.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"System.Threading.RateLimiting/9.0.8": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
@@ -292,10 +1142,29 @@
|
||||
"path": "system.threading.ratelimiting/9.0.8",
|
||||
"hashPath": "system.threading.ratelimiting.9.0.8.nupkg.sha512"
|
||||
},
|
||||
"System.Threading.Tasks.Extensions/4.5.4": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"path": "system.threading.tasks.extensions/4.5.4",
|
||||
"hashPath": "system.threading.tasks.extensions.4.5.4.nupkg.sha512"
|
||||
},
|
||||
"System.Windows.Extensions/6.0.0": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
|
||||
"path": "system.windows.extensions/6.0.0",
|
||||
"hashPath": "system.windows.extensions.6.0.0.nupkg.sha512"
|
||||
},
|
||||
"Elecciones.Core/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Elecciones.Database/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+d78a02a0ebc4c70ea01e48821db963110e7ce280")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f384a640f36be1289d652dc85e78ebdcef30968a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -13,3 +13,5 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0
|
||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\refint\Elecciones.Infrastructure.dll
|
||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\Elecciones.Infrastructure.pdb
|
||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\ref\Elecciones.Infrastructure.dll
|
||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Debug\net9.0\Elecciones.Database.dll
|
||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Debug\net9.0\Elecciones.Database.pdb
|
||||
|
||||
@@ -69,14 +69,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj": {
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj",
|
||||
"projectName": "Elecciones.Infrastructure",
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj",
|
||||
"projectUniqueName": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj",
|
||||
"projectName": "Elecciones.Database",
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj",
|
||||
"packagesPath": "C:\\Users\\dmolinari\\.nuget\\packages\\",
|
||||
"outputPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\obj\\",
|
||||
"outputPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"D:\\Microsoft\\VisualStudio\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
@@ -115,6 +115,90 @@
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.EntityFrameworkCore.Design": {
|
||||
"include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive",
|
||||
"suppressParent": "All",
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
},
|
||||
"Microsoft.EntityFrameworkCore.SqlServer": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.300/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj",
|
||||
"projectName": "Elecciones.Infrastructure",
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\Elecciones.Infrastructure.csproj",
|
||||
"packagesPath": "C:\\Users\\dmolinari\\.nuget\\packages\\",
|
||||
"outputPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Infrastructure\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"D:\\Microsoft\\VisualStudio\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\dmolinari\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj"
|
||||
},
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
@@ -127,6 +211,10 @@
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
},
|
||||
"Serilog": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
},
|
||||
"System.Threading.RateLimiting": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
|
||||
@@ -13,4 +13,7 @@
|
||||
<SourceRoot Include="C:\Users\dmolinari\.nuget\packages\" />
|
||||
<SourceRoot Include="D:\Microsoft\VisualStudio\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore\9.0.8\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore\9.0.8\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props')" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<Import Project="$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\9.0.8\buildTransitive\net8.0\System.Text.Json.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)serilog\4.3.0\build\Serilog.targets" Condition="Exists('$(NuGetPackageRoot)serilog\4.3.0\build\Serilog.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
|
||||
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//Elecciones.Worker/CriticalDataWorker.cs
|
||||
using Elecciones.Database;
|
||||
using Elecciones.Database.Entities;
|
||||
using Elecciones.Infrastructure.Services;
|
||||
@@ -12,17 +13,20 @@ public class CriticalDataWorker : BackgroundService
|
||||
private readonly SharedTokenService _tokenService;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IElectoralApiService _apiService;
|
||||
private readonly WorkerConfigService _configService;
|
||||
|
||||
public CriticalDataWorker(
|
||||
ILogger<CriticalDataWorker> logger,
|
||||
SharedTokenService tokenService,
|
||||
IServiceProvider serviceProvider,
|
||||
IElectoralApiService apiService)
|
||||
IElectoralApiService apiService,
|
||||
WorkerConfigService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_tokenService = tokenService;
|
||||
_serviceProvider = serviceProvider;
|
||||
_apiService = apiService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
@@ -50,9 +54,25 @@ public class CriticalDataWorker : BackgroundService
|
||||
continue;
|
||||
}
|
||||
|
||||
await SondearResultadosMunicipalesAsync(authToken, stoppingToken);
|
||||
await SondearResumenProvincialAsync(authToken, stoppingToken);
|
||||
await SondearEstadoRecuentoGeneralAsync(authToken, stoppingToken);
|
||||
var settings = await _configService.GetSettingsAsync();
|
||||
|
||||
if (settings.Prioridad == "Resultados" && settings.ResultadosActivado)
|
||||
{
|
||||
_logger.LogInformation("Ejecutando tareas de Resultados en alta prioridad.");
|
||||
await SondearResultadosMunicipalesAsync(authToken, stoppingToken);
|
||||
await SondearResumenProvincialAsync(authToken, stoppingToken);
|
||||
await SondearEstadoRecuentoGeneralAsync(authToken, stoppingToken);
|
||||
}
|
||||
else if (settings.Prioridad == "Telegramas" && settings.BajasActivado)
|
||||
{
|
||||
_logger.LogInformation("Ejecutando tareas de Baja Prioridad en alta prioridad.");
|
||||
await SondearProyeccionBancasAsync(authToken, stoppingToken);
|
||||
await SondearNuevosTelegramasAsync(authToken, stoppingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Worker de alta prioridad inactivo según la configuración.");
|
||||
}
|
||||
|
||||
var cicloFin = DateTime.UtcNow;
|
||||
var duracionCiclo = cicloFin - cicloInicio;
|
||||
@@ -69,6 +89,252 @@ public class CriticalDataWorker : BackgroundService
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral.
|
||||
/// Esta versión es completamente robusta: maneja respuestas de API vacías o con fechas mal formadas,
|
||||
/// guarda la CategoriaId y usa una transacción atómica para la escritura en base de datos.
|
||||
/// </summary>
|
||||
/// <param name="authToken">El token de autenticación válido para la sesión.</param>
|
||||
/// <param name="stoppingToken">El token de cancelación para detener la operación.</param>
|
||||
private async Task SondearProyeccionBancasAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
var categoriasDeBancas = await dbContext.CategoriasElectorales
|
||||
.AsNoTracking()
|
||||
.Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS"))
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
var provincia = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
||||
|
||||
var seccionesElectorales = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.Where(a => a.NivelId == 20 && a.DistritoId != null && a.SeccionProvincialId != null)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (!categoriasDeBancas.Any() || provincia == null)
|
||||
{
|
||||
_logger.LogWarning("No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo de bancas.");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales...", seccionesElectorales.Count);
|
||||
|
||||
var todasLasProyecciones = new List<ProyeccionBanca>();
|
||||
bool hasReceivedAnyNewData = false;
|
||||
|
||||
// Bucle para el nivel Provincial
|
||||
foreach (var categoria in categoriasDeBancas)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) break;
|
||||
var repartoBancasDto = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, categoria.Id);
|
||||
|
||||
if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas)
|
||||
{
|
||||
hasReceivedAnyNewData = true;
|
||||
|
||||
// --- SEGURIDAD: Usar TryParse para la fecha ---
|
||||
DateTime fechaTotalizacion;
|
||||
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
|
||||
{
|
||||
// Si la fecha es inválida (nula, vacía, mal formada), lo registramos y usamos la hora actual como respaldo.
|
||||
_logger.LogWarning("No se pudo parsear FechaTotalizacion ('{dateString}') para bancas provinciales. Usando la hora actual.", repartoBancasDto.FechaTotalizacion);
|
||||
fechaTotalizacion = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
fechaTotalizacion = parsedDate.ToUniversalTime();
|
||||
}
|
||||
|
||||
foreach (var banca in bancas)
|
||||
{
|
||||
todasLasProyecciones.Add(new ProyeccionBanca
|
||||
{
|
||||
AmbitoGeograficoId = provincia.Id,
|
||||
AgrupacionPoliticaId = banca.IdAgrupacion,
|
||||
NroBancas = banca.NroBancas,
|
||||
CategoriaId = categoria.Id,
|
||||
FechaTotalizacion = fechaTotalizacion
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bucle para el nivel de Sección Electoral
|
||||
foreach (var seccion in seccionesElectorales)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) break;
|
||||
foreach (var categoria in categoriasDeBancas)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) break;
|
||||
var repartoBancasDto = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionProvincialId!, categoria.Id);
|
||||
|
||||
if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas)
|
||||
{
|
||||
hasReceivedAnyNewData = true;
|
||||
|
||||
// --- APLICAMOS LA MISMA SEGURIDAD AQUÍ ---
|
||||
DateTime fechaTotalizacion;
|
||||
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
|
||||
{
|
||||
_logger.LogWarning("No se pudo parsear FechaTotalizacion ('{dateString}') para bancas de sección. Usando la hora actual.", repartoBancasDto.FechaTotalizacion);
|
||||
fechaTotalizacion = DateTime.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
fechaTotalizacion = parsedDate.ToUniversalTime();
|
||||
}
|
||||
|
||||
foreach (var banca in bancas)
|
||||
{
|
||||
todasLasProyecciones.Add(new ProyeccionBanca
|
||||
{
|
||||
AmbitoGeograficoId = seccion.Id,
|
||||
AgrupacionPoliticaId = banca.IdAgrupacion,
|
||||
NroBancas = banca.NroBancas,
|
||||
CategoriaId = categoria.Id,
|
||||
FechaTotalizacion = fechaTotalizacion
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasReceivedAnyNewData)
|
||||
{
|
||||
_logger.LogInformation("Se recibieron datos válidos de bancas. Procediendo a actualizar la base de datos...");
|
||||
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
|
||||
|
||||
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);
|
||||
await dbContext.ProyeccionesBancas.AddRangeAsync(todasLasProyecciones, stoppingToken);
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
await transaction.CommitAsync(stoppingToken);
|
||||
|
||||
_logger.LogInformation("La tabla de proyecciones ha sido actualizada con {count} registros.", todasLasProyecciones.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Sondeo de Bancas completado. No se encontraron datos nuevos de proyección, la tabla no fue modificada.");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("Sondeo de bancas cancelado.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Bancas.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Busca y descarga nuevos telegramas de forma masiva y concurrente.
|
||||
/// Este método crea una lista de todas las combinaciones de Partido/Categoría,
|
||||
/// las consulta a la API con un grado de paralelismo controlado, y cada tarea concurrente
|
||||
/// maneja su propia lógica de descarga y guardado en la base de datos.
|
||||
/// </summary>
|
||||
/// <param name="authToken">El token de autenticación válido para la sesión.</param>
|
||||
/// <param name="stoppingToken">El token de cancelación para detener la operación.</param>
|
||||
private async Task SondearNuevosTelegramasAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("--- Iniciando sondeo de Nuevos Telegramas (modo de bajo perfil) ---");
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
var partidos = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
var categorias = await dbContext.CategoriasElectorales
|
||||
.AsNoTracking()
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (!partidos.Any() || !categorias.Any()) return;
|
||||
|
||||
foreach (var partido in partidos)
|
||||
{
|
||||
foreach (var categoria in categorias)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) return;
|
||||
|
||||
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, partido.DistritoId!, partido.SeccionId!, categoria.Id);
|
||||
|
||||
if (listaTelegramasApi is { Count: > 0 })
|
||||
{
|
||||
using var innerScope = _serviceProvider.CreateScope();
|
||||
var innerDbContext = innerScope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
var idsYaEnDb = await innerDbContext.Telegramas
|
||||
.Where(t => listaTelegramasApi.Contains(t.Id))
|
||||
.Select(t => t.Id)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
var nuevosTelegramasIds = listaTelegramasApi.Except(idsYaEnDb).ToList();
|
||||
|
||||
if (nuevosTelegramasIds.Any())
|
||||
{
|
||||
_logger.LogInformation("Se encontraron {count} telegramas nuevos en '{partido}' para '{cat}'. Descargando...", nuevosTelegramasIds.Count, partido.Nombre, categoria.Nombre);
|
||||
|
||||
foreach (var mesaId in nuevosTelegramasIds)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) return;
|
||||
|
||||
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
|
||||
if (telegramaFile != null)
|
||||
{
|
||||
// 1. Buscamos el AmbitoGeografico específico de la MESA que estamos procesando.
|
||||
var ambitoMesa = await innerDbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.MesaId == mesaId, stoppingToken);
|
||||
|
||||
// 2. Solo guardamos el telegrama si encontramos su ámbito de mesa correspondiente.
|
||||
if (ambitoMesa != null)
|
||||
{
|
||||
var nuevoTelegrama = new Telegrama
|
||||
{
|
||||
Id = telegramaFile.NombreArchivo,
|
||||
// 3. Usamos el ID del ÁMBITO DE LA MESA, no el del municipio.
|
||||
AmbitoGeograficoId = ambitoMesa.Id,
|
||||
ContenidoBase64 = telegramaFile.Imagen,
|
||||
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
|
||||
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
|
||||
};
|
||||
await innerDbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("No se encontró un ámbito geográfico para la mesa con MesaId {MesaId}. El telegrama no será guardado.", mesaId);
|
||||
}
|
||||
}
|
||||
await Task.Delay(250, stoppingToken);
|
||||
}
|
||||
await innerDbContext.SaveChangesAsync(stoppingToken);
|
||||
}
|
||||
}
|
||||
await Task.Delay(100, stoppingToken);
|
||||
}
|
||||
}
|
||||
_logger.LogInformation("Sondeo de Telegramas completado.");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("Sondeo de telegramas cancelado.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//Elecciones.Worker/LowPriorityDataWorker.cs
|
||||
using Elecciones.Database;
|
||||
using Elecciones.Database.Entities;
|
||||
using Elecciones.Infrastructure.Services;
|
||||
@@ -11,6 +12,7 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
private readonly SharedTokenService _tokenService;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IElectoralApiService _apiService;
|
||||
private readonly WorkerConfigService _configService;
|
||||
|
||||
// Una variable para rastrear la tarea de telegramas, si está en ejecución.
|
||||
private Task? _telegramasTask;
|
||||
@@ -19,12 +21,14 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
ILogger<LowPriorityDataWorker> logger,
|
||||
SharedTokenService tokenService,
|
||||
IServiceProvider serviceProvider,
|
||||
IElectoralApiService apiService)
|
||||
IElectoralApiService apiService,
|
||||
WorkerConfigService configService)
|
||||
{
|
||||
_logger = logger;
|
||||
_tokenService = tokenService;
|
||||
_serviceProvider = serviceProvider;
|
||||
_apiService = apiService;
|
||||
_configService = configService;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
@@ -46,30 +50,25 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
continue;
|
||||
}
|
||||
|
||||
// --- LÓGICA DE EJECUCIÓN INDEPENDIENTE ---
|
||||
var settings = await _configService.GetSettingsAsync();
|
||||
|
||||
// 1. TAREA DE BANCAS: Siempre se ejecuta y se espera. Es rápida.
|
||||
_logger.LogInformation("Iniciando sondeo de Bancas...");
|
||||
await SondearProyeccionBancasAsync(authToken, stoppingToken);
|
||||
_logger.LogInformation("Sondeo de Bancas completado.");
|
||||
|
||||
// 2. TAREA DE TELEGRAMAS: "Dispara y Olvida" de forma segura.
|
||||
// Comprobamos si la tarea anterior de telegramas ya ha terminado.
|
||||
if (_telegramasTask == null || _telegramasTask.IsCompleted)
|
||||
if (settings.Prioridad == "Telegramas" && settings.ResultadosActivado)
|
||||
{
|
||||
_logger.LogInformation("Iniciando sondeo de Telegramas en segundo plano...");
|
||||
// Lanzamos la tarea de telegramas pero NO la esperamos con 'await'.
|
||||
// Guardamos una referencia a la tarea en nuestra variable de estado.
|
||||
_telegramasTask = SondearNuevosTelegramasAsync(authToken, stoppingToken);
|
||||
_logger.LogInformation("Ejecutando tareas de Resultados en baja prioridad.");
|
||||
await SondearResultadosMunicipalesAsync(authToken, stoppingToken);
|
||||
await SondearResumenProvincialAsync(authToken, stoppingToken);
|
||||
await SondearEstadoRecuentoGeneralAsync(authToken, stoppingToken);
|
||||
}
|
||||
else if (settings.Prioridad == "Resultados" && settings.BajasActivado)
|
||||
{
|
||||
_logger.LogInformation("Ejecutando tareas de Baja Prioridad en baja prioridad.");
|
||||
await SondearProyeccionBancasAsync(authToken, stoppingToken);
|
||||
await SondearNuevosTelegramasAsync(authToken, stoppingToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si la descarga anterior todavía está en curso, nos saltamos este sondeo
|
||||
// para no acumular tareas y sobrecargar el sistema.
|
||||
_logger.LogInformation("El sondeo de telegramas anterior sigue en ejecución. Se omitirá en este ciclo.");
|
||||
_logger.LogInformation("Worker de baja prioridad inactivo según la configuración.");
|
||||
}
|
||||
|
||||
_logger.LogInformation("--- Ciclo de Datos de Baja Prioridad completado. Esperando 5 minutos. ---");
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
|
||||
@@ -81,6 +80,337 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
var municipiosASondear = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
var todasLasCategorias = await dbContext.CategoriasElectorales
|
||||
.AsNoTracking()
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (!municipiosASondear.Any() || !todasLasCategorias.Any())
|
||||
{
|
||||
_logger.LogWarning("No se encontraron Partidos (NivelId 30) o Categorías para sondear resultados.");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Iniciando sondeo de resultados para {m} municipios y {c} categorías...", municipiosASondear.Count, todasLasCategorias.Count);
|
||||
|
||||
foreach (var municipio in municipiosASondear)
|
||||
{
|
||||
if (stoppingToken.IsCancellationRequested) break;
|
||||
|
||||
var tareasCategoria = todasLasCategorias.Select(async categoria =>
|
||||
{
|
||||
var resultados = await _apiService.GetResultadosAsync(authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoria.Id);
|
||||
|
||||
if (resultados != null)
|
||||
{
|
||||
using var innerScope = _serviceProvider.CreateScope();
|
||||
var innerDbContext = innerScope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
// --- LLAMADA CORRECTA ---
|
||||
await GuardarResultadosDeAmbitoAsync(innerDbContext, municipio.Id, categoria.Id, resultados, stoppingToken);
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(tareasCategoria);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados municipales.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GuardarResultadosDeAmbitoAsync(
|
||||
EleccionesDbContext dbContext, int ambitoId, int categoriaId,
|
||||
Elecciones.Core.DTOs.ResultadosDto resultadosDto, CancellationToken stoppingToken)
|
||||
{
|
||||
var estadoRecuento = await dbContext.EstadosRecuentos.FindAsync(new object[] { ambitoId, categoriaId }, stoppingToken);
|
||||
|
||||
if (estadoRecuento == null)
|
||||
{
|
||||
estadoRecuento = new EstadoRecuento { AmbitoGeograficoId = ambitoId, CategoriaId = categoriaId };
|
||||
dbContext.EstadosRecuentos.Add(estadoRecuento);
|
||||
}
|
||||
|
||||
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultadosDto.FechaTotalizacion).ToUniversalTime();
|
||||
estadoRecuento.MesasEsperadas = resultadosDto.EstadoRecuento.MesasEsperadas;
|
||||
estadoRecuento.MesasTotalizadas = resultadosDto.EstadoRecuento.MesasTotalizadas;
|
||||
estadoRecuento.MesasTotalizadasPorcentaje = resultadosDto.EstadoRecuento.MesasTotalizadasPorcentaje;
|
||||
estadoRecuento.CantidadElectores = resultadosDto.EstadoRecuento.CantidadElectores;
|
||||
estadoRecuento.CantidadVotantes = resultadosDto.EstadoRecuento.CantidadVotantes;
|
||||
estadoRecuento.ParticipacionPorcentaje = resultadosDto.EstadoRecuento.ParticipacionPorcentaje;
|
||||
|
||||
if (resultadosDto.ValoresTotalizadosOtros != null)
|
||||
{
|
||||
estadoRecuento.VotosEnBlanco = resultadosDto.ValoresTotalizadosOtros.VotosEnBlanco;
|
||||
estadoRecuento.VotosEnBlancoPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosEnBlancoPorcentaje;
|
||||
estadoRecuento.VotosNulos = resultadosDto.ValoresTotalizadosOtros.VotosNulos;
|
||||
estadoRecuento.VotosNulosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosNulosPorcentaje;
|
||||
estadoRecuento.VotosRecurridos = resultadosDto.ValoresTotalizadosOtros.VotosRecurridos;
|
||||
estadoRecuento.VotosRecurridosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosRecurridosPorcentaje;
|
||||
}
|
||||
|
||||
foreach (var votoPositivoDto in resultadosDto.ValoresTotalizadosPositivos)
|
||||
{
|
||||
var resultadoVoto = await dbContext.ResultadosVotos.FirstOrDefaultAsync(
|
||||
rv => rv.AmbitoGeograficoId == ambitoId &&
|
||||
rv.CategoriaId == categoriaId &&
|
||||
rv.AgrupacionPoliticaId == votoPositivoDto.IdAgrupacion,
|
||||
stoppingToken
|
||||
);
|
||||
|
||||
if (resultadoVoto == null)
|
||||
{
|
||||
resultadoVoto = new ResultadoVoto
|
||||
{
|
||||
AmbitoGeograficoId = ambitoId,
|
||||
CategoriaId = categoriaId,
|
||||
AgrupacionPoliticaId = votoPositivoDto.IdAgrupacion
|
||||
};
|
||||
dbContext.ResultadosVotos.Add(resultadoVoto);
|
||||
}
|
||||
resultadoVoto.CantidadVotos = votoPositivoDto.Votos;
|
||||
resultadoVoto.PorcentajeVotos = votoPositivoDto.VotosPorcentaje;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
_logger.LogError(ex, "DbUpdateException al guardar resultados para AmbitoId {ambitoId} y CategoriaId {categoriaId}", ambitoId, categoriaId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial.
|
||||
/// Esta versión actualizada guarda tanto los votos por agrupación (en ResumenesVotos)
|
||||
/// como el estado general del recuento, incluyendo la fecha de totalización (en EstadosRecuentosGenerales),
|
||||
/// asegurando que toda la operación sea atómica mediante una transacción de base de datos.
|
||||
/// </summary>
|
||||
/// <param name="authToken">El token de autenticación válido para la sesión.</param>
|
||||
/// <param name="stoppingToken">El token de cancelación para detener la operación.</param>
|
||||
private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Creamos un scope de DbContext para esta operación.
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
// Obtenemos el registro de la Provincia (NivelId 10).
|
||||
var provincia = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
||||
|
||||
// Si no encontramos el ámbito de la provincia, no podemos continuar.
|
||||
if (provincia == null)
|
||||
{
|
||||
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) para el sondeo de resumen.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Llamamos a la API para obtener el resumen de datos provincial.
|
||||
var resumenDto = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!);
|
||||
|
||||
// Solo procedemos si la API devolvió una respuesta válida y no nula.
|
||||
if (resumenDto != null)
|
||||
{
|
||||
// Iniciamos una transacción explícita. Esto garantiza que todas las operaciones de base de datos
|
||||
// dentro de este bloque (el DELETE, los INSERTs y los UPDATEs) se completen con éxito,
|
||||
// o si algo falla, se reviertan todas, manteniendo la consistencia de los datos.
|
||||
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
|
||||
|
||||
// --- 1. ACTUALIZAR LA TABLA 'ResumenesVotos' ---
|
||||
|
||||
// Verificamos si la respuesta contiene una lista de votos positivos.
|
||||
if (resumenDto.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
|
||||
{
|
||||
// Estrategia "Borrar y Reemplazar": vaciamos la tabla antes de insertar los nuevos datos.
|
||||
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
|
||||
|
||||
// Añadimos cada nuevo registro de voto al DbContext.
|
||||
foreach (var voto in nuevosVotos)
|
||||
{
|
||||
dbContext.ResumenesVotos.Add(new ResumenVoto
|
||||
{
|
||||
AmbitoGeograficoId = provincia.Id,
|
||||
AgrupacionPoliticaId = voto.IdAgrupacion,
|
||||
Votos = voto.Votos,
|
||||
VotosPorcentaje = voto.VotosPorcentaje
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// --- 2. ACTUALIZAR LA TABLA 'EstadosRecuentosGenerales' ---
|
||||
|
||||
// El endpoint de Resumen no especifica una categoría, por lo que aplicamos sus datos de estado de recuento
|
||||
// a todas las categorías que tenemos en nuestra base de datos.
|
||||
var todasLasCategorias = await dbContext.CategoriasElectorales.AsNoTracking().ToListAsync(stoppingToken);
|
||||
foreach (var categoria in todasLasCategorias)
|
||||
{
|
||||
// Buscamos el registro existente usando la clave primaria compuesta.
|
||||
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(new object[] { provincia.Id, categoria.Id }, stoppingToken);
|
||||
|
||||
// Si no existe, lo creamos.
|
||||
if (registroDb == null)
|
||||
{
|
||||
registroDb = new EstadoRecuentoGeneral { AmbitoGeograficoId = provincia.Id, CategoriaId = categoria.Id };
|
||||
dbContext.EstadosRecuentosGenerales.Add(registroDb);
|
||||
}
|
||||
|
||||
// Parseamos la fecha de forma segura para evitar errores con cadenas vacías o nulas.
|
||||
if (DateTime.TryParse(resumenDto.FechaTotalizacion, out var parsedDate))
|
||||
{
|
||||
registroDb.FechaTotalizacion = parsedDate.ToUniversalTime();
|
||||
}
|
||||
|
||||
// Mapeamos el resto de los datos del estado del recuento.
|
||||
registroDb.MesasEsperadas = resumenDto.EstadoRecuento.MesasEsperadas;
|
||||
registroDb.MesasTotalizadas = resumenDto.EstadoRecuento.MesasTotalizadas;
|
||||
registroDb.MesasTotalizadasPorcentaje = resumenDto.EstadoRecuento.MesasTotalizadasPorcentaje;
|
||||
registroDb.CantidadElectores = resumenDto.EstadoRecuento.CantidadElectores;
|
||||
registroDb.CantidadVotantes = resumenDto.EstadoRecuento.CantidadVotantes;
|
||||
registroDb.ParticipacionPorcentaje = resumenDto.EstadoRecuento.ParticipacionPorcentaje;
|
||||
}
|
||||
|
||||
// 3. CONFIRMAR Y GUARDAR
|
||||
// Guardamos todos los cambios preparados (DELETEs, INSERTs, UPDATEs) en la base de datos.
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
// Confirmamos la transacción para hacer los cambios permanentes.
|
||||
await transaction.CommitAsync(stoppingToken);
|
||||
|
||||
_logger.LogInformation("Sondeo de Resumen Provincial completado. Las tablas han sido actualizadas.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Si la API no devolvió datos (ej. devuelve null), no hacemos nada en la BD.
|
||||
_logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos.");
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
_logger.LogInformation("Sondeo de resumen provincial cancelado.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Capturamos cualquier otro error inesperado para que el worker no se detenga.
|
||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resumen Provincial.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene y actualiza el estado general del recuento a nivel provincial para CADA categoría electoral.
|
||||
/// Esta versión es robusta: consulta dinámicamente las categorías, usa la clave primaria compuesta
|
||||
/// de la base de datos y guarda todos los cambios en una única transacción al final.
|
||||
/// </summary>
|
||||
/// <param name="authToken">El token de autenticación válido para la sesión.</param>
|
||||
/// <param name="stoppingToken">El token de cancelación para detener la operación.</param>
|
||||
private async Task SondearEstadoRecuentoGeneralAsync(string authToken, CancellationToken stoppingToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
// PASO 1: Crear un "scope" para obtener una instancia fresca de DbContext.
|
||||
// Esto es una práctica recomendada para servicios de larga duración para evitar problemas de concurrencia.
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||
|
||||
// PASO 2: Obtener el ámbito geográfico de la Provincia.
|
||||
// Necesitamos este objeto para obtener su 'DistritoId' ("02"), que es requerido por la API.
|
||||
var provincia = await dbContext.AmbitosGeograficos
|
||||
.AsNoTracking() // Optimización: Solo necesitamos leer datos, no modificarlos.
|
||||
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
||||
|
||||
// Comprobación de seguridad: Si la sincronización inicial falló y no tenemos el registro de la provincia,
|
||||
// no podemos continuar. Registramos una advertencia y salimos del método.
|
||||
if (provincia == null)
|
||||
{
|
||||
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) en la BD. Omitiendo sondeo de estado general.");
|
||||
return;
|
||||
}
|
||||
|
||||
// PASO 3: Obtener todas las categorías electorales disponibles desde nuestra base de datos.
|
||||
// Esto hace que el método sea dinámico y no dependa de IDs fijos en el código.
|
||||
var categoriasParaSondear = await dbContext.CategoriasElectorales
|
||||
.AsNoTracking()
|
||||
.ToListAsync(stoppingToken);
|
||||
|
||||
if (!categoriasParaSondear.Any())
|
||||
{
|
||||
_logger.LogWarning("No hay categorías en la BD para sondear el estado general del recuento.");
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Iniciando sondeo de Estado Recuento General para {count} categorías...", categoriasParaSondear.Count);
|
||||
|
||||
// PASO 4: Iterar sobre cada categoría para obtener su estado de recuento individual.
|
||||
foreach (var categoria in categoriasParaSondear)
|
||||
{
|
||||
// Salimos limpiamente del bucle si la aplicación se está deteniendo.
|
||||
if (stoppingToken.IsCancellationRequested) break;
|
||||
|
||||
// Llamamos a la API con el distrito y la CATEGORÍA ACTUAL del bucle.
|
||||
var estadoDto = await _apiService.GetEstadoRecuentoGeneralAsync(authToken, provincia.DistritoId!, categoria.Id);
|
||||
|
||||
// Solo procedemos si la API devolvió datos válidos.
|
||||
if (estadoDto != null)
|
||||
{
|
||||
// Lógica "Upsert" (Update or Insert):
|
||||
// Buscamos un registro existente usando la CLAVE PRIMARIA COMPUESTA.
|
||||
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(
|
||||
new object[] { provincia.Id, categoria.Id },
|
||||
cancellationToken: stoppingToken
|
||||
);
|
||||
|
||||
// Si no se encuentra (FindAsync devuelve null), es un registro nuevo.
|
||||
if (registroDb == null)
|
||||
{
|
||||
// Creamos una nueva instancia de la entidad.
|
||||
registroDb = new EstadoRecuentoGeneral
|
||||
{
|
||||
AmbitoGeograficoId = provincia.Id,
|
||||
CategoriaId = categoria.Id // Asignamos ambas partes de la clave primaria.
|
||||
};
|
||||
// Y la añadimos al ChangeTracker de EF para que la inserte en la BD.
|
||||
dbContext.EstadosRecuentosGenerales.Add(registroDb);
|
||||
}
|
||||
|
||||
// Mapeamos los datos del DTO de la API a nuestra entidad de base de datos.
|
||||
// Esto se hace tanto para registros nuevos como para los existentes que se van a actualizar.
|
||||
registroDb.MesasEsperadas = estadoDto.MesasEsperadas;
|
||||
registroDb.MesasTotalizadas = estadoDto.MesasTotalizadas;
|
||||
registroDb.MesasTotalizadasPorcentaje = estadoDto.MesasTotalizadasPorcentaje;
|
||||
registroDb.CantidadElectores = estadoDto.CantidadElectores;
|
||||
registroDb.CantidadVotantes = estadoDto.CantidadVotantes;
|
||||
registroDb.ParticipacionPorcentaje = estadoDto.ParticipacionPorcentaje;
|
||||
}
|
||||
}
|
||||
|
||||
// PASO 5: Guardar todos los cambios en la base de datos.
|
||||
// Al llamar a SaveChangesAsync UNA SOLA VEZ fuera del bucle, EF Core agrupa
|
||||
// todas las inserciones y actualizaciones en una única transacción eficiente.
|
||||
await dbContext.SaveChangesAsync(stoppingToken);
|
||||
_logger.LogInformation("Sondeo de Estado Recuento General completado para todas las categorías.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Capturamos cualquier excepción inesperada para que no detenga el worker y la registramos.
|
||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Estado Recuento General.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Descarga y sincroniza los catálogos base (Categorías, Ámbitos, Agrupaciones)
|
||||
/// desde la API a la base de datos local. Se ejecuta una sola vez al iniciar el worker.
|
||||
@@ -286,7 +616,7 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
{
|
||||
hasReceivedAnyNewData = true;
|
||||
|
||||
// --- CORRECCIÓN DE SEGURIDAD: Usar TryParse para la fecha ---
|
||||
// --- SEGURIDAD: Usar TryParse para la fecha ---
|
||||
DateTime fechaTotalizacion;
|
||||
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
|
||||
{
|
||||
@@ -326,7 +656,7 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
{
|
||||
hasReceivedAnyNewData = true;
|
||||
|
||||
// --- APLICAMOS LA MISMA CORRECCIÓN DE SEGURIDAD AQUÍ ---
|
||||
// --- APLICAMOS SEGURIDAD AQUÍ ---
|
||||
DateTime fechaTotalizacion;
|
||||
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
|
||||
{
|
||||
@@ -439,7 +769,6 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
|
||||
if (telegramaFile != null)
|
||||
{
|
||||
// --- INICIO DE LA CORRECCIÓN ---
|
||||
// 1. Buscamos el AmbitoGeografico específico de la MESA que estamos procesando.
|
||||
var ambitoMesa = await innerDbContext.AmbitosGeograficos
|
||||
.AsNoTracking()
|
||||
@@ -463,7 +792,6 @@ public class LowPriorityDataWorker : BackgroundService
|
||||
{
|
||||
_logger.LogWarning("No se encontró un ámbito geográfico para la mesa con MesaId {MesaId}. El telegrama no será guardado.", mesaId);
|
||||
}
|
||||
// --- FIN DE LA CORRECCIÓN ---
|
||||
}
|
||||
await Task.Delay(250, stoppingToken);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
//Elecciones.Worker/Program.cs
|
||||
using Elecciones.Database;
|
||||
using Elecciones.Infrastructure.Services;
|
||||
using Elecciones.Worker;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.Net;
|
||||
using Serilog;
|
||||
using System.Net.Http;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using Polly;
|
||||
@@ -21,12 +19,20 @@ Log.Information("Iniciando Elecciones.Worker Host...");
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
builder.Services.AddSerilog(config =>
|
||||
config
|
||||
// 1. Registra el servicio del interruptor como siempre.
|
||||
builder.Services.AddSingleton<LoggingSwitchService>();
|
||||
|
||||
// 2. Configura Serilog usando AddSerilog.
|
||||
builder.Services.AddSerilog((services, configuration) => {
|
||||
var loggingSwitch = services.GetRequiredService<LoggingSwitchService>();
|
||||
configuration
|
||||
.ReadFrom.Configuration(builder.Configuration)
|
||||
.ReadFrom.Services(services)
|
||||
.Enrich.FromLogContext()
|
||||
.MinimumLevel.ControlledBy(loggingSwitch.LevelSwitch)
|
||||
.WriteTo.Console()
|
||||
.WriteTo.File("logs/worker-.log", rollingInterval: RollingInterval.Day));
|
||||
.WriteTo.File("logs/worker-.log", rollingInterval: RollingInterval.Day);
|
||||
});
|
||||
|
||||
// --- Configuración de Servicios ---
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||
@@ -93,16 +99,47 @@ builder.Services.AddSingleton<RateLimiter>(sp =>
|
||||
QueueProcessingOrder = QueueProcessingOrder.OldestFirst
|
||||
}));
|
||||
|
||||
builder.Services.AddScoped<IElectoralApiService, ElectoralApiService>();
|
||||
builder.Services.AddScoped<IElectoralApiService, ElectoralApiService>();
|
||||
|
||||
// Registramos el servicio de token como un Singleton para que sea compartido.
|
||||
builder.Services.AddSingleton<SharedTokenService>();
|
||||
// Registramos el servicio de configuraciones de workers como un Singleton para que sea compartido.
|
||||
builder.Services.AddSingleton<WorkerConfigService>();
|
||||
|
||||
// Registramos ambos workers. El framework se encargará de iniciarlos y detenerlos.
|
||||
builder.Services.AddHostedService<CriticalDataWorker>();
|
||||
builder.Services.AddHostedService<LowPriorityDataWorker>();
|
||||
//builder.Services.AddHostedService<Worker>();
|
||||
|
||||
// --- 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 = builder.Services.BuildServiceProvider().CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var dbContext = services.GetRequiredService<EleccionesDbContext>();
|
||||
var loggingSwitchService = services.GetRequiredService<LoggingSwitchService>();
|
||||
|
||||
// Buscamos el nivel de logging guardado en la BD
|
||||
var logLevelConfig = await dbContext.Configuraciones
|
||||
.AsNoTracking()
|
||||
.FirstOrDefaultAsync(c => c.Clave == "Logging_Level");
|
||||
|
||||
if (logLevelConfig != null)
|
||||
{
|
||||
// Si lo encontramos, lo aplicamos al interruptor
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
try
|
||||
|
||||
@@ -14,7 +14,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+55954e18a797dce22f76f00b645832f361d97362")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f384a640f36be1289d652dc85e78ebdcef30968a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -180,6 +180,9 @@
|
||||
"projectReferences": {
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Core\\Elecciones.Core.csproj"
|
||||
},
|
||||
"E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj": {
|
||||
"projectPath": "E:\\Elecciones-2025\\Elecciones-Web\\src\\Elecciones.Database\\Elecciones.Database.csproj"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,6 +211,10 @@
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
},
|
||||
"Serilog": {
|
||||
"target": "Package",
|
||||
"version": "[4.3.0, )"
|
||||
},
|
||||
"System.Threading.RateLimiting": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
|
||||
@@ -14,17 +14,17 @@ services:
|
||||
- shared-net
|
||||
|
||||
# Servicio del Worker (sin cambios)
|
||||
# elecciones-worker:
|
||||
# build:
|
||||
# context: ./Elecciones-Web
|
||||
# dockerfile: src/Elecciones.Worker/Dockerfile
|
||||
# container_name: elecciones-worker
|
||||
# restart: unless-stopped
|
||||
# env_file: ./.env
|
||||
# networks:
|
||||
# - shared-net
|
||||
# volumes:
|
||||
# - ./logs-worker:/app/logs
|
||||
elecciones-worker:
|
||||
build:
|
||||
context: ./Elecciones-Web
|
||||
dockerfile: src/Elecciones.Worker/Dockerfile
|
||||
container_name: elecciones-worker
|
||||
restart: unless-stopped
|
||||
env_file: ./.env
|
||||
networks:
|
||||
- shared-net
|
||||
volumes:
|
||||
- ./logs-worker:/app/logs
|
||||
|
||||
# Servicio del Frontend Público (sin cambios)
|
||||
elecciones-frontend:
|
||||
|
||||
Reference in New Issue
Block a user