Retry Con Cambios Importantes.
This commit is contained in:
@@ -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+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+30f1e751b770bf730fc48b1baefb00f560694f35")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","jLJZbvuuUsGPkZf3QtwRFQTqJiXdNIIW3av7i2nQ\u002B30=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","BwAWLB3mTJ9blXh5ZTZOjcrdnzPEd9wZsoNfmceZfb8="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","7rMeSoKKF2\u002B9j5kLZ30FlE98meJ1tr4dywVzhYb49Qg="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","jLJZbvuuUsGPkZf3QtwRFQTqJiXdNIIW3av7i2nQ\u002B30=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","BwAWLB3mTJ9blXh5ZTZOjcrdnzPEd9wZsoNfmceZfb8="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","vAy46VJ9Gp8QqG/Px4J1mj8jL6ws4/A01UKRmMYfYek=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","7rMeSoKKF2\u002B9j5kLZ30FlE98meJ1tr4dywVzhYb49Qg="],"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+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+30f1e751b770bf730fc48b1baefb00f560694f35")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -18,24 +18,35 @@ public class EleccionesDbContext(DbContextOptions<EleccionesDbContext> options)
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
base.OnModelCreating(modelBuilder); // Es buena práctica llamar a la base
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
// Configuraciones adicionales del modelo (índices, etc.) pueden ir aquí.
|
|
||||||
// Por ejemplo, para optimizar las búsquedas.
|
|
||||||
modelBuilder.Entity<ResultadoVoto>()
|
modelBuilder.Entity<ResultadoVoto>()
|
||||||
.HasIndex(r => new { r.AmbitoGeograficoId, r.AgrupacionPoliticaId })
|
.HasIndex(r => new { r.AmbitoGeograficoId, r.AgrupacionPoliticaId })
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
|
// Precisión para los campos de porcentaje en EstadoRecuento
|
||||||
modelBuilder.Entity<EstadoRecuento>(entity =>
|
modelBuilder.Entity<EstadoRecuento>(entity =>
|
||||||
{
|
{
|
||||||
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.VotosEnBlancoPorcentaje).HasPrecision(18, 4);
|
||||||
|
entity.Property(e => e.VotosRecurridosPorcentaje).HasPrecision(18, 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Precisión para el campo de porcentaje en ResultadoVoto
|
||||||
|
modelBuilder.Entity<ResultadoVoto>()
|
||||||
|
.Property(e => e.PorcentajeVotos).HasPrecision(18, 4);
|
||||||
|
|
||||||
modelBuilder.Entity<ResumenVoto>()
|
modelBuilder.Entity<ResumenVoto>()
|
||||||
.Property(e => e.VotosPorcentaje).HasPrecision(5, 2);
|
.Property(e => e.VotosPorcentaje).HasPrecision(5, 2);
|
||||||
|
|
||||||
modelBuilder.Entity<EstadoRecuentoGeneral>(entity =>
|
modelBuilder.Entity<EstadoRecuentoGeneral>(entity =>
|
||||||
{
|
{
|
||||||
|
// Le decimos a EF que la combinación única es (AmbitoGeograficoId, CategoriaId)
|
||||||
|
entity.HasKey(e => new { e.AmbitoGeograficoId, e.CategoriaId });
|
||||||
|
|
||||||
|
// Mantener la configuración de precisión
|
||||||
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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// src/Elecciones.Database/Entities/EstadoRecuentoGeneral.cs
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
@@ -5,14 +6,16 @@ namespace Elecciones.Database.Entities;
|
|||||||
|
|
||||||
public class EstadoRecuentoGeneral
|
public class EstadoRecuentoGeneral
|
||||||
{
|
{
|
||||||
[Key]
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.None)] // Le dice a EF que no genere este valor.
|
|
||||||
public int AmbitoGeograficoId { get; set; }
|
public int AmbitoGeograficoId { get; set; }
|
||||||
|
public int CategoriaId { get; set; }
|
||||||
public int MesasEsperadas { get; set; }
|
public int MesasEsperadas { get; set; }
|
||||||
public int MesasTotalizadas { get; set; }
|
public int MesasTotalizadas { get; set; }
|
||||||
public decimal MesasTotalizadasPorcentaje { get; set; }
|
public decimal MesasTotalizadasPorcentaje { get; set; }
|
||||||
public int CantidadElectores { get; set; }
|
public int CantidadElectores { get; set; }
|
||||||
public int CantidadVotantes { get; set; }
|
public int CantidadVotantes { get; set; }
|
||||||
public decimal ParticipacionPorcentaje { get; set; }
|
public decimal ParticipacionPorcentaje { get; set; }
|
||||||
|
|
||||||
|
// --- Propiedades de navegación (Opcional pero recomendado) ---
|
||||||
|
[ForeignKey("CategoriaId")]
|
||||||
|
public CategoriaElectoral CategoriaElectoral { get; set; } = null!;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,364 @@
|
|||||||
|
// <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("20250817230412_MakeEstadoGeneralKeyComposite")]
|
||||||
|
partial class MakeEstadoGeneralKeyComposite
|
||||||
|
{
|
||||||
|
/// <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>("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");
|
||||||
|
|
||||||
|
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<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,148 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Elecciones.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class MakeEstadoGeneralKeyComposite : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentosGenerales",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "PorcentajeVotos",
|
||||||
|
table: "ResultadosVotos",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,2)");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosRecurridosPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,2)");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosNulosPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,2)");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosEnBlancoPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,4)",
|
||||||
|
precision: 18,
|
||||||
|
scale: 4,
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,2)");
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentosGenerales",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
columns: new[] { "AmbitoGeograficoId", "CategoriaId" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EstadosRecuentosGenerales_CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
column: "CategoriaId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_EstadosRecuentosGenerales_CategoriasElectorales_CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
column: "CategoriaId",
|
||||||
|
principalTable: "CategoriasElectorales",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_EstadosRecuentosGenerales_CategoriasElectorales_CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.DropPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentosGenerales",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EstadosRecuentosGenerales_CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CategoriaId",
|
||||||
|
table: "EstadosRecuentosGenerales");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "PorcentajeVotos",
|
||||||
|
table: "ResultadosVotos",
|
||||||
|
type: "decimal(18,2)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,4)",
|
||||||
|
oldPrecision: 18,
|
||||||
|
oldScale: 4);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosRecurridosPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,2)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,4)",
|
||||||
|
oldPrecision: 18,
|
||||||
|
oldScale: 4);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosNulosPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,2)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,4)",
|
||||||
|
oldPrecision: 18,
|
||||||
|
oldScale: 4);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<decimal>(
|
||||||
|
name: "VotosEnBlancoPorcentaje",
|
||||||
|
table: "EstadosRecuentos",
|
||||||
|
type: "decimal(18,2)",
|
||||||
|
nullable: false,
|
||||||
|
oldClrType: typeof(decimal),
|
||||||
|
oldType: "decimal(18,4)",
|
||||||
|
oldPrecision: 18,
|
||||||
|
oldScale: 4);
|
||||||
|
|
||||||
|
migrationBuilder.AddPrimaryKey(
|
||||||
|
name: "PK_EstadosRecuentosGenerales",
|
||||||
|
table: "EstadosRecuentosGenerales",
|
||||||
|
column: "AmbitoGeograficoId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,19 +130,22 @@ namespace Elecciones.Database.Migrations
|
|||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<decimal>("VotosEnBlancoPorcentaje")
|
b.Property<decimal>("VotosEnBlancoPorcentaje")
|
||||||
.HasColumnType("decimal(18,2)");
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
b.Property<long>("VotosNulos")
|
b.Property<long>("VotosNulos")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<decimal>("VotosNulosPorcentaje")
|
b.Property<decimal>("VotosNulosPorcentaje")
|
||||||
.HasColumnType("decimal(18,2)");
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
b.Property<long>("VotosRecurridos")
|
b.Property<long>("VotosRecurridos")
|
||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<decimal>("VotosRecurridosPorcentaje")
|
b.Property<decimal>("VotosRecurridosPorcentaje")
|
||||||
.HasColumnType("decimal(18,2)");
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
b.HasKey("AmbitoGeograficoId");
|
b.HasKey("AmbitoGeograficoId");
|
||||||
|
|
||||||
@@ -154,6 +157,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");
|
||||||
|
|
||||||
@@ -174,7 +180,9 @@ namespace Elecciones.Database.Migrations
|
|||||||
.HasPrecision(5, 2)
|
.HasPrecision(5, 2)
|
||||||
.HasColumnType("decimal(5,2)");
|
.HasColumnType("decimal(5,2)");
|
||||||
|
|
||||||
b.HasKey("AmbitoGeograficoId");
|
b.HasKey("AmbitoGeograficoId", "CategoriaId");
|
||||||
|
|
||||||
|
b.HasIndex("CategoriaId");
|
||||||
|
|
||||||
b.ToTable("EstadosRecuentosGenerales");
|
b.ToTable("EstadosRecuentosGenerales");
|
||||||
});
|
});
|
||||||
@@ -225,7 +233,8 @@ namespace Elecciones.Database.Migrations
|
|||||||
.HasColumnType("bigint");
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
b.Property<decimal>("PorcentajeVotos")
|
b.Property<decimal>("PorcentajeVotos")
|
||||||
.HasColumnType("decimal(18,2)");
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
@@ -298,6 +307,17 @@ namespace Elecciones.Database.Migrations
|
|||||||
b.Navigation("AmbitoGeografico");
|
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 =>
|
modelBuilder.Entity("Elecciones.Database.Entities.ProyeccionBanca", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
b.HasOne("Elecciones.Database.Entities.AgrupacionPolitica", "AgrupacionPolitica")
|
||||||
|
|||||||
@@ -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+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+30f1e751b770bf730fc48b1baefb00f560694f35")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -53,20 +53,30 @@ public class ElectoralApiService : IElectoralApiService
|
|||||||
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<AgrupacionDto>>() : null;
|
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<List<AgrupacionDto>>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId)
|
public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId)
|
||||||
{
|
{
|
||||||
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
||||||
var requestUri = $"/api/resultados/getResultados?distritoId={distritoId}&seccionId={seccionId}&municipioId={municipioId}&categoriaId=5"; // OJO: La categoría está fija
|
|
||||||
|
// Construimos la URL base
|
||||||
|
var requestUri = $"/api/resultados/getResultados?distritoId={distritoId}&seccionId={seccionId}&categoriaId={categoriaId}";
|
||||||
|
|
||||||
|
// Añadimos el municipioId a la URL SÓLO si no es nulo o vacío
|
||||||
|
if (!string.IsNullOrEmpty(municipioId))
|
||||||
|
{
|
||||||
|
requestUri += $"&municipioId={municipioId}";
|
||||||
|
}
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||||
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResultadosDto>() : null;
|
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResultadosDto>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId)
|
public async Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId)
|
||||||
{
|
{
|
||||||
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
||||||
var requestUri = $"/api/resultados/getBancas?distritoId={distritoId}&seccionId={seccionId}&categoriaId=5"; // OJO: La categoría está fija
|
// Usamos la categoriaId recibida en lugar de una fija
|
||||||
|
var requestUri = $"/api/resultados/getBancas?distritoId={distritoId}&seccionId={seccionId}&categoriaId={categoriaId}";
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||||
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
@@ -103,10 +113,11 @@ public class ElectoralApiService : IElectoralApiService
|
|||||||
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResumenDto>() : null;
|
return response.IsSuccessStatusCode ? await response.Content.ReadFromJsonAsync<ResumenDto>() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId)
|
public async Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId)
|
||||||
{
|
{
|
||||||
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
|
||||||
var requestUri = $"/api/estados/estadoRecuento?distritoId={distritoId}";
|
// La URL ahora usa el parámetro 'categoriaId' que se recibe
|
||||||
|
var requestUri = $"/api/estados/estadoRecuento?distritoId={distritoId}&categoriaId={categoriaId}";
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
|
||||||
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
request.Headers.Add("Authorization", $"Bearer {authToken}");
|
||||||
var response = await client.SendAsync(request);
|
var response = await client.SendAsync(request);
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ 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);
|
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string? municipioId, int categoriaId);
|
||||||
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId);
|
Task<RepartoBancasDto?> GetBancasAsync(string authToken, string distritoId, string seccionId, int categoriaId);
|
||||||
Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId);
|
Task<List<string[]>?> GetTelegramasTotalizadosAsync(string authToken, string distritoId, string seccionId);
|
||||||
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
|
Task<TelegramaFileDto?> GetTelegramaFileAsync(string authToken, string mesaId);
|
||||||
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
|
Task<ResumenDto?> GetResumenAsync(string authToken, string distritoId);
|
||||||
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId);
|
Task<EstadoRecuentoGeneralDto?> GetEstadoRecuentoGeneralAsync(string authToken, string distritoId, int categoriaId);
|
||||||
Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken);
|
Task<List<CategoriaDto>?> GetCategoriasAsync(string authToken);
|
||||||
}
|
}
|
||||||
@@ -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+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+30f1e751b770bf730fc48b1baefb00f560694f35")]
|
||||||
[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")]
|
||||||
|
|||||||
@@ -10,12 +10,15 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Elecciones.Worker;
|
namespace Elecciones.Worker;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Servicio de fondo (BackgroundService) responsable de sincronizar y sondear
|
||||||
|
/// periódicamente los datos de la API electoral.
|
||||||
|
/// </summary>
|
||||||
public class Worker : BackgroundService
|
public class Worker : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly ILogger<Worker> _logger;
|
private readonly ILogger<Worker> _logger;
|
||||||
private readonly IElectoralApiService _apiService;
|
private readonly IElectoralApiService _apiService;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private string? _authToken; // Almacenamos el token para reutilizarlo en el ciclo de vida
|
|
||||||
|
|
||||||
public Worker(ILogger<Worker> logger, IElectoralApiService apiService, IServiceProvider serviceProvider)
|
public Worker(ILogger<Worker> logger, IElectoralApiService apiService, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
@@ -24,154 +27,75 @@ public class Worker : BackgroundService
|
|||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Método principal del worker que se ejecuta en segundo plano.
|
||||||
|
/// </summary>
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Elecciones Worker iniciado a las: {time}", DateTimeOffset.Now);
|
_logger.LogInformation("Elecciones Worker iniciado a las: {time}", DateTimeOffset.Now);
|
||||||
|
|
||||||
await SincronizarCatalogosAsync(stoppingToken);
|
// 1. SINCRONIZACIÓN INICIAL: Se ejecuta una vez al iniciar el worker para
|
||||||
|
// asegurar que la base de datos local tenga todos los catálogos maestros.
|
||||||
|
await SincronizarCatalogosMaestrosAsync(stoppingToken);
|
||||||
|
|
||||||
_logger.LogInformation("-------------------------------------------------");
|
_logger.LogInformation("-------------------------------------------------");
|
||||||
_logger.LogInformation("Iniciando sondeo periódico de resultados...");
|
_logger.LogInformation("Iniciando sondeo periódico de resultados...");
|
||||||
_logger.LogInformation("-------------------------------------------------");
|
_logger.LogInformation("-------------------------------------------------");
|
||||||
|
|
||||||
|
// 2. BUCLE DE SONDEO: Se ejecuta continuamente hasta que se detenga la aplicación.
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
// Ejecutamos todos los sondeos en paralelo para mayor eficiencia
|
var authToken = await _apiService.GetAuthTokenAsync();
|
||||||
|
if (string.IsNullOrEmpty(authToken))
|
||||||
|
{
|
||||||
|
_logger.LogError("CRÍTICO: No se pudo obtener el token de autenticación. Reintentando en 1 minuto...");
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
await Task.WhenAll(
|
await Task.WhenAll(
|
||||||
SondearResultadosAsync(stoppingToken),
|
SondearResultadosMunicipalesAsync(authToken, stoppingToken),
|
||||||
SondearBancasAsync(stoppingToken),
|
SondearProyeccionBancasAsync(authToken, stoppingToken),
|
||||||
SondearTelegramasAsync(stoppingToken),
|
SondearNuevosTelegramasAsync(authToken, stoppingToken),
|
||||||
SondearResumenProvincialAsync(stoppingToken),
|
SondearResumenProvincialAsync(authToken, stoppingToken),
|
||||||
SondearEstadoRecuentoGeneralAsync(stoppingToken)
|
SondearEstadoRecuentoGeneralAsync(authToken, stoppingToken)
|
||||||
);
|
);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Esperamos 1 minuto antes del siguiente ciclo completo de sondeos
|
_logger.LogInformation("Ciclo de sondeo completado. Esperando 5 minutos para el siguiente...");
|
||||||
_logger.LogInformation("Ciclo de sondeo completado. Esperando 5 minuto para el siguiente...");
|
|
||||||
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
|
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException) { break; }
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Elecciones Worker se está deteniendo.");
|
_logger.LogInformation("Elecciones Worker se está deteniendo.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
private async Task ObtenerTokenSiEsNecesario(CancellationToken stoppingToken)
|
/// Descarga y sincroniza los catálogos base (Categorías, Ámbitos, Agrupaciones)
|
||||||
{
|
/// desde la API a la base de datos local.
|
||||||
// En un futuro, se podría añadir lógica para renovar el token solo cuando expire.
|
/// </summary>
|
||||||
// Por ahora, para asegurar que siempre sea válido, lo obtenemos cada vez.
|
private async Task SincronizarCatalogosMaestrosAsync(CancellationToken stoppingToken)
|
||||||
_authToken = await _apiService.GetAuthTokenAsync();
|
|
||||||
if (string.IsNullOrEmpty(_authToken))
|
|
||||||
{
|
|
||||||
_logger.LogError("No se pudo obtener el token, se cancelan las operaciones.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SondearResultadosAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
|
||||||
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
|
|
||||||
|
|
||||||
// Usamos un 'scope' propio para esta operación
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
|
||||||
|
|
||||||
// 1. OBTENEMOS LA LISTA DE TODOS LOS MUNICIPIOS DE NUESTRA BD
|
|
||||||
var municipiosASondear = await dbContext.AmbitosGeograficos
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(a => a.NivelId == 5 && a.MunicipioId != null && a.DistritoId != null && a.SeccionId != null)
|
|
||||||
.Select(a => new { a.Id, a.MunicipioId, a.SeccionId, a.DistritoId })
|
|
||||||
.ToListAsync(stoppingToken);
|
|
||||||
|
|
||||||
_logger.LogInformation("Iniciando sondeo para {count} municipios.", municipiosASondear.Count);
|
|
||||||
|
|
||||||
// 2. RECORREMOS LA LISTA Y PROCESAMOS CADA MUNICIPIO
|
|
||||||
foreach (var municipio in municipiosASondear)
|
|
||||||
{
|
|
||||||
if (stoppingToken.IsCancellationRequested) break;
|
|
||||||
|
|
||||||
_logger.LogInformation("Sondeando resultados para el municipio: {municipioId}", municipio.MunicipioId);
|
|
||||||
|
|
||||||
var resultados = await _apiService.GetResultadosAsync(_authToken, municipio.DistritoId!, municipio.SeccionId!, municipio.MunicipioId!);
|
|
||||||
if (resultados is null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("No se recibieron resultados para el municipio {municipioId}", municipio.MunicipioId);
|
|
||||||
continue; // Saltamos al siguiente municipio
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. GUARDAMOS LOS DATOS (Lógica de Upsert)
|
|
||||||
// La lógica de guardado que ya teníamos funciona perfectamente para cada municipio.
|
|
||||||
// La pasamos a un método separado para mayor claridad.
|
|
||||||
await GuardarResultadosDeAmbitoAsync(dbContext, municipio.Id, resultados, stoppingToken);
|
|
||||||
|
|
||||||
_logger.LogInformation("Resultados para el municipio {municipioId} guardados/actualizados.", municipio.MunicipioId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GuardarResultadosDeAmbitoAsync(EleccionesDbContext dbContext, int ambitoId, Elecciones.Core.DTOs.ResultadosDto resultados, CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
// --- ACTUALIZAR O INSERTAR ESTADO RECUENTO ---
|
|
||||||
var estadoRecuento = await dbContext.EstadosRecuentos.FindAsync(new object[] { ambitoId }, cancellationToken: stoppingToken);
|
|
||||||
if (estadoRecuento == null)
|
|
||||||
{
|
|
||||||
estadoRecuento = new EstadoRecuento { AmbitoGeograficoId = ambitoId };
|
|
||||||
dbContext.EstadosRecuentos.Add(estadoRecuento);
|
|
||||||
}
|
|
||||||
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultados.FechaTotalizacion).ToUniversalTime();
|
|
||||||
estadoRecuento.MesasEsperadas = resultados.EstadoRecuento.MesasEsperadas;
|
|
||||||
estadoRecuento.MesasTotalizadas = resultados.EstadoRecuento.MesasTotalizadas;
|
|
||||||
estadoRecuento.CantidadElectores = resultados.EstadoRecuento.CantidadElectores;
|
|
||||||
estadoRecuento.ParticipacionPorcentaje = resultados.EstadoRecuento.ParticipacionPorcentaje;
|
|
||||||
if (resultados.ValoresTotalizadosOtros != null)
|
|
||||||
{
|
|
||||||
estadoRecuento.VotosEnBlanco = resultados.ValoresTotalizadosOtros.VotosEnBlanco;
|
|
||||||
estadoRecuento.VotosNulos = resultados.ValoresTotalizadosOtros.VotosNulos;
|
|
||||||
estadoRecuento.VotosRecurridos = resultados.ValoresTotalizadosOtros.VotosRecurridos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- ACTUALIZAR O INSERTAR VOTOS POSITIVOS ---
|
|
||||||
foreach (var votoPositivo in resultados.ValoresTotalizadosPositivos)
|
|
||||||
{
|
|
||||||
var resultadoVoto = await dbContext.ResultadosVotos.FirstOrDefaultAsync(
|
|
||||||
rv => rv.AmbitoGeograficoId == ambitoId && rv.AgrupacionPoliticaId == votoPositivo.IdAgrupacion, stoppingToken);
|
|
||||||
|
|
||||||
if (resultadoVoto == null)
|
|
||||||
{
|
|
||||||
resultadoVoto = new ResultadoVoto
|
|
||||||
{
|
|
||||||
AmbitoGeograficoId = ambitoId,
|
|
||||||
AgrupacionPoliticaId = votoPositivo.IdAgrupacion
|
|
||||||
};
|
|
||||||
dbContext.ResultadosVotos.Add(resultadoVoto);
|
|
||||||
}
|
|
||||||
resultadoVoto.CantidadVotos = votoPositivo.Votos;
|
|
||||||
}
|
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SincronizarCatalogosAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Iniciando sincronización de catálogos maestros...");
|
_logger.LogInformation("Iniciando sincronización de catálogos maestros...");
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
var authToken = await _apiService.GetAuthTokenAsync();
|
||||||
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
|
if (string.IsNullOrEmpty(authToken) || stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogError("No se pudo obtener token para la sincronización de catálogos.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
// --- 1. SINCRONIZAR CATEGORÍAS ---
|
// --- 1. SINCRONIZAR CATEGORÍAS ELECTORALES ---
|
||||||
var categoriasApi = await _apiService.GetCategoriasAsync(_authToken);
|
var categoriasApi = await _apiService.GetCategoriasAsync(authToken);
|
||||||
if (categoriasApi is null || !categoriasApi.Any())
|
if (categoriasApi is null || !categoriasApi.Any())
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No se recibieron datos del catálogo de Categorías.");
|
_logger.LogWarning("No se recibieron datos del catálogo de Categorías.");
|
||||||
@@ -189,15 +113,17 @@ public class Worker : BackgroundService
|
|||||||
dbContext.CategoriasElectorales.Add(new CategoriaElectoral { Id = categoriaDto.CategoriaId, Nombre = categoriaDto.Nombre, Orden = categoriaDto.Orden });
|
dbContext.CategoriasElectorales.Add(new CategoriaElectoral { Id = categoriaDto.CategoriaId, Nombre = categoriaDto.Nombre, Orden = categoriaDto.Orden });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Guardamos las categorías primero para asegurar que existan para los siguientes pasos
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
|
|
||||||
|
// --- 2. SINCRONIZAR ÁMBITOS Y AGRUPACIONES POR CADA CATEGORÍA ---
|
||||||
|
|
||||||
// --- 2. SINCRONIZAR ÁMBITOS Y AGRUPACIONES ---
|
// CORRECCIÓN: La siguiente línea faltaba por completo, causando el error CS0103.
|
||||||
// Cargamos los catálogos existentes en memoria UNA SOLA VEZ para eficiencia.
|
// Carga todos los ámbitos existentes en un diccionario para una verificación rápida.
|
||||||
var ambitosEnDb = await dbContext.AmbitosGeograficos.ToDictionaryAsync(
|
var ambitosEnDb = await dbContext.AmbitosGeograficos.ToDictionaryAsync(
|
||||||
a => (a.DistritoId, a.SeccionId, a.MunicipioId, a.CircuitoId, a.EstablecimientoId),
|
a => (a.DistritoId, a.SeccionId, a.MunicipioId, a.CircuitoId, a.EstablecimientoId),
|
||||||
a => a, stoppingToken);
|
a => a,
|
||||||
|
stoppingToken
|
||||||
|
);
|
||||||
|
|
||||||
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
|
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
|
||||||
|
|
||||||
@@ -206,13 +132,12 @@ public class Worker : BackgroundService
|
|||||||
if (stoppingToken.IsCancellationRequested) break;
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
_logger.LogInformation("--- Sincronizando datos para la categoría: {Nombre} (ID: {Id}) ---", categoria.Nombre, categoria.CategoriaId);
|
_logger.LogInformation("--- Sincronizando datos para la categoría: {Nombre} (ID: {Id}) ---", categoria.Nombre, categoria.CategoriaId);
|
||||||
|
|
||||||
var catalogoDto = await _apiService.GetCatalogoAmbitosAsync(_authToken, categoria.CategoriaId);
|
var catalogoDto = await _apiService.GetCatalogoAmbitosAsync(authToken, categoria.CategoriaId);
|
||||||
if (catalogoDto != null)
|
if (catalogoDto != null)
|
||||||
{
|
{
|
||||||
// SINCRONIZAR ÁMBITOS
|
// SINCRONIZAR ÁMBITOS
|
||||||
foreach (var ambitoDto in catalogoDto.Ambitos)
|
foreach (var ambitoDto in catalogoDto.Ambitos)
|
||||||
{
|
{
|
||||||
// CLAVE ÚNICA CORREGIDA Y MÁS COMPLETA
|
|
||||||
var claveUnica = (
|
var claveUnica = (
|
||||||
ambitoDto.CodigoAmbitos.DistritoId,
|
ambitoDto.CodigoAmbitos.DistritoId,
|
||||||
ambitoDto.CodigoAmbitos.SeccionId,
|
ambitoDto.CodigoAmbitos.SeccionId,
|
||||||
@@ -221,6 +146,7 @@ public class Worker : BackgroundService
|
|||||||
ambitoDto.CodigoAmbitos.EstablecimientoId
|
ambitoDto.CodigoAmbitos.EstablecimientoId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Aquí se usaba 'ambitosEnDb' sin haberlo declarado. Ahora funciona.
|
||||||
if (!ambitosEnDb.ContainsKey(claveUnica))
|
if (!ambitosEnDb.ContainsKey(claveUnica))
|
||||||
{
|
{
|
||||||
var nuevoAmbito = new AmbitoGeografico
|
var nuevoAmbito = new AmbitoGeografico
|
||||||
@@ -235,15 +161,18 @@ public class Worker : BackgroundService
|
|||||||
SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId
|
SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId
|
||||||
};
|
};
|
||||||
dbContext.AmbitosGeograficos.Add(nuevoAmbito);
|
dbContext.AmbitosGeograficos.Add(nuevoAmbito);
|
||||||
ambitosEnDb.Add(claveUnica, nuevoAmbito); // Añadimos al diccionario en memoria para evitar duplicados en el mismo ciclo
|
// Se añade al diccionario en memoria para evitar duplicados en el mismo ciclo.
|
||||||
|
ambitosEnDb.Add(claveUnica, nuevoAmbito);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SINCRONIZAR AGRUPACIONES
|
// Lógica para sincronizar AGRUPACIONES POLÍTICAS
|
||||||
var provincia = catalogoDto.Ambitos.FirstOrDefault(a => a.NivelId == 10);
|
var provincia = catalogoDto.Ambitos.FirstOrDefault(a => a.NivelId == 10);
|
||||||
if (provincia != null && !string.IsNullOrEmpty(provincia.CodigoAmbitos.DistritoId))
|
if (provincia != null && !string.IsNullOrEmpty(provincia.CodigoAmbitos.DistritoId))
|
||||||
{
|
{
|
||||||
var agrupacionesApi = await _apiService.GetAgrupacionesAsync(_authToken, provincia.CodigoAmbitos.DistritoId, categoria.CategoriaId);
|
try
|
||||||
|
{
|
||||||
|
var agrupacionesApi = await _apiService.GetAgrupacionesAsync(authToken, provincia.CodigoAmbitos.DistritoId, categoria.CategoriaId);
|
||||||
if (agrupacionesApi != null && agrupacionesApi.Any())
|
if (agrupacionesApi != null && agrupacionesApi.Any())
|
||||||
{
|
{
|
||||||
foreach (var agrupacionDto in agrupacionesApi)
|
foreach (var agrupacionDto in agrupacionesApi)
|
||||||
@@ -262,6 +191,11 @@ public class Worker : BackgroundService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "No se pudieron obtener agrupaciones para la categoría '{catNombre}' ({catId}). Esto puede ser normal si la categoría no aplica a nivel provincial.", categoria.Nombre, categoria.CategoriaId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,20 +210,130 @@ public class Worker : BackgroundService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SondearBancasAsync(CancellationToken stoppingToken)
|
// El resto de los métodos (SondearResultadosMunicipalesAsync, GuardarResultadosDeAmbitoAsync, etc.)
|
||||||
|
// se mantienen como en la versión anterior que te proporcioné. Los incluyo aquí para
|
||||||
|
// que tengas el archivo completo y sin errores.
|
||||||
|
private async Task SondearResultadosMunicipalesAsync(string authToken, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
|
||||||
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
|
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
// Obtenemos las secciones electorales (donde se reparten bancas de diputados)
|
// Buscamos los ámbitos de nivel Municipio/Partido
|
||||||
|
var municipiosASondear = await dbContext.AmbitosGeograficos
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(a => a.NivelId == 5 && a.MunicipioId != null && a.DistritoId != null && a.SeccionId != null)
|
||||||
|
.Select(a => new { a.Id, a.MunicipioId, a.SeccionId, a.DistritoId })
|
||||||
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
if (!municipiosASondear.Any()) return;
|
||||||
|
|
||||||
|
_logger.LogInformation("Iniciando sondeo de resultados para {count} municipios (Partidos)...", municipiosASondear.Count);
|
||||||
|
|
||||||
|
foreach (var municipio in municipiosASondear)
|
||||||
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
var categoriaConcejales = await dbContext.CategoriasElectorales
|
||||||
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(c => c.Nombre.Contains("CONCEJALES"), stoppingToken);
|
||||||
|
|
||||||
|
if (categoriaConcejales != null)
|
||||||
|
{
|
||||||
|
// Para obtener resultados del PARTIDO completo, pasamos 'municipioId' como null.
|
||||||
|
// Usamos el 'seccionId' del registro, que según la aclaración, es el ID del Partido.
|
||||||
|
var resultados = await _apiService.GetResultadosAsync(
|
||||||
|
authToken,
|
||||||
|
municipio.DistritoId!,
|
||||||
|
municipio.SeccionId!,
|
||||||
|
null, // <- AHORA ES NULL
|
||||||
|
categoriaConcejales.Id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resultados != null)
|
||||||
|
{
|
||||||
|
await GuardarResultadosDeAmbitoAsync(dbContext, municipio.Id, resultados, stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Ocurrió un error inesperado durante el sondeo de resultados municipales.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task GuardarResultadosDeAmbitoAsync(EleccionesDbContext dbContext, int ambitoId, Elecciones.Core.DTOs.ResultadosDto resultadosDto, CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
var estadoRecuento = await dbContext.EstadosRecuentos.FindAsync(new object[] { ambitoId }, cancellationToken: stoppingToken);
|
||||||
|
if (estadoRecuento == null)
|
||||||
|
{
|
||||||
|
estadoRecuento = new EstadoRecuento { AmbitoGeograficoId = ambitoId };
|
||||||
|
dbContext.EstadosRecuentos.Add(estadoRecuento);
|
||||||
|
}
|
||||||
|
|
||||||
|
estadoRecuento.FechaTotalizacion = DateTime.Parse(resultadosDto.FechaTotalizacion).ToUniversalTime();
|
||||||
|
estadoRecuento.MesasEsperadas = resultadosDto.EstadoRecuento.MesasEsperadas;
|
||||||
|
estadoRecuento.MesasTotalizadas = resultadosDto.EstadoRecuento.MesasTotalizadas;
|
||||||
|
estadoRecuento.MesasTotalizadasPorcentaje = resultadosDto.EstadoRecuento.MesasTotalizadasPorcentaje;
|
||||||
|
estadoRecuento.CantidadElectores = resultadosDto.EstadoRecuento.CantidadElectores;
|
||||||
|
estadoRecuento.CantidadVotantes = resultadosDto.EstadoRecuento.CantidadVotantes;
|
||||||
|
estadoRecuento.ParticipacionPorcentaje = resultadosDto.EstadoRecuento.ParticipacionPorcentaje;
|
||||||
|
|
||||||
|
if (resultadosDto.ValoresTotalizadosOtros != null)
|
||||||
|
{
|
||||||
|
estadoRecuento.VotosEnBlanco = resultadosDto.ValoresTotalizadosOtros.VotosEnBlanco;
|
||||||
|
estadoRecuento.VotosEnBlancoPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosEnBlancoPorcentaje;
|
||||||
|
estadoRecuento.VotosNulos = resultadosDto.ValoresTotalizadosOtros.VotosNulos;
|
||||||
|
estadoRecuento.VotosNulosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosNulosPorcentaje;
|
||||||
|
estadoRecuento.VotosRecurridos = resultadosDto.ValoresTotalizadosOtros.VotosRecurridos;
|
||||||
|
estadoRecuento.VotosRecurridosPorcentaje = resultadosDto.ValoresTotalizadosOtros.VotosRecurridosPorcentaje;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var votoPositivoDto in resultadosDto.ValoresTotalizadosPositivos)
|
||||||
|
{
|
||||||
|
var resultadoVoto = await dbContext.ResultadosVotos.FirstOrDefaultAsync(
|
||||||
|
rv => rv.AmbitoGeograficoId == ambitoId && rv.AgrupacionPoliticaId == votoPositivoDto.IdAgrupacion,
|
||||||
|
stoppingToken
|
||||||
|
);
|
||||||
|
|
||||||
|
if (resultadoVoto == null)
|
||||||
|
{
|
||||||
|
resultadoVoto = new ResultadoVoto
|
||||||
|
{
|
||||||
|
AmbitoGeograficoId = ambitoId,
|
||||||
|
AgrupacionPoliticaId = votoPositivoDto.IdAgrupacion
|
||||||
|
};
|
||||||
|
dbContext.ResultadosVotos.Add(resultadoVoto);
|
||||||
|
}
|
||||||
|
resultadoVoto.CantidadVotos = votoPositivoDto.Votos;
|
||||||
|
resultadoVoto.PorcentajeVotos = votoPositivoDto.VotosPorcentaje;
|
||||||
|
}
|
||||||
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SondearProyeccionBancasAsync(string authToken, CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
|
var categoriasDeBancas = await dbContext.CategoriasElectorales
|
||||||
|
.AsNoTracking()
|
||||||
|
.Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS"))
|
||||||
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
|
if (!categoriasDeBancas.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No se encontraron categorías para 'Senadores' o 'Diputados' en la BD. Omitiendo sondeo de bancas.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var secciones = await dbContext.AmbitosGeograficos
|
var secciones = await dbContext.AmbitosGeograficos
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null) // Nivel 4 = Sección Electoral
|
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
if (!secciones.Any())
|
if (!secciones.Any())
|
||||||
@@ -298,30 +342,25 @@ public class Worker : BackgroundService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Iniciando sondeo de Bancas para {count} secciones...", secciones.Count);
|
_logger.LogInformation("Iniciando sondeo de Bancas para {count} secciones y {catCount} categorías...", secciones.Count, categoriasDeBancas.Count);
|
||||||
|
|
||||||
// Esta bandera nos asegura que solo borramos la tabla una vez y solo si hay datos nuevos.
|
|
||||||
bool hasReceivedNewData = false;
|
|
||||||
|
|
||||||
|
bool hasReceivedAnyNewData = false;
|
||||||
foreach (var seccion in secciones)
|
foreach (var seccion in secciones)
|
||||||
{
|
{
|
||||||
if (stoppingToken.IsCancellationRequested) break;
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
foreach (var categoria in categoriasDeBancas)
|
||||||
var repartoBancas = await _apiService.GetBancasAsync(_authToken, seccion.DistritoId!, seccion.SeccionId!);
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
// Verificamos que la respuesta no sea nula y que la lista de bancas contenga al menos un elemento.
|
var repartoBancas = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionId!, categoria.Id);
|
||||||
if (repartoBancas?.RepartoBancas is { Count: > 0 })
|
if (repartoBancas?.RepartoBancas is { Count: > 0 })
|
||||||
{
|
{
|
||||||
// Si esta es la primera vez en este ciclo de sondeo que recibimos datos válidos,
|
if (!hasReceivedAnyNewData)
|
||||||
// borramos todos los datos viejos de la tabla.
|
|
||||||
if (!hasReceivedNewData)
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Se recibieron nuevos datos de bancas. Limpiando tabla de proyecciones...");
|
_logger.LogInformation("Se recibieron nuevos datos de bancas. Limpiando la tabla de proyecciones para evitar duplicados...");
|
||||||
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);
|
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);
|
||||||
hasReceivedNewData = true; // Marcamos que ya hemos limpiado la tabla.
|
hasReceivedAnyNewData = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Procedemos a añadir las nuevas proyecciones a la sesión de EF Core.
|
|
||||||
foreach (var banca in repartoBancas.RepartoBancas)
|
foreach (var banca in repartoBancas.RepartoBancas)
|
||||||
{
|
{
|
||||||
var nuevaProyeccion = new ProyeccionBanca
|
var nuevaProyeccion = new ProyeccionBanca
|
||||||
@@ -333,18 +372,13 @@ public class Worker : BackgroundService
|
|||||||
await dbContext.ProyeccionesBancas.AddAsync(nuevaProyeccion, stoppingToken);
|
await dbContext.ProyeccionesBancas.AddAsync(nuevaProyeccion, stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("No se recibieron datos de bancas para la sección {seccionId}.", seccion.SeccionId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si hemos añadido alguna entidad nueva (es decir, hasReceivedNewData es true),
|
if (hasReceivedAnyNewData)
|
||||||
// guardamos todos los cambios en la base de datos.
|
|
||||||
if (hasReceivedNewData)
|
|
||||||
{
|
{
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
_logger.LogInformation("Sondeo de Bancas completado. La tabla de proyecciones ha sido actualizada.");
|
_logger.LogInformation("Sondeo de Bancas completado. La tabla de proyecciones ha sido actualizada con nuevos datos.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -353,18 +387,14 @@ public class Worker : BackgroundService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Ocurrió un error en el sondeo de Bancas.");
|
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Bancas.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SondearTelegramasAsync(CancellationToken stoppingToken)
|
private async Task SondearNuevosTelegramasAsync(string authToken, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
|
||||||
if (string.IsNullOrEmpty(_authToken) || stoppingToken.IsCancellationRequested) return;
|
|
||||||
|
|
||||||
// --- CADA SONDEO USA SU PROPIO DBCONTEXT FRESCO ---
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
@@ -373,7 +403,11 @@ public class Worker : BackgroundService
|
|||||||
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null)
|
.Where(a => a.NivelId == 4 && a.DistritoId != null && a.SeccionId != null)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
if (!secciones.Any()) return;
|
if (!secciones.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No hay Secciones Electorales en la BD para sondear telegramas.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Iniciando sondeo de Telegramas nuevos...");
|
_logger.LogInformation("Iniciando sondeo de Telegramas nuevos...");
|
||||||
|
|
||||||
@@ -381,78 +415,63 @@ public class Worker : BackgroundService
|
|||||||
{
|
{
|
||||||
if (stoppingToken.IsCancellationRequested) break;
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(_authToken, seccion.DistritoId!, seccion.SeccionId!);
|
var listaTelegramasApi = await _apiService.GetTelegramasTotalizadosAsync(authToken, seccion.DistritoId!, seccion.SeccionId!);
|
||||||
if (listaTelegramasApi is { Count: > 0 })
|
if (listaTelegramasApi is { Count: > 0 })
|
||||||
{
|
{
|
||||||
var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList();
|
var idsDeApi = listaTelegramasApi.Select(t => t[0]).Distinct().ToList();
|
||||||
|
|
||||||
// --- LÓGICA DE DUPLICADOS ---
|
|
||||||
// Consultamos a la base de datos por los IDs que la API nos acaba de dar
|
|
||||||
var idsYaEnDb = await dbContext.Telegramas
|
var idsYaEnDb = await dbContext.Telegramas
|
||||||
.Where(t => idsDeApi.Contains(t.Id))
|
.Where(t => idsDeApi.Contains(t.Id))
|
||||||
.Select(t => t.Id)
|
.Select(t => t.Id)
|
||||||
.ToListAsync(stoppingToken);
|
.ToListAsync(stoppingToken);
|
||||||
|
|
||||||
// Comparamos las dos listas para encontrar los que realmente son nuevos
|
|
||||||
var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList();
|
var nuevosTelegramasIds = idsDeApi.Except(idsYaEnDb).ToList();
|
||||||
|
|
||||||
if (!nuevosTelegramasIds.Any())
|
if (!nuevosTelegramasIds.Any())
|
||||||
{
|
{
|
||||||
_logger.LogInformation("No hay telegramas nuevos para la sección {seccionId}.", seccion.SeccionId);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Se encontraron {count} telegramas nuevos en la sección {seccionId}. Descargando...", nuevosTelegramasIds.Count, seccion.SeccionId);
|
_logger.LogInformation("Se encontraron {count} telegramas nuevos en la sección {seccionId}. Descargando...", nuevosTelegramasIds.Count, seccion.SeccionId);
|
||||||
|
|
||||||
foreach (var mesaId in nuevosTelegramasIds)
|
foreach (var mesaId in nuevosTelegramasIds)
|
||||||
{
|
{
|
||||||
if (stoppingToken.IsCancellationRequested) break;
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
var telegramaFile = await _apiService.GetTelegramaFileAsync(authToken, mesaId);
|
||||||
var telegramaFile = await _apiService.GetTelegramaFileAsync(_authToken, mesaId);
|
|
||||||
if (telegramaFile != null)
|
if (telegramaFile != null)
|
||||||
{
|
{
|
||||||
var nuevoTelegrama = new Telegrama
|
var nuevoTelegrama = new Telegrama
|
||||||
{
|
{
|
||||||
Id = telegramaFile.NombreArchivo,
|
Id = telegramaFile.NombreArchivo,
|
||||||
AmbitoGeograficoId = seccion.Id, // Lo asociamos a la sección por simplicidad
|
AmbitoGeograficoId = seccion.Id,
|
||||||
ContenidoBase64 = telegramaFile.Imagen,
|
ContenidoBase64 = telegramaFile.Imagen,
|
||||||
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
|
FechaEscaneo = DateTime.Parse(telegramaFile.FechaEscaneo).ToUniversalTime(),
|
||||||
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
|
FechaTotalizacion = DateTime.Parse(telegramaFile.FechaTotalizacion).ToUniversalTime()
|
||||||
};
|
};
|
||||||
// Como estamos en un DbContext fresco, AddAsync no dará conflicto
|
|
||||||
await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
|
await dbContext.Telegramas.AddAsync(nuevoTelegrama, stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Guardamos los cambios al final de cada sección procesada
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Sondeo de Telegramas completado.");
|
_logger.LogInformation("Sondeo de Telegramas completado.");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Ocurrió un error en el sondeo de Telegramas.");
|
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Telegramas.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SondearResumenProvincialAsync(CancellationToken stoppingToken)
|
private async Task SondearResumenProvincialAsync(string authToken, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
|
||||||
if (string.IsNullOrEmpty(_authToken)) return;
|
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
||||||
if (provincia == null) return;
|
if (provincia == null) return;
|
||||||
|
|
||||||
var resumen = await _apiService.GetResumenAsync(_authToken, provincia.DistritoId!);
|
var resumen = await _apiService.GetResumenAsync(authToken, provincia.DistritoId!);
|
||||||
if (resumen?.ValoresTotalizadosPositivos is { Count: > 0 })
|
if (resumen?.ValoresTotalizadosPositivos is { Count: > 0 })
|
||||||
{
|
{
|
||||||
// Estrategia: Reemplazo completo
|
|
||||||
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
|
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ResumenesVotos", stoppingToken);
|
||||||
foreach (var voto in resumen.ValoresTotalizadosPositivos)
|
foreach (var voto in resumen.ValoresTotalizadosPositivos)
|
||||||
{
|
{
|
||||||
@@ -474,27 +493,51 @@ public class Worker : BackgroundService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SondearEstadoRecuentoGeneralAsync(CancellationToken stoppingToken)
|
private async Task SondearEstadoRecuentoGeneralAsync(string authToken, CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await ObtenerTokenSiEsNecesario(stoppingToken);
|
|
||||||
if (string.IsNullOrEmpty(_authToken)) return;
|
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
|
||||||
|
|
||||||
var provincia = await dbContext.AmbitosGeograficos.AsNoTracking().FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
var provincia = await dbContext.AmbitosGeograficos
|
||||||
if (provincia == null) return;
|
.AsNoTracking()
|
||||||
|
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
|
||||||
|
if (provincia == null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No se encontró el ámbito 'Provincia' (NivelId 10) en la BD. Omitiendo sondeo de estado general.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var estadoDto = await _apiService.GetEstadoRecuentoGeneralAsync(_authToken, provincia.DistritoId!);
|
var categoriasParaSondear = await dbContext.CategoriasElectorales
|
||||||
|
.AsNoTracking()
|
||||||
|
.ToListAsync(stoppingToken);
|
||||||
|
if (!categoriasParaSondear.Any())
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No hay categorías en la BD para sondear el estado general del recuento.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_logger.LogInformation("Iniciando sondeo de Estado Recuento General para {count} categorías...", categoriasParaSondear.Count);
|
||||||
|
|
||||||
|
foreach (var categoria in categoriasParaSondear)
|
||||||
|
{
|
||||||
|
if (stoppingToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
var estadoDto = await _apiService.GetEstadoRecuentoGeneralAsync(authToken, provincia.DistritoId!, categoria.Id);
|
||||||
if (estadoDto != null)
|
if (estadoDto != null)
|
||||||
{
|
{
|
||||||
// Estrategia: Upsert
|
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(
|
||||||
var registroDb = await dbContext.EstadosRecuentosGenerales.FindAsync(provincia.Id);
|
new object[] { provincia.Id, categoria.Id },
|
||||||
|
cancellationToken: stoppingToken
|
||||||
|
);
|
||||||
|
|
||||||
if (registroDb == null)
|
if (registroDb == null)
|
||||||
{
|
{
|
||||||
registroDb = new EstadoRecuentoGeneral { AmbitoGeograficoId = provincia.Id };
|
registroDb = new EstadoRecuentoGeneral
|
||||||
|
{
|
||||||
|
AmbitoGeograficoId = provincia.Id,
|
||||||
|
CategoriaId = categoria.Id
|
||||||
|
};
|
||||||
dbContext.EstadosRecuentosGenerales.Add(registroDb);
|
dbContext.EstadosRecuentosGenerales.Add(registroDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,14 +547,14 @@ public class Worker : BackgroundService
|
|||||||
registroDb.CantidadElectores = estadoDto.CantidadElectores;
|
registroDb.CantidadElectores = estadoDto.CantidadElectores;
|
||||||
registroDb.CantidadVotantes = estadoDto.CantidadVotantes;
|
registroDb.CantidadVotantes = estadoDto.CantidadVotantes;
|
||||||
registroDb.ParticipacionPorcentaje = estadoDto.ParticipacionPorcentaje;
|
registroDb.ParticipacionPorcentaje = estadoDto.ParticipacionPorcentaje;
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync(stoppingToken);
|
|
||||||
_logger.LogInformation("Sondeo de Estado Recuento General completado.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await dbContext.SaveChangesAsync(stoppingToken);
|
||||||
|
_logger.LogInformation("Sondeo de Estado Recuento General completado para todas las categorías.");
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Ocurrió un error en el sondeo de Estado Recuento General.");
|
_logger.LogError(ex, "Ocurrió un error CRÍTICO en el sondeo de Estado Recuento General.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+30f1e751b770bf730fc48b1baefb00f560694f35")]
|
||||||
[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")]
|
||||||
|
|||||||
Reference in New Issue
Block a user