Fix Worker
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -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+18e6e8d3c0a378a172ad8e8afd31109673460717")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5de9d6729c40c2a0d191760aa879b57282ff37e1")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.
|
|||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Diagnostics.Abstractions.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Diagnostics.Abstractions.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Http.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Http.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\buenos-aires-municipios.geojson
|
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.AspNetCore.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.AspNetCore.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Extensions.Hosting.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Serilog.Extensions.Hosting.dll
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Dji\u002Bta/0e7zUKw3oe\u002BriV3kbWxZ93FP2z2QIYsHXTl4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","42z\u002Bw0pajpMLFLNS29VoU/hUn9IzvZ/pVLNadS0rApY=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","ifGdmI/zx2hsc8PYkk8IWTP8aZ9RYmaQbfk383bAiYQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Dji\u002Bta/0e7zUKw3oe\u002BriV3kbWxZ93FP2z2QIYsHXTl4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","GjNU75/P064gZSGHRGFQCzuU1j91Y6ISVIG9G9dpcs0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","Oxph25v5ZpGJ3gq0\u002BdtskQ7kRY5YG8YA8z83gWvNxyw="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Dji\u002Bta/0e7zUKw3oe\u002BriV3kbWxZ93FP2z2QIYsHXTl4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","42z\u002Bw0pajpMLFLNS29VoU/hUn9IzvZ/pVLNadS0rApY=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","ifGdmI/zx2hsc8PYkk8IWTP8aZ9RYmaQbfk383bAiYQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Dji\u002Bta/0e7zUKw3oe\u002BriV3kbWxZ93FP2z2QIYsHXTl4=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","cdgbHR/E4DJsddPc\u002BTpzoUMOVNaFJZm33Pw7AxU9Ees=","4r4JGR4hS5m4rsLfuCSZxzrknTBxKFkLQDXc\u002B2KbqTU=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","GjNU75/P064gZSGHRGFQCzuU1j91Y6ISVIG9G9dpcs0=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","Oxph25v5ZpGJ3gq0\u002BdtskQ7kRY5YG8YA8z83gWvNxyw="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -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+18e6e8d3c0a378a172ad8e8afd31109673460717")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5de9d6729c40c2a0d191760aa879b57282ff37e1")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
|
|||||||
// Precisión para los campos de porcentaje en EstadoRecuento
|
// Precisión para los campos de porcentaje en EstadoRecuento
|
||||||
modelBuilder.Entity<EstadoRecuento>(entity =>
|
modelBuilder.Entity<EstadoRecuento>(entity =>
|
||||||
{
|
{
|
||||||
|
entity.HasKey(e => new { e.AmbitoGeograficoId, e.CategoriaId });
|
||||||
entity.Property(e => e.MesasTotalizadasPorcentaje).HasPrecision(5, 2);
|
entity.Property(e => e.MesasTotalizadasPorcentaje).HasPrecision(5, 2);
|
||||||
entity.Property(e => e.ParticipacionPorcentaje).HasPrecision(5, 2);
|
entity.Property(e => e.ParticipacionPorcentaje).HasPrecision(5, 2);
|
||||||
entity.Property(e => e.VotosNulosPorcentaje).HasPrecision(18, 4);
|
entity.Property(e => e.VotosNulosPorcentaje).HasPrecision(18, 4);
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ public class EstadoRecuento
|
|||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
public int AmbitoGeograficoId { get; set; }
|
public int AmbitoGeograficoId { get; set; }
|
||||||
|
public int CategoriaId { get; set; }
|
||||||
[ForeignKey("AmbitoGeograficoId")]
|
[ForeignKey("AmbitoGeograficoId")]
|
||||||
public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
|
public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
|
||||||
|
|
||||||
public DateTime FechaTotalizacion { get; set; }
|
public DateTime FechaTotalizacion { get; set; }
|
||||||
public int MesasEsperadas { get; set; }
|
public int MesasEsperadas { get; set; }
|
||||||
public int MesasTotalizadas { get; set; }
|
public int MesasTotalizadas { get; set; }
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public class ResultadoVoto
|
|||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
|
||||||
public int AmbitoGeograficoId { get; set; }
|
public int AmbitoGeograficoId { get; set; }
|
||||||
|
public int CategoriaId { get; set; }
|
||||||
|
|
||||||
[ForeignKey("AmbitoGeograficoId")]
|
[ForeignKey("AmbitoGeograficoId")]
|
||||||
public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
|
public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
|
||||||
|
|
||||||
|
|||||||
370
Elecciones-Web/src/Elecciones.Database/Migrations/20250823135433_AddCategoriaIdToResultados.Designer.cs
generated
Normal file
370
Elecciones-Web/src/Elecciones.Database/Migrations/20250823135433_AddCategoriaIdToResultados.Designer.cs
generated
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
// <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("20250823135433_AddCategoriaIdToResultados")]
|
||||||
|
partial class AddCategoriaIdToResultados
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.8")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Elecciones.Database.Entities.AgrupacionPolitica", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("IdTelegrama")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Nombre")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
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.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.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<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>("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.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>("NroBancas")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.HasIndex("AmbitoGeograficoId");
|
||||||
|
|
||||||
|
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<decimal>("PorcentajeVotos")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AgrupacionPoliticaId");
|
||||||
|
|
||||||
|
b.HasIndex("AmbitoGeograficoId", "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(max)");
|
||||||
|
|
||||||
|
b.Property<int>("AmbitoGeograficoId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("Votos")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<decimal>("VotosPorcentaje")
|
||||||
|
.HasPrecision(5, 2)
|
||||||
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
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<DateTime>("FechaEscaneo")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FechaTotalizacion")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Telegramas");
|
||||||
|
});
|
||||||
|
|
||||||
|
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.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");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Elecciones.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddCategoriaIdToResultados : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentos",
|
||||||
|
table: "EstadosRecuentos");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "ResultadosVotos",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentos",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
columns: new[] { "AmbitoGeograficoId", "CategoriaId" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentos",
|
||||||
|
table: "EstadosRecuentos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "ResultadosVotos");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "EstadosRecuentos");
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentos",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
column: "AmbitoGeograficoId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,6 +103,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>("CantidadElectores")
|
b.Property<int>("CantidadElectores")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
@@ -147,7 +150,7 @@ namespace Elecciones.Database.Migrations
|
|||||||
.HasPrecision(18, 4)
|
.HasPrecision(18, 4)
|
||||||
.HasColumnType("decimal(18,4)");
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
b.HasKey("AmbitoGeograficoId");
|
b.HasKey("AmbitoGeograficoId", "CategoriaId");
|
||||||
|
|
||||||
b.ToTable("EstadosRecuentos");
|
b.ToTable("EstadosRecuentos");
|
||||||
});
|
});
|
||||||
@@ -232,6 +235,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Property<long>("CantidadVotos")
|
b.Property<long>("CantidadVotos")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<int>("CategoriaId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<decimal>("PorcentajeVotos")
|
b.Property<decimal>("PorcentajeVotos")
|
||||||
.HasPrecision(18, 4)
|
.HasPrecision(18, 4)
|
||||||
.HasColumnType("decimal(18,4)");
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|||||||
@@ -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+18e6e8d3c0a378a172ad8e8afd31109673460717")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5de9d6729c40c2a0d191760aa879b57282ff37e1")]
|
||||||
[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")]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -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+18e6e8d3c0a378a172ad8e8afd31109673460717")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+5de9d6729c40c2a0d191760aa879b57282ff37e1")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0
|
|||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\refint\Elecciones.Infrastructure.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\refint\Elecciones.Infrastructure.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\Elecciones.Infrastructure.pdb
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\Elecciones.Infrastructure.pdb
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\ref\Elecciones.Infrastructure.dll
|
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\obj\Debug\net9.0\ref\Elecciones.Infrastructure.dll
|
||||||
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Infrastructure\bin\Debug\net9.0\buenos-aires-municipios.geojson
|
|
||||||
|
|||||||
@@ -11,38 +11,35 @@ public class CriticalDataWorker : BackgroundService
|
|||||||
private readonly ILogger<CriticalDataWorker> _logger;
|
private readonly ILogger<CriticalDataWorker> _logger;
|
||||||
private readonly SharedTokenService _tokenService;
|
private readonly SharedTokenService _tokenService;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly IElectoralApiService _apiService; // <-- DEPENDENCIA AÑADIDA
|
private readonly IElectoralApiService _apiService;
|
||||||
|
|
||||||
// Inyectamos IElectoralApiService en el constructor
|
|
||||||
public CriticalDataWorker(
|
public CriticalDataWorker(
|
||||||
ILogger<CriticalDataWorker> logger,
|
ILogger<CriticalDataWorker> logger,
|
||||||
SharedTokenService tokenService,
|
SharedTokenService tokenService,
|
||||||
IServiceProvider serviceProvider,
|
IServiceProvider serviceProvider,
|
||||||
IElectoralApiService apiService) // <-- PARÁMETRO AÑADIDO
|
IElectoralApiService apiService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_tokenService = tokenService;
|
_tokenService = tokenService;
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
_apiService = apiService; // <-- ASIGNACIÓN AÑADIDA
|
_apiService = apiService;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Worker de Datos Críticos iniciado.");
|
_logger.LogInformation("Worker de Datos Críticos iniciado.");
|
||||||
|
|
||||||
// Damos tiempo a la sincronización inicial del otro worker para que se complete.
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken);
|
await Task.Delay(TimeSpan.FromMinutes(2), stoppingToken);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException) { return; } // Salir si la app se apaga durante la espera inicial
|
catch (TaskCanceledException) { return; }
|
||||||
|
|
||||||
int cicloContador = 0;
|
int cicloContador = 0;
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var cicloInicio = DateTime.UtcNow;
|
var cicloInicio = DateTime.UtcNow;
|
||||||
cicloContador++;
|
cicloContador++;
|
||||||
|
|
||||||
_logger.LogInformation("--- Iniciando Ciclo de Datos Críticos #{ciclo} ---", cicloContador);
|
_logger.LogInformation("--- Iniciando Ciclo de Datos Críticos #{ciclo} ---", cicloContador);
|
||||||
|
|
||||||
var authToken = await _tokenService.GetValidAuthTokenAsync(stoppingToken);
|
var authToken = await _tokenService.GetValidAuthTokenAsync(stoppingToken);
|
||||||
@@ -68,153 +65,73 @@ public class CriticalDataWorker : BackgroundService
|
|||||||
{
|
{
|
||||||
await Task.Delay(tiempoDeEspera, stoppingToken);
|
await Task.Delay(tiempoDeEspera, stoppingToken);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException) { break; }
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sondea los resultados electorales para todos los municipios/partidos de forma optimizada.
|
|
||||||
/// Utiliza paralelismo controlado para ejecutar múltiples peticiones a la API simultáneamente
|
|
||||||
/// sin sobrecargar la red, y luego guarda todos los resultados en la base de datos de forma masiva.
|
|
||||||
/// </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 SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
|
private async Task SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// PASO 1: Preparar el DbContext y los datos necesarios.
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
// Obtenemos de nuestra BD local la lista de todos los partidos (NivelId=30) que necesitamos consultar.
|
|
||||||
var municipiosASondear = await dbContext.AmbitosGeograficos
|
var municipiosASondear = await dbContext.AmbitosGeograficos
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
.Where(a => a.NivelId == 30 && a.DistritoId != null && a.SeccionId != null)
|
||||||
.Select(a => new { a.Id, a.Nombre, a.MunicipioId, a.SeccionId, a.DistritoId })
|
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
if (!municipiosASondear.Any())
|
var todasLasCategorias = await dbContext.CategoriasElectorales
|
||||||
{
|
|
||||||
_logger.LogWarning("No se encontraron Partidos (NivelId 30) en la BD para sondear resultados.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtenemos la categoría "CONCEJALES", ya que los resultados municipales aplican a esta.
|
|
||||||
var categoriaConcejales = await dbContext.CategoriasElectorales
|
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.FirstOrDefaultAsync(c => c.Nombre.Contains("CONCEJALES"), stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
if (categoriaConcejales == null)
|
if (!municipiosASondear.Any() || !todasLasCategorias.Any())
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No se encontró la categoría 'CONCEJALES'. Omitiendo sondeo de resultados municipales.");
|
_logger.LogWarning("No se encontraron Partidos (NivelId 30) o Categorías para sondear resultados.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PASO 2: Ejecutar las consultas a la API con paralelismo controlado.
|
_logger.LogInformation("Iniciando sondeo de resultados para {m} municipios y {c} categorías...", municipiosASondear.Count, todasLasCategorias.Count);
|
||||||
|
|
||||||
// Definimos cuántas peticiones queremos que se ejecuten simultáneamente.
|
foreach (var municipio in municipiosASondear)
|
||||||
// Un valor entre 8 y 16 es generalmente seguro y ofrece una gran mejora de velocidad.
|
|
||||||
const int GRADO_DE_PARALELISMO = 3;
|
|
||||||
// Creamos un semáforo que actuará como un "control de acceso" con 10 pases libres.
|
|
||||||
var semaforo = new SemaphoreSlim(GRADO_DE_PARALELISMO);
|
|
||||||
|
|
||||||
// Usamos un ConcurrentDictionary para almacenar los resultados. A diferencia de un Dictionary normal,
|
|
||||||
// este permite que múltiples tareas escriban en él al mismo tiempo sin conflictos.
|
|
||||||
var resultadosPorId = new ConcurrentDictionary<int, Elecciones.Core.DTOs.ResultadosDto>();
|
|
||||||
|
|
||||||
_logger.LogInformation("Iniciando sondeo de resultados para {count} municipios con un paralelismo de {degree}...", municipiosASondear.Count, GRADO_DE_PARALELISMO);
|
|
||||||
|
|
||||||
// Creamos una lista de tareas (Tasks), una por cada municipio a consultar.
|
|
||||||
// El método .Select() no ejecuta las tareas todavía, solo las prepara.
|
|
||||||
var tareas = municipiosASondear.Select(async municipio =>
|
|
||||||
{
|
{
|
||||||
// Cada tarea debe "pedir permiso" al semáforo antes de ejecutarse.
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
// Si ya hay 10 tareas en ejecución, esta línea esperará hasta que una termine.
|
|
||||||
await semaforo.WaitAsync(stoppingToken);
|
var tareasCategoria = todasLasCategorias.Select(async categoria =>
|
||||||
try
|
{
|
||||||
{
|
var resultados = await _apiService.GetResultadosAsync(authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoria.Id);
|
||||||
// Una vez que obtiene el permiso, ejecuta la petición a la API.
|
|
||||||
var resultados = await _apiService.GetResultadosAsync(
|
|
||||||
authToken, municipio.DistritoId!, municipio.SeccionId!, null, categoriaConcejales.Id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Si la API devuelve datos válidos...
|
|
||||||
if (resultados != null)
|
if (resultados != null)
|
||||||
{
|
{
|
||||||
// ...los guardamos en el diccionario concurrente.
|
using var innerScope = _serviceProvider.CreateScope();
|
||||||
resultadosPorId[municipio.Id] = resultados;
|
var innerDbContext = innerScope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
}
|
|
||||||
}
|
// --- LLAMADA CORRECTA ---
|
||||||
finally
|
await GuardarResultadosDeAmbitoAsync(innerDbContext, municipio.Id, categoria.Id, resultados, stoppingToken);
|
||||||
{
|
|
||||||
// ¡CRUCIAL! Liberamos el pase del semáforo, permitiendo que la siguiente
|
|
||||||
// tarea en espera pueda comenzar su ejecución.
|
|
||||||
semaforo.Release();
|
|
||||||
// Añadir un pequeño retraso aleatorio para no parecer un robot
|
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(new Random().Next(50, 251)), stoppingToken);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Ahora sí, ejecutamos todas las tareas preparadas en paralelo y esperamos a que todas terminen.
|
await Task.WhenAll(tareasCategoria);
|
||||||
await Task.WhenAll(tareas);
|
|
||||||
|
|
||||||
// PASO 3: Guardar los resultados en la base de datos.
|
|
||||||
// Solo procedemos si recolectamos al menos un resultado válido.
|
|
||||||
if (resultadosPorId.Any())
|
|
||||||
{
|
|
||||||
// Llamamos a nuestro método de guardado masivo y optimizado, pasándole todos los resultados
|
|
||||||
// recolectados para que los inserte en una única y eficiente transacción.
|
|
||||||
await GuardarResultadosDeMunicipiosAsync(dbContext, resultadosPorId.ToDictionary(kv => kv.Key, kv => kv.Value), stoppingToken);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Capturamos cualquier error inesperado en el proceso para que el worker no se detenga.
|
|
||||||
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados municipales.");
|
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados municipales.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Realiza una operación "Upsert" (Update o Insert) de forma masiva y optimizada.
|
private async Task GuardarResultadosDeAmbitoAsync(
|
||||||
/// Este método es llamado por SondearResultadosMunicipalesAsync.
|
EleccionesDbContext dbContext, int ambitoId, int categoriaId,
|
||||||
/// </summary>
|
Elecciones.Core.DTOs.ResultadosDto resultadosDto, CancellationToken stoppingToken)
|
||||||
private async Task GuardarResultadosDeMunicipiosAsync(
|
|
||||||
EleccionesDbContext dbContext,
|
|
||||||
Dictionary<int, Elecciones.Core.DTOs.ResultadosDto> todosLosResultados,
|
|
||||||
CancellationToken stoppingToken) // <-- PARÁMETRO AÑADIDO
|
|
||||||
{
|
{
|
||||||
// Obtenemos los IDs de todos los ámbitos que vamos a actualizar.
|
var estadoRecuento = await dbContext.EstadosRecuentos.FindAsync(new object[] { ambitoId, categoriaId }, stoppingToken);
|
||||||
var ambitoIds = todosLosResultados.Keys;
|
|
||||||
|
|
||||||
// --- OPTIMIZACIÓN 1: Cargar todos los datos existentes en memoria UNA SOLA VEZ ---
|
if (estadoRecuento == null)
|
||||||
var estadosRecuentoExistentes = await dbContext.EstadosRecuentos
|
|
||||||
.Where(e => ambitoIds.Contains(e.AmbitoGeograficoId))
|
|
||||||
.ToDictionaryAsync(e => e.AmbitoGeograficoId, stoppingToken);
|
|
||||||
|
|
||||||
var resultadosVotosExistentes = await dbContext.ResultadosVotos
|
|
||||||
.Where(rv => ambitoIds.Contains(rv.AmbitoGeograficoId))
|
|
||||||
.GroupBy(rv => rv.AmbitoGeograficoId)
|
|
||||||
.ToDictionaryAsync(g => g.Key, g => g.ToDictionary(item => item.AgrupacionPoliticaId), stoppingToken);
|
|
||||||
|
|
||||||
_logger.LogInformation("Procesando en memoria los resultados de {count} municipios.", todosLosResultados.Count);
|
|
||||||
|
|
||||||
// --- OPTIMIZACIÓN 2: Procesar todo en memoria ---
|
|
||||||
foreach (var kvp in todosLosResultados)
|
|
||||||
{
|
{
|
||||||
var ambitoId = kvp.Key;
|
estadoRecuento = new EstadoRecuento { AmbitoGeograficoId = ambitoId, CategoriaId = categoriaId };
|
||||||
var resultadosDto = kvp.Value;
|
|
||||||
|
|
||||||
// Lógica Upsert para EstadoRecuento
|
|
||||||
if (!estadosRecuentoExistentes.TryGetValue(ambitoId, out var estadoRecuento))
|
|
||||||
{
|
|
||||||
estadoRecuento = new EstadoRecuento { AmbitoGeograficoId = ambitoId };
|
|
||||||
dbContext.EstadosRecuentos.Add(estadoRecuento);
|
dbContext.EstadosRecuentos.Add(estadoRecuento);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mapeo completo de propiedades para EstadoRecuento
|
|
||||||
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultadosDto.FechaTotalizacion).ToUniversalTime();
|
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultadosDto.FechaTotalizacion).ToUniversalTime();
|
||||||
estadoRecuento.MesasEsperadas = resultadosDto.EstadoRecuento.MesasEsperadas;
|
estadoRecuento.MesasEsperadas = resultadosDto.EstadoRecuento.MesasEsperadas;
|
||||||
estadoRecuento.MesasTotalizadas = resultadosDto.EstadoRecuento.MesasTotalizadas;
|
estadoRecuento.MesasTotalizadas = resultadosDto.EstadoRecuento.MesasTotalizadas;
|
||||||
@@ -233,21 +150,21 @@ public class CriticalDataWorker : BackgroundService
|
|||||||
estadoRecuento.VotosRecurridosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosRecurridosPorcentaje;
|
estadoRecuento.VotosRecurridosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosRecurridosPorcentaje;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lógica Upsert para ResultadosVotos
|
|
||||||
var votosDeAmbitoExistentes = resultadosVotosExistentes.GetValueOrDefault(ambitoId);
|
|
||||||
foreach (var votoPositivoDto in resultadosDto.ValoresTotalizadosPositivos)
|
foreach (var votoPositivoDto in resultadosDto.ValoresTotalizadosPositivos)
|
||||||
{
|
{
|
||||||
ResultadoVoto? resultadoVoto = null;
|
var resultadoVoto = await dbContext.ResultadosVotos.FirstOrDefaultAsync(
|
||||||
if (votosDeAmbitoExistentes != null)
|
rv => rv.AmbitoGeograficoId == ambitoId &&
|
||||||
{
|
rv.CategoriaId == categoriaId &&
|
||||||
votosDeAmbitoExistentes.TryGetValue(votoPositivoDto.IdAgrupacion, out resultadoVoto);
|
rv.AgrupacionPoliticaId == votoPositivoDto.IdAgrupacion,
|
||||||
}
|
stoppingToken
|
||||||
|
);
|
||||||
|
|
||||||
if (resultadoVoto == null)
|
if (resultadoVoto == null)
|
||||||
{
|
{
|
||||||
resultadoVoto = new ResultadoVoto
|
resultadoVoto = new ResultadoVoto
|
||||||
{
|
{
|
||||||
AmbitoGeograficoId = ambitoId,
|
AmbitoGeograficoId = ambitoId,
|
||||||
|
CategoriaId = categoriaId,
|
||||||
AgrupacionPoliticaId = votoPositivoDto.IdAgrupacion
|
AgrupacionPoliticaId = votoPositivoDto.IdAgrupacion
|
||||||
};
|
};
|
||||||
dbContext.ResultadosVotos.Add(resultadoVoto);
|
dbContext.ResultadosVotos.Add(resultadoVoto);
|
||||||
@@ -255,13 +172,15 @@ public class CriticalDataWorker : BackgroundService
|
|||||||
resultadoVoto.CantidadVotos = votoPositivoDto.Votos;
|
resultadoVoto.CantidadVotos = votoPositivoDto.Votos;
|
||||||
resultadoVoto.PorcentajeVotos = votoPositivoDto.VotosPorcentaje;
|
resultadoVoto.PorcentajeVotos = votoPositivoDto.VotosPorcentaje;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// --- OPTIMIZACIÓN 3: Guardar todos los cambios en UNA SOLA TRANSACCIÓN ---
|
try
|
||||||
_logger.LogInformation("Guardando todos los cambios de resultados municipales en la base de datos...");
|
{
|
||||||
// Ahora 'stoppingToken' es reconocido aquí
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
_logger.LogInformation("Guardado completado.");
|
}
|
||||||
|
catch (DbUpdateException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "DbUpdateException al guardar resultados para AmbitoId {ambitoId} y CategoriaId {categoriaId}", ambitoId, categoriaId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -420,11 +339,4 @@ public class CriticalDataWorker : BackgroundService
|
|||||||
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Estado Recuento General.");
|
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Estado Recuento General.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pega aquí los métodos:
|
|
||||||
// - SondearResultadosMunicipalesAsync
|
|
||||||
// - GuardarResultadosDeMunicipiosAsync
|
|
||||||
// - SondearResumenProvincialAsync
|
|
||||||
// - SondearEstadoRecuentoGeneralAsync
|
|
||||||
// (Estos métodos necesitan IServiceProvider y SharedTokenService, que ya están inyectados)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user