diff --git a/Elecciones-Web/frontend-admin/src/components/CandidatoOverridesManager.tsx b/Elecciones-Web/frontend-admin/src/components/CandidatoOverridesManager.tsx new file mode 100644 index 0000000..e84732e --- /dev/null +++ b/Elecciones-Web/frontend-admin/src/components/CandidatoOverridesManager.tsx @@ -0,0 +1,86 @@ +import { useState, useMemo, useEffect } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import Select from 'react-select'; +import { getMunicipiosForAdmin, getAgrupaciones, getCandidatos, updateCandidatos } from '../services/apiService'; +import type { MunicipioSimple, AgrupacionPolitica, CandidatoOverride } from '../types'; + +// Las categorías son las mismas que para los logos +const CATEGORIAS_OPTIONS = [ + { value: 5, label: 'Senadores' }, + { value: 6, label: 'Diputados' }, + { value: 7, label: 'Concejales' } +]; + +export const CandidatoOverridesManager = () => { + const queryClient = useQueryClient(); + const { data: municipios = [] } = useQuery({ queryKey: ['municipiosForAdmin'], queryFn: getMunicipiosForAdmin }); + const { data: agrupaciones = [] } = useQuery({ queryKey: ['agrupaciones'], queryFn: getAgrupaciones }); + // --- Usar la query para candidatos --- + const { data: candidatos = [] } = useQuery({ queryKey: ['candidatos'], queryFn: getCandidatos }); + + const [selectedCategoria, setSelectedCategoria] = useState<{ value: number; label: string } | null>(null); + const [selectedMunicipio, setSelectedMunicipio] = useState<{ value: string; label: string } | null>(null); + const [selectedAgrupacion, setSelectedAgrupacion] = useState<{ value: string; label: string } | null>(null); + // --- El estado es para el nombre del candidato --- + const [nombreCandidato, setNombreCandidato] = useState(''); + + const municipioOptions = useMemo(() => municipios.map(m => ({ value: m.id, label: m.nombre })), [municipios]); + const agrupacionOptions = useMemo(() => agrupaciones.map(a => ({ value: a.id, label: a.nombre })), [agrupaciones]); + + // --- Lógica para encontrar el nombre del candidato actual --- + const currentCandidato = useMemo(() => { + if (!selectedMunicipio || !selectedAgrupacion || !selectedCategoria) return ''; + return candidatos.find(c => + c.ambitoGeograficoId === parseInt(selectedMunicipio.value) && + c.agrupacionPoliticaId === selectedAgrupacion.value && + c.categoriaId === selectedCategoria.value + )?.nombreCandidato || ''; + }, [candidatos, selectedMunicipio, selectedAgrupacion, selectedCategoria]); + + useEffect(() => { setNombreCandidato(currentCandidato) }, [currentCandidato]); + + const handleSave = async () => { + if (!selectedMunicipio || !selectedAgrupacion || !selectedCategoria) return; + + // --- Construir el objeto CandidatoOverride --- + const newCandidatoEntry: CandidatoOverride = { + id: 0, // El backend no necesita el ID para un upsert + agrupacionPoliticaId: selectedAgrupacion.value, + categoriaId: selectedCategoria.value, + ambitoGeograficoId: parseInt(selectedMunicipio.value), + nombreCandidato: nombreCandidato + }; + + try { + await updateCandidatos([newCandidatoEntry]); + queryClient.invalidateQueries({ queryKey: ['candidatos'] }); + alert('Override de candidato guardado.'); + } catch { alert('Error al guardar.'); } + }; + + return ( +
+

Overrides de Nombres de Candidatos

+

Configure un nombre de candidato específico para un partido en un municipio y categoría determinados.

+
+
+ + +
+
+ + setNombreCandidato(e.target.value)} style={{ width: '100%' }} disabled={!selectedMunicipio || !selectedAgrupacion || !selectedCategoria} /> +
+ +
+
+ ); +}; \ No newline at end of file diff --git a/Elecciones-Web/frontend-admin/src/components/DashboardPage.tsx b/Elecciones-Web/frontend-admin/src/components/DashboardPage.tsx index c17a903..88ea33d 100644 --- a/Elecciones-Web/frontend-admin/src/components/DashboardPage.tsx +++ b/Elecciones-Web/frontend-admin/src/components/DashboardPage.tsx @@ -6,6 +6,7 @@ import { OrdenSenadoresManager } from './OrdenSenadoresManager'; import { ConfiguracionGeneral } from './ConfiguracionGeneral'; import { BancasManager } from './BancasManager'; import { LogoOverridesManager } from './LogoOverridesManager'; +import { CandidatoOverridesManager } from './CandidatoOverridesManager'; export const DashboardPage = () => { const { logout } = useAuth(); @@ -16,9 +17,14 @@ export const DashboardPage = () => {

Panel de Administración Electoral

-
+
- +
+ +
+
+ +
@@ -27,7 +33,7 @@ export const DashboardPage = () => {
- +
diff --git a/Elecciones-Web/frontend-admin/src/services/apiService.ts b/Elecciones-Web/frontend-admin/src/services/apiService.ts index c96a325..7c41b09 100644 --- a/Elecciones-Web/frontend-admin/src/services/apiService.ts +++ b/Elecciones-Web/frontend-admin/src/services/apiService.ts @@ -1,7 +1,7 @@ // src/services/apiService.ts import axios from 'axios'; import { triggerLogout } from '../context/authUtils'; -import type { AgrupacionPolitica, UpdateAgrupacionData, Bancada, LogoAgrupacionCategoria, MunicipioSimple } from '../types'; +import type { CandidatoOverride, AgrupacionPolitica, UpdateAgrupacionData, Bancada, LogoAgrupacionCategoria, MunicipioSimple } from '../types'; /** * URL base para las llamadas a la API. @@ -125,4 +125,14 @@ export const getMunicipiosForAdmin = async (): Promise => { // La URL final será /api/admin/catalogos/municipios const response = await adminApiClient.get('/catalogos/municipios'); return response.data; +}; + +// 6. Overrides de Candidatos +export const getCandidatos = async (): Promise => { + const response = await adminApiClient.get('/candidatos'); + return response.data; +}; + +export const updateCandidatos = async (data: CandidatoOverride[]): Promise => { + await adminApiClient.put('/candidatos', data); }; \ No newline at end of file diff --git a/Elecciones-Web/frontend-admin/src/types/index.ts b/Elecciones-Web/frontend-admin/src/types/index.ts index 9e10744..9d0da80 100644 --- a/Elecciones-Web/frontend-admin/src/types/index.ts +++ b/Elecciones-Web/frontend-admin/src/types/index.ts @@ -48,4 +48,12 @@ export interface LogoAgrupacionCategoria { ambitoGeograficoId: number | null; } -export interface MunicipioSimple { id: string; nombre: string; } \ No newline at end of file +export interface MunicipioSimple { id: string; nombre: string; } + +export interface CandidatoOverride { + id: number; + agrupacionPoliticaId: string; + categoriaId: number; + ambitoGeograficoId: number | null; + nombreCandidato: string; +} \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx b/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx index dcd5542..a03bbae 100644 --- a/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx +++ b/Elecciones-Web/frontend/src/components/ConcejalesWidget.tsx @@ -27,11 +27,11 @@ export const ConcejalesWidget = () => { // 2. Query para la lista de municipios const { data: municipios = [], isLoading: isLoadingMunicipios } = useQuery({ - // Usamos una clave genérica porque siempre pedimos la lista completa. - queryKey: ['municipios'], - // Llamamos a la función sin argumentos para obtener todos los municipios. - queryFn: () => getMunicipios(), -}); + // Usamos una clave genérica porque siempre pedimos la lista completa. + queryKey: ['municipios'], + // Llamamos a la función sin argumentos para obtener todos los municipios. + queryFn: () => getMunicipios(), + }); const cantidadAMostrar = parseInt(configData?.ConcejalesResultadosCantidad || '5', 10); @@ -48,7 +48,7 @@ export const ConcejalesWidget = () => { municipios .map(m => ({ value: m.id, label: m.nombre })) .sort((a, b) => a.label.localeCompare(b.label)), - [municipios]); + [municipios]); const { data: resultados, isLoading: isLoadingResultados } = useQuery({ queryKey: ['resultadosPorMunicipio', selectedMunicipio?.value, CATEGORIA_ID], @@ -92,7 +92,7 @@ export const ConcejalesWidget = () => {
{(isLoadingMunicipios || (isLoadingResultados && selectedMunicipio)) &&

Cargando...

} - {!selectedMunicipio && !isLoadingMunicipios &&

Seleccione un municipio.

} + {!selectedMunicipio && !isLoadingMunicipios &&

Seleccione un municipio.

} {displayResults.map(partido => (
@@ -106,6 +106,9 @@ export const ConcejalesWidget = () => {
+
+ {partido.nombreCandidato} +
))} diff --git a/Elecciones-Web/frontend/src/components/DiputadosWidget.tsx b/Elecciones-Web/frontend/src/components/DiputadosWidget.tsx index 1b1ac4f..36088c5 100644 --- a/Elecciones-Web/frontend/src/components/DiputadosWidget.tsx +++ b/Elecciones-Web/frontend/src/components/DiputadosWidget.tsx @@ -50,7 +50,7 @@ export const DiputadosWidget = () => { municipios .map(m => ({ value: m.id, label: m.nombre })) .sort((a, b) => a.label.localeCompare(b.label)), - [municipios]); + [municipios]); const { data: resultados, isLoading: isLoadingResultados } = useQuery({ queryKey: ['resultadosMunicipio', selectedMunicipio?.value, CATEGORIA_ID], @@ -94,7 +94,7 @@ export const DiputadosWidget = () => {
{(isLoadingMunicipios || (isLoadingResultados && selectedMunicipio)) &&

Cargando...

} - {!selectedMunicipio && !isLoadingMunicipios &&

Seleccione un municipio.

} + {!selectedMunicipio && !isLoadingMunicipios &&

Seleccione un municipio.

} {displayResults.map(partido => (
@@ -108,6 +108,9 @@ export const DiputadosWidget = () => {
+
+ {partido.nombreCandidato} +
))} diff --git a/Elecciones-Web/frontend/src/components/SenadoresWidget.tsx b/Elecciones-Web/frontend/src/components/SenadoresWidget.tsx index 81d1929..7fc24ee 100644 --- a/Elecciones-Web/frontend/src/components/SenadoresWidget.tsx +++ b/Elecciones-Web/frontend/src/components/SenadoresWidget.tsx @@ -107,6 +107,9 @@ export const SenadoresWidget = () => {
+
+ {partido.nombreCandidato} +
))} diff --git a/Elecciones-Web/frontend/src/components/TickerWidget.css b/Elecciones-Web/frontend/src/components/TickerWidget.css index 438b4dd..d9e94a0 100644 --- a/Elecciones-Web/frontend/src/components/TickerWidget.css +++ b/Elecciones-Web/frontend/src/components/TickerWidget.css @@ -133,4 +133,13 @@ .ticker-party .party-name { white-space: normal; } +} + +.party-candidate-name { + font-size: 0.8rem; + color: #555; + margin-top: 4px; /* Espacio entre la barra y el nombre */ + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; } \ No newline at end of file diff --git a/Elecciones-Web/frontend/src/types/types.ts b/Elecciones-Web/frontend/src/types/types.ts index a1f2863..626098a 100644 --- a/Elecciones-Web/frontend/src/types/types.ts +++ b/Elecciones-Web/frontend/src/types/types.ts @@ -49,6 +49,7 @@ export interface ResultadoTicker { logoUrl: string | null; votos: number; porcentaje: number; + nombreCandidato?: string | null; } export interface EstadoRecuentoTicker { diff --git a/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs b/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs index 501b735..3ff5e58 100644 --- a/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs +++ b/Elecciones-Web/src/Elecciones.Api/Controllers/AdminController.cs @@ -235,4 +235,77 @@ public class AdminController : ControllerBase return Ok(municipios); } + + /// + /// Obtiene todos los overrides de candidatos configurados. + /// + [HttpGet("candidatos")] + public async Task GetCandidatos() + { + var candidatos = await _dbContext.CandidatosOverrides + .AsNoTracking() + .ToListAsync(); + return Ok(candidatos); + } + + /// + /// Guarda (actualiza o crea) una lista de overrides de candidatos. + /// + [HttpPut("candidatos")] + public async Task UpdateCandidatos([FromBody] List candidatos) + { + foreach (var candidatoDto in candidatos) + { + // Buscamos un override existente basado en la combinación única + var candidatoExistente = await _dbContext.CandidatosOverrides + .FirstOrDefaultAsync(c => + c.AgrupacionPoliticaId == candidatoDto.AgrupacionPoliticaId && + c.CategoriaId == candidatoDto.CategoriaId && + c.AmbitoGeograficoId == candidatoDto.AmbitoGeograficoId); + + if (candidatoExistente != null) + { + // Si existe y el nombre es diferente, lo actualizamos. + if (candidatoExistente.NombreCandidato != candidatoDto.NombreCandidato) + { + candidatoExistente.NombreCandidato = candidatoDto.NombreCandidato; + } + } + else + { + // Si no se encontró un registro exacto, lo añadimos a la base de datos, + // pero solo si el nombre del candidato no está vacío. + if (!string.IsNullOrWhiteSpace(candidatoDto.NombreCandidato)) + { + _dbContext.CandidatosOverrides.Add(new CandidatoOverride + { + AgrupacionPoliticaId = candidatoDto.AgrupacionPoliticaId, + CategoriaId = candidatoDto.CategoriaId, + AmbitoGeograficoId = candidatoDto.AmbitoGeograficoId, + NombreCandidato = candidatoDto.NombreCandidato + }); + } + } + } + + // También necesitamos manejar los casos donde se borra un nombre (se envía un string vacío) + var overridesAEliminar = await _dbContext.CandidatosOverrides + .Where(c => candidatos.Any(dto => + dto.AgrupacionPoliticaId == c.AgrupacionPoliticaId && + dto.CategoriaId == c.CategoriaId && + dto.AmbitoGeograficoId == c.AmbitoGeograficoId && + string.IsNullOrWhiteSpace(dto.NombreCandidato) + )) + .ToListAsync(); + + if (overridesAEliminar.Any()) + { + _dbContext.CandidatosOverrides.RemoveRange(overridesAEliminar); + } + + + await _dbContext.SaveChangesAsync(); + _logger.LogInformation("Se procesaron {Count} overrides de candidatos.", candidatos.Count); + return NoContent(); // Respuesta estándar para un PUT exitoso + } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs b/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs index 3c866bf..baa85a8 100644 --- a/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs +++ b/Elecciones-Web/src/Elecciones.Api/Controllers/ResultadosController.cs @@ -22,7 +22,7 @@ public class ResultadosController : ControllerBase _configuration = configuration; } - [HttpGet("partido/{municipioId}")] // Renombramos el parámetro para mayor claridad + [HttpGet("partido/{municipioId}")] public async Task GetResultadosPorPartido(string municipioId, [FromQuery] int categoriaId) { var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking() @@ -51,6 +51,11 @@ public class ResultadosController : ControllerBase .Where(rv => rv.AmbitoGeograficoId == ambito.Id && rv.CategoriaId == categoriaId) .ToListAsync(); + var candidatosRelevantes = await _dbContext.CandidatosOverrides.AsNoTracking() + .Where(c => c.CategoriaId == categoriaId && agrupacionIds.Contains(c.AgrupacionPoliticaId) && + (c.AmbitoGeograficoId == null || c.AmbitoGeograficoId == ambito.Id)) + .ToListAsync(); + long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos); var respuestaDto = new MunicipioResultadosDto @@ -64,6 +69,9 @@ public class ResultadosController : ControllerBase var logoUrl = logosRelevantes.FirstOrDefault(l => l.AgrupacionPoliticaId == rv.AgrupacionPoliticaId && l.AmbitoGeograficoId == ambito.Id)?.LogoUrl ?? logosRelevantes.FirstOrDefault(l => l.AgrupacionPoliticaId == rv.AgrupacionPoliticaId && l.AmbitoGeograficoId == null)?.LogoUrl; + var nombreCandidato = candidatosRelevantes.FirstOrDefault(c => c.AgrupacionPoliticaId == rv.AgrupacionPoliticaId && c.AmbitoGeograficoId == ambito.Id)?.NombreCandidato + ?? candidatosRelevantes.FirstOrDefault(c => c.AgrupacionPoliticaId == rv.AgrupacionPoliticaId && c.AmbitoGeograficoId == null)?.NombreCandidato; + return new AgrupacionResultadoDto { Id = rv.AgrupacionPolitica.Id, @@ -72,6 +80,7 @@ public class ResultadosController : ControllerBase Color = rv.AgrupacionPolitica.Color, LogoUrl = logoUrl, Votos = rv.CantidadVotos, + NombreCandidato = nombreCandidato, Porcentaje = totalVotosPositivos > 0 ? (rv.CantidadVotos * 100.0m / totalVotosPositivos) : 0 }; }).OrderByDescending(r => r.Votos).ToList(), @@ -557,27 +566,18 @@ public class ResultadosController : ControllerBase if (!municipiosDeLaSeccion.Any()) { - return Ok(new List()); + return Ok(new { UltimaActualizacion = DateTime.UtcNow, Resultados = new List() }); } - // --- INICIO DE LA CORRECCIÓN --- + // --- INICIO DE LA CORRECCIÓN DE LOGOS --- - // 1. Obtenemos TODOS los logos para la categoría, sin convertirlos a diccionario todavía. - var todosLosLogos = await _dbContext.LogosAgrupacionesCategorias + // 1. Buscamos logos que sean para esta categoría Y que sean generales (ámbito null). + var logosGenerales = await _dbContext.LogosAgrupacionesCategorias .AsNoTracking() - .Where(l => l.CategoriaId == categoriaId) - .ToListAsync(); + .Where(l => l.CategoriaId == categoriaId && l.AmbitoGeograficoId == null) + .ToDictionaryAsync(l => l.AgrupacionPoliticaId); - // 2. Procesamos los logos en memoria para manejar duplicados, priorizando los que tienen ámbito. - // El resultado es un diccionario limpio sin claves duplicadas. - var logos = todosLosLogos - .GroupBy(l => l.AgrupacionPoliticaId) // Agrupamos por la clave que causa el problema - .ToDictionary( - g => g.Key, // La clave del diccionario es AgrupacionPoliticaId - g => g.OrderByDescending(l => l.AmbitoGeograficoId).First() // Para cada grupo, tomamos el logo más específico (el que tiene un AmbitoId) o el general si es el único. - ); - - // --- FIN DE LA CORRECCIÓN --- + // --- FIN DE LA CORRECCIÓN DE LOGOS --- var resultadosMunicipales = await _dbContext.ResultadosVotos .AsNoTracking() @@ -601,7 +601,8 @@ public class ResultadosController : ControllerBase r.Agrupacion.Nombre, r.Agrupacion.NombreCorto, r.Agrupacion.Color, - LogoUrl = logos.GetValueOrDefault(r.Agrupacion.Id)?.LogoUrl, + // 2. Usamos el diccionario de logos generales para buscar la URL. + LogoUrl = logosGenerales.GetValueOrDefault(r.Agrupacion.Id)?.LogoUrl, Votos = r.Votos, Porcentaje = totalVotosSeccion > 0 ? ((decimal)r.Votos * 100 / totalVotosSeccion) : 0 }) diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs index 042c866..3048262 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/Elecciones.Api.AssemblyInfo.cs @@ -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+0ce5e2e2c9bd6b64a7ea8629eeef35d204013a33")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json index 727cb71..9a04546 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json @@ -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=","hGxHNQ\u002B1azEK\u002Bi1edq5HWPFILZgOw/WtDwYY8eIiOpE=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","DJ\u002BouDXt6xAa6457U4R5nMU/5dFpuSVR7W9oUVdiomg="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"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=","/hrHm\u002B3v8DuHSlyFsxHPCFUvDW\u002BZsLR4kaMxNwUHl2M=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v\u002BEjGaN1m59e9gwl3kXTpjNw\u002B3kwhJD2SLKx38/opjM="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json index 257a3f2..7aff450 100644 --- a/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json +++ b/Elecciones-Web/src/Elecciones.Api/obj/Debug/net9.0/rjsmrazor.dswa.cache.json @@ -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=","hGxHNQ\u002B1azEK\u002Bi1edq5HWPFILZgOw/WtDwYY8eIiOpE=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","DJ\u002BouDXt6xAa6457U4R5nMU/5dFpuSVR7W9oUVdiomg="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file +{"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=","/hrHm\u002B3v8DuHSlyFsxHPCFUvDW\u002BZsLR4kaMxNwUHl2M=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","v\u002BEjGaN1m59e9gwl3kXTpjNw\u002B3kwhJD2SLKx38/opjM="],"CachedAssets":{},"CachedCopyCandidates":{}} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Core/DTOs/ApiResponses/AgrupacionResultadoDto.cs b/Elecciones-Web/src/Elecciones.Core/DTOs/ApiResponses/AgrupacionResultadoDto.cs index aead960..60007c4 100644 --- a/Elecciones-Web/src/Elecciones.Core/DTOs/ApiResponses/AgrupacionResultadoDto.cs +++ b/Elecciones-Web/src/Elecciones.Core/DTOs/ApiResponses/AgrupacionResultadoDto.cs @@ -8,6 +8,7 @@ public class AgrupacionResultadoDto public string? NombreCorto { get; set; } = null!; public string? Color { get; set; } = null!; public string? LogoUrl { get; set; } = null!; + public string? NombreCandidato { get; set; } = null!; public long Votos { get; set; } public decimal Porcentaje { get; set; } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs index 62f3f17..cd7e1a0 100644 --- a/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Core/obj/Debug/net9.0/Elecciones.Core.AssemblyInfo.cs @@ -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+0ce5e2e2c9bd6b64a7ea8629eeef35d204013a33")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs b/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs index cc72a3e..ebaae6c 100644 --- a/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs +++ b/Elecciones-Web/src/Elecciones.Database/EleccionesDbContext.cs @@ -20,6 +20,7 @@ public class EleccionesDbContext(DbContextOptions options) public DbSet Bancadas { get; set; } public DbSet OcupantesBancas { get; set; } public DbSet LogosAgrupacionesCategorias { get; set; } + public DbSet CandidatosOverrides { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -81,5 +82,12 @@ public class EleccionesDbContext(DbContextOptions options) // La combinación de las tres columnas debe ser única. entity.HasIndex(l => new { l.AgrupacionPoliticaId, l.CategoriaId, l.AmbitoGeograficoId }).IsUnique(); }); + modelBuilder.Entity(entity => + { + // La combinación de agrupación, categoría y ámbito debe ser única + // para evitar tener dos nombres de candidato diferentes para la misma situación. + entity.HasIndex(c => new { c.AgrupacionPoliticaId, c.CategoriaId, c.AmbitoGeograficoId }) + .IsUnique(); + }); } } \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Entities/CandidatoOverride.cs b/Elecciones-Web/src/Elecciones.Database/Entities/CandidatoOverride.cs new file mode 100644 index 0000000..d23a29c --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Entities/CandidatoOverride.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Elecciones.Database.Entities; + +public class CandidatoOverride +{ + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int Id { get; set; } + + [Required] + public string AgrupacionPoliticaId { get; set; } = null!; + + [ForeignKey("AgrupacionPoliticaId")] + public AgrupacionPolitica AgrupacionPolitica { get; set; } = null!; + + [Required] + public int CategoriaId { get; set; } + + [ForeignKey("CategoriaId")] + public CategoriaElectoral CategoriaElectoral { get; set; } = null!; + + // El AmbitoGeograficoId es opcional. Si es null, el override es general. + public int? AmbitoGeograficoId { get; set; } + + [ForeignKey("AmbitoGeograficoId")] + public AmbitoGeografico? AmbitoGeografico { get; set; } + + // El nombre del candidato que queremos mostrar. + [Required] + [MaxLength(255)] + public string NombreCandidato { get; set; } = null!; +} \ No newline at end of file diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.Designer.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.Designer.cs new file mode 100644 index 0000000..3664ea4 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.Designer.cs @@ -0,0 +1,617 @@ +// +using System; +using Elecciones.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elecciones.Database.Migrations +{ + [DbContext(typeof(EleccionesDbContext))] + [Migration("20250905134421_AddCandidatosOverridesTable")] + partial class AddCandidatosOverridesTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseCollation("Modern_Spanish_CI_AS") + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elecciones.Database.Entities.AdminUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordSalt") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.ToTable("AdminUsers"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Color") + .HasColumnType("nvarchar(max)"); + + b.Property("IdTelegrama") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("NombreCorto") + .HasColumnType("nvarchar(max)"); + + b.Property("OrdenDiputados") + .HasColumnType("int"); + + b.Property("OrdenSenadores") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("AgrupacionesPoliticas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CircuitoId") + .HasColumnType("nvarchar(max)"); + + b.Property("DistritoId") + .HasColumnType("nvarchar(max)"); + + b.Property("EstablecimientoId") + .HasColumnType("nvarchar(max)"); + + b.Property("MesaId") + .HasColumnType("nvarchar(max)"); + + b.Property("MunicipioId") + .HasColumnType("nvarchar(max)"); + + b.Property("NivelId") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionId") + .HasColumnType("nvarchar(max)"); + + b.Property("SeccionProvincialId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("AmbitosGeograficos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .HasColumnType("nvarchar(450)"); + + b.Property("Camara") + .HasColumnType("int"); + + b.Property("NumeroBanca") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.ToTable("Bancadas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("NombreCandidato") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AmbitoGeograficoId"); + + b.HasIndex("CategoriaId"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId") + .IsUnique() + .HasFilter("[AmbitoGeograficoId] IS NOT NULL"); + + b.ToTable("CandidatosOverrides"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Nombre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Orden") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("CategoriasElectorales"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Configuracion", b => + { + b.Property("Clave") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Valor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Clave"); + + b.ToTable("Configuraciones"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("ParticipacionPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("VotosEnBlanco") + .HasColumnType("bigint"); + + b.Property("VotosEnBlancoPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosNulos") + .HasColumnType("bigint"); + + b.Property("VotosNulosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.Property("VotosRecurridos") + .HasColumnType("bigint"); + + b.Property("VotosRecurridosPorcentaje") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("AmbitoGeograficoId", "CategoriaId"); + + b.ToTable("EstadosRecuentos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b => + { + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("CantidadElectores") + .HasColumnType("int"); + + b.Property("CantidadVotantes") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("MesasEsperadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadas") + .HasColumnType("int"); + + b.Property("MesasTotalizadasPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.Property("ParticipacionPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.HasKey("AmbitoGeograficoId", "CategoriaId"); + + b.HasIndex("CategoriaId"); + + b.ToTable("EstadosRecuentosGenerales"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.LogoAgrupacionCategoria", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("LogoUrl") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId") + .IsUnique() + .HasFilter("[AmbitoGeograficoId] IS NOT NULL"); + + b.ToTable("LogosAgrupacionesCategorias"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BancadaId") + .HasColumnType("int"); + + b.Property("FotoUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("NombreOcupante") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Periodo") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("BancadaId") + .IsUnique(); + + b.ToTable("OcupantesBancas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.Property("NroBancas") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId") + .IsUnique(); + + b.ToTable("ProyeccionesBancas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CantidadVotos") + .HasColumnType("bigint"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("PorcentajeVotos") + .HasPrecision(18, 4) + .HasColumnType("decimal(18,4)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId") + .IsUnique(); + + b.ToTable("ResultadosVotos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("Votos") + .HasColumnType("bigint"); + + b.Property("VotosPorcentaje") + .HasPrecision(5, 2) + .HasColumnType("decimal(5,2)"); + + b.HasKey("Id"); + + b.HasIndex("AgrupacionPoliticaId"); + + b.ToTable("ResumenesVotos"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("ContenidoBase64") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FechaEscaneo") + .HasColumnType("datetime2"); + + b.Property("FechaTotalizacion") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("Telegramas"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId"); + + b.Navigation("AgrupacionPolitica"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") + .WithMany() + .HasForeignKey("AmbitoGeograficoId"); + + b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral") + .WithMany() + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgrupacionPolitica"); + + b.Navigation("AmbitoGeografico"); + + b.Navigation("CategoriaElectoral"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b => + { + b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") + .WithMany() + .HasForeignKey("AmbitoGeograficoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AmbitoGeografico"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b => + { + b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral") + .WithMany() + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CategoriaElectoral"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.OcupanteBanca", b => + { + b.HasOne("Elecciones.Database.Entities.Bancada", "Bancada") + .WithOne("Ocupante") + .HasForeignKey("Elecciones.Database.Entities.OcupanteBanca", "BancadaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Bancada"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") + .WithMany() + .HasForeignKey("AmbitoGeograficoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgrupacionPolitica"); + + b.Navigation("AmbitoGeografico"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") + .WithMany() + .HasForeignKey("AmbitoGeograficoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgrupacionPolitica"); + + b.Navigation("AmbitoGeografico"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgrupacionPolitica"); + }); + + modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b => + { + b.Navigation("Ocupante"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.cs new file mode 100644 index 0000000..62a8926 --- /dev/null +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/20250905134421_AddCandidatosOverridesTable.cs @@ -0,0 +1,71 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elecciones.Database.Migrations +{ + /// + public partial class AddCandidatosOverridesTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CandidatosOverrides", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + AgrupacionPoliticaId = table.Column(type: "nvarchar(450)", nullable: false), + CategoriaId = table.Column(type: "int", nullable: false), + AmbitoGeograficoId = table.Column(type: "int", nullable: true), + NombreCandidato = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CandidatosOverrides", x => x.Id); + table.ForeignKey( + name: "FK_CandidatosOverrides_AgrupacionesPoliticas_AgrupacionPoliticaId", + column: x => x.AgrupacionPoliticaId, + principalTable: "AgrupacionesPoliticas", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CandidatosOverrides_AmbitosGeograficos_AmbitoGeograficoId", + column: x => x.AmbitoGeograficoId, + principalTable: "AmbitosGeograficos", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_CandidatosOverrides_CategoriasElectorales_CategoriaId", + column: x => x.CategoriaId, + principalTable: "CategoriasElectorales", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CandidatosOverrides_AgrupacionPoliticaId_CategoriaId_AmbitoGeograficoId", + table: "CandidatosOverrides", + columns: new[] { "AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId" }, + unique: true, + filter: "[AmbitoGeograficoId] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_CandidatosOverrides_AmbitoGeograficoId", + table: "CandidatosOverrides", + column: "AmbitoGeograficoId"); + + migrationBuilder.CreateIndex( + name: "IX_CandidatosOverrides_CategoriaId", + table: "CandidatosOverrides", + column: "CategoriaId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CandidatosOverrides"); + } + } +} diff --git a/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs b/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs index 9721e92..d29e3bd 100644 --- a/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs +++ b/Elecciones-Web/src/Elecciones.Database/Migrations/EleccionesDbContextModelSnapshot.cs @@ -144,6 +144,42 @@ namespace Elecciones.Database.Migrations b.ToTable("Bancadas"); }); + modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AgrupacionPoliticaId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("AmbitoGeograficoId") + .HasColumnType("int"); + + b.Property("CategoriaId") + .HasColumnType("int"); + + b.Property("NombreCandidato") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AmbitoGeograficoId"); + + b.HasIndex("CategoriaId"); + + b.HasIndex("AgrupacionPoliticaId", "CategoriaId", "AmbitoGeograficoId") + .IsUnique() + .HasFilter("[AmbitoGeograficoId] IS NOT NULL"); + + b.ToTable("CandidatosOverrides"); + }); + modelBuilder.Entity("Elecciones.Database.Entities.CategoriaElectoral", b => { b.Property("Id") @@ -461,6 +497,31 @@ namespace Elecciones.Database.Migrations b.Navigation("AgrupacionPolitica"); }); + modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b => + { + b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica") + .WithMany() + .HasForeignKey("AgrupacionPoliticaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") + .WithMany() + .HasForeignKey("AmbitoGeograficoId"); + + b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral") + .WithMany() + .HasForeignKey("CategoriaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AgrupacionPolitica"); + + b.Navigation("AmbitoGeografico"); + + b.Navigation("CategoriaElectoral"); + }); + modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b => { b.HasOne("Elecciones.Database.Entities.AmbitoGeografico", "AmbitoGeografico") diff --git a/Elecciones-Web/src/Elecciones.Database/obj/Debug/net9.0/Elecciones.Database.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Database/obj/Debug/net9.0/Elecciones.Database.AssemblyInfo.cs index 4e9d0a9..2f7b9da 100644 --- a/Elecciones-Web/src/Elecciones.Database/obj/Debug/net9.0/Elecciones.Database.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Database/obj/Debug/net9.0/Elecciones.Database.AssemblyInfo.cs @@ -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+0ce5e2e2c9bd6b64a7ea8629eeef35d204013a33")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs index 19c9312..df81f6d 100644 --- a/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs +++ b/Elecciones-Web/src/Elecciones.Infrastructure/obj/Debug/net9.0/Elecciones.Infrastructure.AssemblyInfo.cs @@ -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+0ce5e2e2c9bd6b64a7ea8629eeef35d204013a33")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+479c2c60f214472aeffd4404b482ffb940c3049e")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]