Fix Añade FechaTotalizado en Proyeccion Bancas

This commit is contained in:
2025-08-23 12:54:57 -03:00
parent 303a469c57
commit 13c6accd15
11 changed files with 453 additions and 47 deletions

View File

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

View File

@@ -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=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","RHA7Sn\u002BBPhg/0ggRGaPs3zisENV95kflY7QYn6W0Zh4=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","FRMUpBvV6x/7QVk3Omkda30ILFYfD05f5dk1HcnqQW0="],"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=","\u002B1WVFC1fp/70404bSmvblp/iQiRDfBqAK2zILDLIK04=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","NjY7JdiCu55MOnreQavF53Y5DhSnDcSEpiAcbi6UO60="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -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=","yVoZ4UnBcSOapsJIi046hnn7ylD3jAcEBUxQ\u002Brkvj/4=","RHA7Sn\u002BBPhg/0ggRGaPs3zisENV95kflY7QYn6W0Zh4=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","FRMUpBvV6x/7QVk3Omkda30ILFYfD05f5dk1HcnqQW0="],"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=","\u002B1WVFC1fp/70404bSmvblp/iQiRDfBqAK2zILDLIK04=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","NjY7JdiCu55MOnreQavF53Y5DhSnDcSEpiAcbi6UO60="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

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

View File

@@ -23,4 +23,5 @@ public class ProyeccionBanca
// Cantidad de bancas obtenidas
public int NroBancas { get; set; }
public DateTime FechaTotalizacion { get; set; }
}

View File

@@ -0,0 +1,377 @@
// <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("20250823155053_AddFechaTotalizacionToProyeccionesBancas")]
partial class AddFechaTotalizacionToProyeccionesBancas
{
/// <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>("CategoriaId")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("NroBancas")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
.IsUnique();
b.ToTable("ProyeccionesBancas");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResultadoVoto", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(450)");
b.Property<int>("AmbitoGeograficoId")
.HasColumnType("int");
b.Property<long>("CantidadVotos")
.HasColumnType("bigint");
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<decimal>("PorcentajeVotos")
.HasPrecision(18, 4)
.HasColumnType("decimal(18,4)");
b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId");
b.HasIndex("AmbitoGeograficoId", "CategoriaId", "AgrupacionPoliticaId")
.IsUnique();
b.ToTable("ResultadosVotos");
});
modelBuilder.Entity("Elecciones.Database.Entities.ResumenVoto", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("AgrupacionPoliticaId")
.IsRequired()
.HasColumnType("nvarchar(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
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Elecciones.Database.Migrations
{
/// <inheritdoc />
public partial class AddFechaTotalizacionToProyeccionesBancas : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "FechaTotalizacion",
table: "ProyeccionesBancas",
type: "datetime2",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "FechaTotalizacion",
table: "ProyeccionesBancas");
}
}
}

View File

@@ -208,6 +208,9 @@ namespace Elecciones.Database.Migrations
b.Property<int>("CategoriaId")
.HasColumnType("int");
b.Property<DateTime>("FechaTotalizacion")
.HasColumnType("datetime2");
b.Property<int>("NroBancas")
.HasColumnType("int");

View File

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

View File

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

View File

@@ -239,26 +239,10 @@ public class LowPriorityDataWorker : BackgroundService
/// <summary>
/// Sondea la proyección de bancas. Este método ahora es más completo:
/// 1. Consulta el reparto de bancas a nivel PROVINCIAL para cada categoría.
/// 2. Consulta el reparto de bancas desglosado por SECCIÓN ELECTORAL para cada categoría.
/// </summary>
/// <summary>
/// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral.
/// Esta versión recolecta todos los datos disponibles y los guarda en una única transacción.
/// </summary>
/// <summary>
/// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral.
/// Esta versión incluye logging detallado para diagnosticar problemas de deserialización
/// o respuestas inesperadas de la API, y guarda los datos en una transacción atómica.
/// </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>
/// <summary>
/// Sondea la proyección de bancas a nivel Provincial y por Sección Electoral.
/// Esta versión guarda la CategoriaId junto con cada proyección para poder distinguir
/// entre diferentes elecciones (ej. Senadores vs. Diputados).
/// Esta versión es completamente robusta: maneja respuestas de API vacías o con fechas mal formadas,
/// guarda la CategoriaId y usa una transacción atómica para la escritura en base de datos.
/// </summary>
/// <param name="authToken">El token de autenticación válido para la sesión.</param>
/// <param name="stoppingToken">El token de cancelación para detener la operación.</param>
@@ -266,30 +250,23 @@ public class LowPriorityDataWorker : BackgroundService
{
try
{
// Usamos un scope de DbContext para esta operación, asegurando una conexión limpia.
using var scope = _serviceProvider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<EleccionesDbContext>();
// 1. OBTENER DATOS DE CONFIGURACIÓN DE NUESTRA BD
// Buscamos las categorías que reparten bancas (Senadores y Diputados).
var categoriasDeBancas = await dbContext.CategoriasElectorales
.AsNoTracking()
.Where(c => c.Nombre.Contains("SENADORES") || c.Nombre.Contains("DIPUTADOS"))
.ToListAsync(stoppingToken);
// Obtenemos el registro de la Provincia para hacer la consulta a nivel provincial.
var provincia = await dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.NivelId == 10, stoppingToken);
// Obtenemos todas las Secciones Electorales para hacer las consultas por sección.
var seccionesElectorales = await dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 20 && a.DistritoId != null && a.SeccionProvincialId != null)
.ToListAsync(stoppingToken);
// Si falta alguna configuración base, no podemos continuar.
if (!categoriasDeBancas.Any() || provincia == null)
{
_logger.LogWarning("No se encontraron categorías de bancas o el ámbito provincial en la BD. Omitiendo sondeo de bancas.");
@@ -298,25 +275,32 @@ public class LowPriorityDataWorker : BackgroundService
_logger.LogInformation("Iniciando sondeo de Bancas a nivel Provincial y para {count} Secciones Electorales...", seccionesElectorales.Count);
// 2. RECOLECTAR DATOS DE LA API
// Una lista para acumular todas las proyecciones que encontremos.
var todasLasProyecciones = new List<ProyeccionBanca>();
// Una bandera para saber si hemos recibido al menos una banca con datos.
bool hasReceivedAnyNewData = false;
// Bucle para el nivel Provincial
foreach (var categoria in categoriasDeBancas)
{
if (stoppingToken.IsCancellationRequested) break;
var repartoBancasDto = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, categoria.Id);
var repartoBancas = await _apiService.GetBancasAsync(authToken, provincia.DistritoId!, null, categoria.Id);
if (repartoBancas?.RepartoBancas is { Count: > 0 } bancas)
if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas)
{
_logger.LogInformation("Se encontraron {count} registros de bancas a nivel provincial para la categoría {catId}.", bancas.Count, categoria.Id);
hasReceivedAnyNewData = true;
// --- CORRECCIÓN DE SEGURIDAD: Usar TryParse para la fecha ---
DateTime fechaTotalizacion;
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
{
// Si la fecha es inválida (nula, vacía, mal formada), lo registramos y usamos la hora actual como respaldo.
_logger.LogWarning("No se pudo parsear FechaTotalizacion ('{dateString}') para bancas provinciales. Usando la hora actual.", repartoBancasDto.FechaTotalizacion);
fechaTotalizacion = DateTime.UtcNow;
}
else
{
fechaTotalizacion = parsedDate.ToUniversalTime();
}
foreach (var banca in bancas)
{
todasLasProyecciones.Add(new ProyeccionBanca
@@ -324,7 +308,8 @@ public class LowPriorityDataWorker : BackgroundService
AmbitoGeograficoId = provincia.Id,
AgrupacionPoliticaId = banca.IdAgrupacion,
NroBancas = banca.NroBancas,
CategoriaId = categoria.Id // <-- CORRECCIÓN: Se guarda la CategoriaId
CategoriaId = categoria.Id,
FechaTotalizacion = fechaTotalizacion
});
}
}
@@ -337,11 +322,24 @@ public class LowPriorityDataWorker : BackgroundService
foreach (var categoria in categoriasDeBancas)
{
if (stoppingToken.IsCancellationRequested) break;
var repartoBancas = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionProvincialId!, categoria.Id);
var repartoBancasDto = await _apiService.GetBancasAsync(authToken, seccion.DistritoId!, seccion.SeccionProvincialId!, categoria.Id);
if (repartoBancas?.RepartoBancas is { Count: > 0 } bancas)
if (repartoBancasDto?.RepartoBancas is { Count: > 0 } bancas)
{
hasReceivedAnyNewData = true;
// --- APLICAMOS LA MISMA CORRECCIÓN DE SEGURIDAD AQUÍ ---
DateTime fechaTotalizacion;
if (!DateTime.TryParse(repartoBancasDto.FechaTotalizacion, out var parsedDate))
{
_logger.LogWarning("No se pudo parsear FechaTotalizacion ('{dateString}') para bancas de sección. Usando la hora actual.", repartoBancasDto.FechaTotalizacion);
fechaTotalizacion = DateTime.UtcNow;
}
else
{
fechaTotalizacion = parsedDate.ToUniversalTime();
}
foreach (var banca in bancas)
{
todasLasProyecciones.Add(new ProyeccionBanca
@@ -349,20 +347,17 @@ public class LowPriorityDataWorker : BackgroundService
AmbitoGeograficoId = seccion.Id,
AgrupacionPoliticaId = banca.IdAgrupacion,
NroBancas = banca.NroBancas,
CategoriaId = categoria.Id // <-- CORRECCIÓN: Se guarda la CategoriaId
CategoriaId = categoria.Id,
FechaTotalizacion = fechaTotalizacion
});
}
}
}
}
// 3. GUARDAR DATOS EN LA BASE DE DATOS
// Solo procedemos a escribir en la BD si la bandera se activó.
if (hasReceivedAnyNewData)
{
_logger.LogInformation("Se recibieron datos válidos de bancas. Procediendo a actualizar la base de datos...");
await using var transaction = await dbContext.Database.BeginTransactionAsync(stoppingToken);
await dbContext.Database.ExecuteSqlRawAsync("DELETE FROM ProyeccionesBancas", stoppingToken);