feat(Worker): Adaptación integral para la API de Elecciones Nacionales

Este commit refactoriza por completo el sistema de recolección de datos para asegurar la compatibilidad con la nueva API nacional, pasando de un modelo de distrito único a uno multi-distrito.

Cambios principales:

- **Refactorización de `SondearResumenProvincialAsync`:**
  - Se elimina la dependencia del endpoint obsoleto `/getResumen`.
  - El método ahora itera sobre todas las provincias (`NivelId=10`) y categorías, utilizando `GetResultadosAsync` para obtener los datos agregados.

- **Expansión de `SondearResultadosMunicipalesAsync`:**
  - Se renombra a `SondearResultadosPorAmbitosAsync` para reflejar su nueva responsabilidad.
  - La lógica ahora sondea múltiples niveles jerárquicos (`NivelId` 10, 20, 30), capturando resultados detallados para Provincias, Secciones Electorales y Municipios.

- **Modificación del Modelo de Datos:**
  - Se añade la columna `CategoriaId` a la entidad y tabla `ResumenVoto`.
  - Se crea la migración de base de datos `AddCategoriaIdToResumenVoto` para aplicar el cambio.

- **Ajustes de Nulabilidad en API Service:**
  - Se actualizan las firmas de `GetResultadosAsync` en `IElectoralApiService` y `ElectoralApiService` para permitir que `seccionId` y `municipioId` sean nulables (`string?`), resolviendo errores de compilación CS8625.

- **Deshabilitación de Seeders de Ejemplo:**
  - Se introduce una bandera `generarDatosDeEjemplo` en `Program.cs` de la API, establecida en `false`, para prevenir la ejecución de código de simulación en entornos de producción o pruebas.
This commit is contained in:
2025-10-14 16:00:55 -03:00
parent 84f7643907
commit 316f49f25b
18 changed files with 1053 additions and 343 deletions

View File

@@ -167,6 +167,9 @@ using (var scope = app.Services.CreateScope())
var logger = services.GetRequiredService<ILogger<Program>>(); var logger = services.GetRequiredService<ILogger<Program>>();
var hasher = services.GetRequiredService<IPasswordHasher>(); var hasher = services.GetRequiredService<IPasswordHasher>();
// --- PASO 1: Añadir esta bandera de control ---
bool generarDatosDeEjemplo = false; // <-- Poner en 'false' para deshabilitar
// --- SEEDER 1: DATOS ESTRUCTURALES BÁSICOS (se ejecutan una sola vez si la BD está vacía) --- // --- SEEDER 1: DATOS ESTRUCTURALES BÁSICOS (se ejecutan una sola vez si la BD está vacía) ---
// Estos son los datos maestros que NUNCA cambian. // Estos son los datos maestros que NUNCA cambian.
@@ -218,7 +221,9 @@ using (var scope = app.Services.CreateScope())
await context.SaveChangesAsync(); await context.SaveChangesAsync();
logger.LogInformation("--> Default configurations verified/seeded."); logger.LogInformation("--> Default configurations verified/seeded.");
// --- PASO 2: Envolver todo el bloque del Seeder 2 en esta condición ---
if (generarDatosDeEjemplo)
{
// --- SEEDER 2: DATOS DE EJEMPLO PARA ELECCIÓN NACIONAL (se ejecuta solo si faltan sus votos) --- // --- SEEDER 2: DATOS DE EJEMPLO PARA ELECCIÓN NACIONAL (se ejecuta solo si faltan sus votos) ---
const int eleccionNacionalId = 2; const int eleccionNacionalId = 2;
if (!await context.ResultadosVotos.AnyAsync(r => r.EleccionId == eleccionNacionalId)) if (!await context.ResultadosVotos.AnyAsync(r => r.EleccionId == eleccionNacionalId))
@@ -397,6 +402,7 @@ using (var scope = app.Services.CreateScope())
logger.LogInformation("--> Seeded Bancas Previas para la Elección Nacional."); logger.LogInformation("--> Seeded Bancas Previas para la Elección Nacional.");
} }
} }
}
} }
// --- FIN DEL BLOQUE DE SEEDERS UNIFICADO --- // --- FIN DEL BLOQUE DE SEEDERS UNIFICADO ---

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a316e5dd0876a581437b08d9980b658cc714da90")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+84f764390766e1d9716a38464ee478f3d0b75f96")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","M5oop6AzAA5LmqkUXPrT16q3xF0Jty6n7nfGWkjXG94="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","Zf7uND\u002BRw1vwmlJX6vYl9l2j52U48b1N59mSoDoOeFU=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","ViGA16LZjzGiizSwwZApo6OQjv5hBNvl3QW/ISbKUWM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","A7nz7qgOtQ1CwZZLvNnr0b5QZB3fTi3y4i6y7rBIcxQ=","znnuRi2tsk7AACuYo4WSgj7NcLriG4PKVaF4L35SvDk=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","M5oop6AzAA5LmqkUXPrT16q3xF0Jty6n7nfGWkjXG94="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["YB39loxHH43S4MF8aTOiogcIbBAIq5Qj3dlJkIfYVxI=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","E2ODTAlJxzsXY1iP1eB/02NIUK\u002BnQveGlWAOHY1cpgA=","GelE32odx/vTului267wqi6zL3abBnF9yvwC2Q66LoM=","TEsXImnzxFKTIq2f5fiDu7i6Ar/cbecW5MZ3z8Wb/a4=","5WogJu\u002BUPlF\u002BE5mq/ILtDXpVwqwmhHtsEB13nmT5JJk=","dcHQRkttjMjo2dvhL7hA9t4Pg\u002B7OnjZpkFmakT4QR9U=","Of8nTYw5l\u002BgiAJo7z6XYIntG2tUtCFcILzHbTiiXn\u002Bw=","PDy\u002BTiayvNAoXXBEgwC/kCojpgOOMI6RQOIoSXs3LJc=","ePXrkee3hv3wHUr8S7aYmRVvXUTxQf76zApKGv3/l3o=","DXx5dQywLo3UsY2zQaUG\u002BbW4ObiYbybxPBWxeJD2bhk=","muVh5sjH3sgdvuz4TbuTwTggX1uDnsWXgoosMKST/r4=","nrP5gSIA5vzgp8v12CAOr943QYLxU4Til6oiCcWSNI8=","yMd45U9BK07I3b3fBQ627PWTYyZ2ZjrmFc5VD\u002BQVx1Q=","xKskvcoJU0RVRN1a5dRqKRM7IP5vmmbraUaPFYjhnCc=","p7BjQw7aSZjfOCqmKm7/kPO9qegEQZBfirMjlOx/I1I=","MI0hVVLYavEhzHq/Z1UbajfrxanA1aET19aOH8G2ImI=","2dY8CqW9fAY8yN0foa\u002BZp2gc0RfPoPmB/tKSj1QoTw0=","79rfGLH4UjfTPvc//\u002BZjnBqdz585pUtYZ0/hwE2iEic=","PUqgvMdfTQkF5lpBVtHv2teQLV5WaEH0xMKTmINe2YQ=","\u002BFI0b4ppdxel/pby/y/xKImHrtdxo2g83OhskdREyIg=","jEESu6\u002BhbDvNMjLt/6OufuK\u002B9cHmzx\u002BTCIn4fWa9nSc=","UaCPJEvR4nVxxGCB5CUnRlJiw4drDW3Q3Nss\u002Bya2cv4=","ZqF13CT3rok/Gzl\u002BMsw3q9X1nf65bwEVD670efE3k\u002Bk=","gH3W7phPzBCY1DAVn4YnP4SA8Uaq73TpctS0yFSvzNM=","u5F4J4\u002BLHUIOCz5ze5NSF42mDeAaAfi\u002BKN3Ay3rKLY8=","GeUUID0ymF5rrBWdX7YHzWA5GiGkNWCNUog4sp4xL3c=","3BxX4I0JXoDqmE8m0BrRZhixBRlHEueS3jAlmUXE/I8=","IlET7uqumshgFxIEvfKRskON\u002BeAKZ7OfD/kCeAwn0PM=","NN2rS\u002B89ZAITWlNODPcF/lHIh3ZNmAHvUX4EjqSkX4s=","OE89N/FsYhRU1Dy5Ne83ehzSwlNc/RcxHrJpHxPHfqY=","QI7IL4TkYEqfUiIEXQiVCaZx4vrM9/wZlvOrhnUd4jQ=","UIntj4QoiyGr7bnJN8KK5PGrhQd89m\u002BLfh4T8VKPxAk=","Zf7uND\u002BRw1vwmlJX6vYl9l2j52U48b1N59mSoDoOeFU=","BY4GeeFiQbYpWuSzb2XIY4JatmLNOZ6dhKs4ZT92nsM=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","ViGA16LZjzGiizSwwZApo6OQjv5hBNvl3QW/ISbKUWM="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a316e5dd0876a581437b08d9980b658cc714da90")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+84f764390766e1d9716a38464ee478f3d0b75f96")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Core")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -56,8 +56,14 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
modelBuilder.Entity<ResultadoVoto>() modelBuilder.Entity<ResultadoVoto>()
.Property(e => e.PorcentajeVotos).HasPrecision(18, 4); .Property(e => e.PorcentajeVotos).HasPrecision(18, 4);
modelBuilder.Entity<ResumenVoto>() modelBuilder.Entity<ResumenVoto>(entity => // Busca o crea este bloque
.Property(e => e.VotosPorcentaje).HasPrecision(5, 2); {
entity.Property(e => e.VotosPorcentaje).HasPrecision(5, 2);
// Esto asegura que no se pueda tener dos entradas para el mismo partido,
// en la misma categoría y en el mismo ámbito.
entity.HasIndex(r => new { r.AmbitoGeograficoId, r.CategoriaId, r.AgrupacionPoliticaId })
.IsUnique();
});
modelBuilder.Entity<EstadoRecuentoGeneral>(entity => modelBuilder.Entity<EstadoRecuentoGeneral>(entity =>
{ {

View File

@@ -13,6 +13,9 @@ public class ResumenVoto
[Required] [Required]
public int AmbitoGeograficoId { get; set; } public int AmbitoGeograficoId { get; set; }
[Required]
public int CategoriaId { get; set; }
[Required] [Required]
public string AgrupacionPoliticaId { get; set; } = null!; public string AgrupacionPoliticaId { get; set; } = null!;

View File

@@ -0,0 +1,732 @@
// <auto-generated />
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("20251014172711_AddCategoriaIdToResumenVoto")]
partial class AddCategoriaIdToResumenVoto
{
/// <inheritdoc />
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("Eleccion", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<string>("DistritoId")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<DateOnly>("Fecha")
.HasColumnType("date");
b.Property<string>("Nivel")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("Elecciones");
});
modelBuilder.Entity("Elecciones.Database.Entities.AdminUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("PasswordHash")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("PasswordSalt")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Username")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("AdminUsers");
});
modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<string>("Color")
.HasColumnType("nvarchar(max)");
b.Property<string>("IdTelegrama")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("NombreCorto")
.HasColumnType("nvarchar(max)");
b.Property<int?>("OrdenDiputados")
.HasColumnType("int");
b.Property<int?>("OrdenDiputadosNacionales")
.HasColumnType("int");
b.Property<int?>("OrdenSenadores")
.HasColumnType("int");
b.Property<int?>("OrdenSenadoresNacionales")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("AgrupacionesPoliticas");
});
modelBuilder.Entity("Elecciones.Database.Entities.AmbitoGeografico", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("CircuitoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("DistritoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("EstablecimientoId")
.HasColumnType("nvarchar(max)");
b.Property<string>("MesaId")
.HasColumnType("nvarchar(max)");
b.Property<string>("MunicipioId")
.HasColumnType("nvarchar(max)");
b.Property<int>("NivelId")
.HasColumnType("int");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("SeccionId")
.HasColumnType("nvarchar(max)");
b.Property<string>("SeccionProvincialId")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.ToTable("AmbitosGeograficos");
});
modelBuilder.Entity("Elecciones.Database.Entities.BancaPrevia", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)")
.UseCollation("Modern_Spanish_CI_AS");
b.Property<int>("Camara")
.HasColumnType("int");
b.Property<int>("Cantidad")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.ToTable("BancasPrevias");
});
modelBuilder.Entity("Elecciones.Database.Entities.Bancada", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.HasColumnType("nvarchar(450)");
b.Property<int>("Camara")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<int>("NumeroBanca")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.ToTable("Bancadas");
});
modelBuilder.Entity("Elecciones.Database.Entities.CandidatoOverride", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int?>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<string>("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<int>("Id")
.HasColumnType("int");
b.Property<string>("Nombre")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("Orden")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("CategoriasElectorales");
});
modelBuilder.Entity("Elecciones.Database.Entities.Configuracion", b =>
{
b.Property<string>("Clave")
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Valor")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Clave");
b.ToTable("Configuraciones");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuento", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("ParticipacionPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<long>("VotosEnBlanco")
.HasColumnType("bigint");
b.Property<decimal>("VotosEnBlancoPorcentaje")
.HasPrecision(18, 4)
.HasColumnType("decimal(18,4)");
b.Property<long>("VotosNulos")
.HasColumnType("bigint");
b.Property<decimal>("VotosNulosPorcentaje")
.HasPrecision(18, 4)
.HasColumnType("decimal(18,4)");
b.Property<long>("VotosRecurridos")
.HasColumnType("bigint");
b.Property<decimal>("VotosRecurridosPorcentaje")
.HasPrecision(18, 4)
.HasColumnType("decimal(18,4)");
b.HasKey("AmbitoGeograficoId", "CategoriaId");
b.ToTable("EstadosRecuentos");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
{
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("CantidadElectores")
.HasColumnType("int");
b.Property<int>("CantidadVotantes")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("MesasEsperadas")
.HasColumnType("int");
b.Property<int>("MesasTotalizadas")
.HasColumnType("int");
b.Property<decimal>("MesasTotalizadasPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.Property<decimal>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int?>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int?>("EleccionId")
.HasColumnType("int");
b.Property<string>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BancadaId")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<string>("FotoUrl")
.HasColumnType("nvarchar(max)");
b.Property<string>("NombreOcupante")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("Periodo")
.HasColumnType("nvarchar(max)");
b.HasKey("Id");
b.HasIndex("BancadaId")
.IsUnique();
b.ToTable("OcupantesBancas");
});
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("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<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("CantidadVotos")
.HasColumnType("bigint");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<decimal>("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<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<long>("Votos")
.HasColumnType("bigint");
b.Property<decimal>("VotosPorcentaje")
.HasPrecision(5, 2)
.HasColumnType("decimal(5,2)");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
.IsUnique();
b.ToTable("ResumenesVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.Telegrama", b =>
{
b.Property<string>("Id")
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<string>("ContenidoBase64")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<int>("EleccionId")
.HasColumnType("int");
b.Property<DateTime>("FechaEscaneo")
.HasColumnType("datetime2");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.HasKey("Id");
b.ToTable("Telegramas");
});
modelBuilder.Entity("Elecciones.Database.Entities.BancaPrevia", 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.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.AmbitoGeografico", "AmbitoGeografico")
.WithMany()
.HasForeignKey("AmbitoGeograficoId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Elecciones.Database.Entities.CategoriaElectoral", "CategoriaElectoral")
.WithMany()
.HasForeignKey("CategoriaId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AmbitoGeografico");
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
}
}
}

View File

@@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Elecciones.Database.Migrations
{
/// <inheritdoc />
public partial class AddCategoriaIdToResumenVoto : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "CategoriaId",
table: "ResumenesVotos",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.CreateIndex(
name: "IX_ResumenesVotos_AmbitoGeograficoId_CategoriaId_AgrupacionPoliticaId",
table: "ResumenesVotos",
columns: new[] { "AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId" },
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_ResumenesVotos_AmbitoGeograficoId_CategoriaId_AgrupacionPoliticaId",
table: "ResumenesVotos");
migrationBuilder.DropColumn(
name: "CategoriaId",
table: "ResumenesVotos");
}
}
}

View File

@@ -535,6 +535,9 @@ namespace Elecciones.Database.Migrations
b.Property<int>("AmbitoGeograficoId") b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int"); .HasColumnType("int");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<int>("EleccionId") b.Property<int>("EleccionId")
.HasColumnType("int"); .HasColumnType("int");
@@ -549,6 +552,9 @@ namespace Elecciones.Database.Migrations
b.HasIndex("AgrupacionPoliticaId"); b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
.IsUnique();
b.ToTable("ResumenesVotos"); b.ToTable("ResumenesVotos");
}); });

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a316e5dd0876a581437b08d9980b658cc714da90")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+84f764390766e1d9716a38464ee478f3d0b75f96")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Database")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -92,7 +92,7 @@ public class ElectoralApiService : IElectoralApiService
return null;*/ return null;*/
} }
public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId) public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId)
{ {
using RateLimitLease lease = await _rateLimiter.AcquireAsync(1); using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
if (!lease.IsAcquired) return null; if (!lease.IsAcquired) return null;
@@ -256,27 +256,6 @@ public class ElectoralApiService : IElectoralApiService
} }
} }
public async Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId)
{
// "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).
// Si no hay fichas, esperará aquí automáticamente hasta que se rellene el cubo.
/*
using RateLimitLease lease = await _rateLimiter.AcquireAsync(1);
// Si se nos concede el permiso para proceder...
if (lease.IsAcquired)
{*/
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/resultados/getResumen?distritoId={distritoId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResumenDto>() : null;
/* }
// Si no se pudo obtener un permiso (ej. la cola está llena), devolvemos null.
return null;*/
}
public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId) public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId)
{ {
// "Pedir una ficha". Este método ahora devuelve un "lease" (permiso). // "Pedir una ficha". Este método ahora devuelve un "lease" (permiso).

View File

@@ -11,11 +11,10 @@ public interface IElectoralApiService
// Métodos para catálogos // Métodos para catálogos
Task<CatalogoDto?> GetCatalogoAmbitosAsync(string authToken, int categoriaId); Task<CatalogoDto?> GetCatalogoAmbitosAsync(string authToken, int categoriaId);
Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId); Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId);
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId); Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string? seccionId, string? municipioId, int categoriaId);
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId); Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string? seccionProvincialId, int categoriaId);
Task<List<string>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null); Task<List<string>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId, int? categoriaId = null);
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId); Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId); Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId);
Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken); Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken);
} }

View File

@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a316e5dd0876a581437b08d9980b658cc714da90")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+84f764390766e1d9716a38464ee478f3d0b75f96")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -473,119 +473,89 @@ public class CriticalDataWorker : BackgroundService
} }
/// <summary> /// <summary>
/// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial. /// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial para CADA categoría.
/// Esta versión actualizada guarda tanto los votos por agrupación (en ResumenesVotos) /// Este método itera sobre todas las provincias y categorías, obteniendo sus resultados consolidados
/// como el estado general del recuento, incluyendo la fecha de totalización (en EstadosRecuentosGenerales), /// y guardándolos en las tablas 'ResumenesVotos' y 'EstadosRecuentosGenerales'.
/// asegurando que toda la operación sea atómica mediante una transacción de base de datos.
/// </summary> /// </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) private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
{ {
try try
{ {
// Creamos un scope de DbContext para esta operación. _logger.LogInformation("Iniciando sondeo de Resúmenes Provinciales...");
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
// Obtenemos el registro de la Provincia (NivelId 10). var provinciasASondear = await dbContext.AmbitosGeograficos
var provincia = await dbContext.AmbitosGeograficos
.AsNoTracking() .AsNoTracking()
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); .Where(a => a.NivelId == 10 && a.DistritoId != null)
.ToListAsync(stoppingToken);
// Si no encontramos el ámbito de la provincia, no podemos continuar. var todasLasCategorias = await dbContext.CategoriasElectorales
if (provincia == null) .AsNoTracking()
.ToListAsync(stoppingToken);
if (!provinciasASondear.Any() || !todasLasCategorias.Any())
{ {
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) para el sondeo de resumen."); _logger.LogWarning("No se encontraron Provincias o Categorías para sondear resúmenes.");
return; return;
} }
// Llamamos a la API para obtener el resumen de datos provincial. foreach (var provincia in provinciasASondear)
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 if (stoppingToken.IsCancellationRequested) break;
// 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. foreach (var categoria in todasLasCategorias)
{
if (stoppingToken.IsCancellationRequested) break;
// Usamos GetResultados sin seccionId/municipioId para obtener el resumen del distrito.
var resultadosDto = await _apiService.GetResultadosAsync(authToken, provincia.DistritoId!, null, null, categoria.Id);
if (resultadosDto?.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
{
// Usamos una transacción para asegurar que el borrado y la inserción sean atómicos.
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken); await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
// --- 1. ACTUALIZAR LA TABLA 'ResumenesVotos' --- // A. Borrar los resúmenes viejos SOLO para esta provincia y categoría.
await dbContext.ResumenesVotos
.Where(rv => rv.AmbitoGeograficoId == provincia.Id && rv.CategoriaId == categoria.Id)
.ExecuteDeleteAsync(stoppingToken);
// Verificamos si la respuesta contiene una lista de votos positivos. // B. Añadir los nuevos resúmenes.
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) foreach (var voto in nuevosVotos)
{ {
dbContext.ResumenesVotos.Add(new ResumenVoto dbContext.ResumenesVotos.Add(new ResumenVoto
{ {
AmbitoGeograficoId = provincia.Id, AmbitoGeograficoId = provincia.Id,
CategoriaId = categoria.Id,
AgrupacionPoliticaId = voto.IdAgrupacion, AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos, Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje VotosPorcentaje = voto.VotosPorcentaje
}); });
} }
}
// --- 2. ACTUALIZAR LA TABLA 'EstadosRecuentosGenerales' --- // C. Guardar los cambios en la tabla ResumenesVotos.
// 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); 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."); // No es necesario actualizar EstadosRecuentosGenerales aquí,
} // ya que el método SondearEstadoRecuentoGeneralAsync se encarga de eso
else // de forma más específica y eficiente.
{
// Si la API no devolvió datos (ej. devuelve null), no hacemos nada en la BD. await transaction.CommitAsync(stoppingToken);
_logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos.");
} }
} // Fin bucle categorías
} // Fin bucle provincias
_logger.LogInformation("Sondeo de Resúmenes Provinciales completado.");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.LogInformation("Sondeo de resumen provincial cancelado."); _logger.LogInformation("Sondeo de resúmenes provinciales cancelado.");
} }
catch (Exception ex) 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 Resúmenes Provinciales.");
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resumen Provincial.");
} }
} }

View File

@@ -195,119 +195,89 @@ public class LowPriorityDataWorker : BackgroundService
} }
/// <summary> /// <summary>
/// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial. /// Obtiene y actualiza el resumen de votos y el estado del recuento a nivel provincial para CADA categoría.
/// Esta versión actualizada guarda tanto los votos por agrupación (en ResumenesVotos) /// Este método itera sobre todas las provincias y categorías, obteniendo sus resultados consolidados
/// como el estado general del recuento, incluyendo la fecha de totalización (en EstadosRecuentosGenerales), /// y guardándolos en las tablas 'ResumenesVotos' y 'EstadosRecuentosGenerales'.
/// asegurando que toda la operación sea atómica mediante una transacción de base de datos.
/// </summary> /// </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) private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
{ {
try try
{ {
// Creamos un scope de DbContext para esta operación. _logger.LogInformation("Iniciando sondeo de Resúmenes Provinciales...");
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>(); var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
// Obtenemos el registro de la Provincia (NivelId 10). var provinciasASondear = await dbContext.AmbitosGeograficos
var provincia = await dbContext.AmbitosGeograficos
.AsNoTracking() .AsNoTracking()
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken); .Where(a => a.NivelId == 10 && a.DistritoId != null)
.ToListAsync(stoppingToken);
// Si no encontramos el ámbito de la provincia, no podemos continuar. var todasLasCategorias = await dbContext.CategoriasElectorales
if (provincia == null) .AsNoTracking()
.ToListAsync(stoppingToken);
if (!provinciasASondear.Any() || !todasLasCategorias.Any())
{ {
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) para el sondeo de resumen."); _logger.LogWarning("No se encontraron Provincias o Categorías para sondear resúmenes.");
return; return;
} }
// Llamamos a la API para obtener el resumen de datos provincial. foreach (var provincia in provinciasASondear)
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 if (stoppingToken.IsCancellationRequested) break;
// 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. foreach (var categoria in todasLasCategorias)
{
if (stoppingToken.IsCancellationRequested) break;
// Usamos GetResultados sin seccionId/municipioId para obtener el resumen del distrito.
var resultadosDto = await _apiService.GetResultadosAsync(authToken, provincia.DistritoId!, null, null, categoria.Id);
if (resultadosDto?.ValoresTotalizadosPositivos is { Count: > 0 } nuevosVotos)
{
// Usamos una transacción para asegurar que el borrado y la inserción sean atómicos.
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken); await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
// --- 1. ACTUALIZAR LA TABLA 'ResumenesVotos' --- // A. Borrar los resúmenes viejos SOLO para esta provincia y categoría.
await dbContext.ResumenesVotos
.Where(rv => rv.AmbitoGeograficoId == provincia.Id && rv.CategoriaId == categoria.Id)
.ExecuteDeleteAsync(stoppingToken);
// Verificamos si la respuesta contiene una lista de votos positivos. // B. Añadir los nuevos resúmenes.
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) foreach (var voto in nuevosVotos)
{ {
dbContext.ResumenesVotos.Add(new ResumenVoto dbContext.ResumenesVotos.Add(new ResumenVoto
{ {
AmbitoGeograficoId = provincia.Id, AmbitoGeograficoId = provincia.Id,
CategoriaId = categoria.Id,
AgrupacionPoliticaId = voto.IdAgrupacion, AgrupacionPoliticaId = voto.IdAgrupacion,
Votos = voto.Votos, Votos = voto.Votos,
VotosPorcentaje = voto.VotosPorcentaje VotosPorcentaje = voto.VotosPorcentaje
}); });
} }
}
// --- 2. ACTUALIZAR LA TABLA 'EstadosRecuentosGenerales' --- // C. Guardar los cambios en la tabla ResumenesVotos.
// 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); 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."); // No es necesario actualizar EstadosRecuentosGenerales aquí,
} // ya que el método SondearEstadoRecuentoGeneralAsync se encarga de eso
else // de forma más específica y eficiente.
{
// Si la API no devolvió datos (ej. devuelve null), no hacemos nada en la BD. await transaction.CommitAsync(stoppingToken);
_logger.LogInformation("Sondeo de Resumen Provincial completado. No se recibieron datos nuevos.");
} }
} // Fin bucle categorías
} // Fin bucle provincias
_logger.LogInformation("Sondeo de Resúmenes Provinciales completado.");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
_logger.LogInformation("Sondeo de resumen provincial cancelado."); _logger.LogInformation("Sondeo de resúmenes provinciales cancelado.");
} }
catch (Exception ex) 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 Resúmenes Provinciales.");
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Resumen Provincial.");
} }
} }

View File

@@ -6,7 +6,7 @@
} }
}, },
"ElectoralApi": { "ElectoralApi": {
"BaseUrl": "https://api.eleccionesbonaerenses.gba.gob.ar", "BaseUrl": "https://api.resultados.gob.ar",
"Username": "30500094156@elecciones2025.onmicrosoft.com", "Username": "30500094156@elecciones2025.onmicrosoft.com",
"Password": "PTP847elec" "Password": "PTP847elec"
}, },

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ce4fc52d4ab234dc5c25703d8945a3f79dccebc4")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+84f764390766e1d9716a38464ee478f3d0b75f96")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]