Fix Campos añadidos

This commit is contained in:
2025-08-16 13:05:44 -03:00
parent 69ddf2b2d2
commit 931d0f4e91
19 changed files with 546 additions and 79 deletions

View File

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

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/FHWuH7ftxc5u992j4YhVijd0fBiiQLWe\u002BV0ZlveX5c="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","jLJZbvuuUsGPkZf3QtwRFQTqJiXdNIIW3av7i2nQ\u002B30=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","BwAWLB3mTJ9blXh5ZTZOjcrdnzPEd9wZsoNfmceZfb8="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","/FHWuH7ftxc5u992j4YhVijd0fBiiQLWe\u002BV0ZlveX5c="],"CachedAssets":{},"CachedCopyCandidates":{}} {"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","t631p0kaOa0gMRIcaPzz1ZVPZ1kuq4pq4kYPWQgoPcM=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","6WTvWQ72AaZBYOVSmaxaci9tc1dW5p7IK9Kscjj2cb0=","jLJZbvuuUsGPkZf3QtwRFQTqJiXdNIIW3av7i2nQ\u002B30=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","BwAWLB3mTJ9blXh5ZTZOjcrdnzPEd9wZsoNfmceZfb8="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1,20 +1,27 @@
// src/Elecciones.Core/DTOs/BancaDto.cs // Archivo: Elecciones.Core/DTOs/BancaDto.cs
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs; namespace Elecciones.Core.DTOs;
public class BancaDto public class BancaDto
{ {
[JsonPropertyName("idAgrupacion")] [JsonPropertyName("idAgrupacion")]
public string IdAgrupacion { get; set; } = null!; public string IdAgrupacion { get; set; } = null!;
[JsonPropertyName("nombreAgrupacion")]
public string NombreAgrupacion { get; set; } = null!; [JsonPropertyName("nombreAgrupacion")]
public string NombreAgrupacion { get; set; } = null!;
[JsonPropertyName("nroBancas")] [JsonPropertyName("idAgrupacionTelegrama")]
public int NroBancas { get; set; } public string? IdAgrupacionTelegrama { get; set; }
public class RepartoBancasDto [JsonPropertyName("nroBancas")]
{ public int NroBancas { get; set; }
}
public class RepartoBancasDto
{
[JsonPropertyName("fechaTotalizacion")]
public string? FechaTotalizacion { get; set; }
[JsonPropertyName("repartoBancas")] [JsonPropertyName("repartoBancas")]
public List<BancaDto> RepartoBancas { get; set; } = []; public List<BancaDto> RepartoBancas { get; set; } = [];
}
} }

View File

@@ -1,4 +1,3 @@
// src/Elecciones.Core/DTOs/CodigoAmbitoDto.cs
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs; namespace Elecciones.Core.DTOs;
@@ -8,7 +7,6 @@ public class CodigoAmbitoDto
[JsonPropertyName("distritoId")] [JsonPropertyName("distritoId")]
public string DistritoId { get; set; } = null!; public string DistritoId { get; set; } = null!;
// Hacemos que los demás IDs acepten nulos para ser más flexibles
[JsonPropertyName("seccionProvincialId")] [JsonPropertyName("seccionProvincialId")]
public string? SeccionProvincialId { get; set; } public string? SeccionProvincialId { get; set; }
@@ -17,4 +15,13 @@ public class CodigoAmbitoDto
[JsonPropertyName("municipioId")] [JsonPropertyName("municipioId")]
public string? MunicipioId { get; set; } public string? MunicipioId { get; set; }
[JsonPropertyName("circuitoId")]
public string? CircuitoId { get; set; }
[JsonPropertyName("establecimientoId")]
public string? EstablecimientoId { get; set; }
[JsonPropertyName("mesaId")]
public string? MesaId { get; set; }
} }

View File

@@ -1,4 +1,4 @@
// src/Elecciones.Core/DTOs/VotosOtrosDto.cs // Archivo: Elecciones.Core/DTOs/VotosOtrosDto.cs
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs; namespace Elecciones.Core.DTOs;
@@ -7,10 +7,19 @@ public class VotosOtrosDto
{ {
[JsonPropertyName("votosNulos")] [JsonPropertyName("votosNulos")]
public long VotosNulos { get; set; } public long VotosNulos { get; set; }
[JsonPropertyName("votosNulosPorcentaje")]
public decimal VotosNulosPorcentaje { get; set; }
[JsonPropertyName("votosEnBlanco")] [JsonPropertyName("votosEnBlanco")]
public long VotosEnBlanco { get; set; } public long VotosEnBlanco { get; set; }
[JsonPropertyName("votosEnBlancoPorcentaje")]
public decimal VotosEnBlancoPorcentaje { get; set; }
[JsonPropertyName("votosRecurridosComandoImpugnados")] [JsonPropertyName("votosRecurridosComandoImpugnados")]
public long VotosRecurridos { get; set; } public long VotosRecurridos { get; set; }
[JsonPropertyName("votosRecurridosComandoImpugnadosPorcentaje")]
public decimal VotosRecurridosPorcentaje { get; set; }
} }

View File

@@ -1,4 +1,4 @@
// src/Elecciones.Core/DTOs/VotosPositivosDto.cs // Archivo: Elecciones.Core/DTOs/VotosPositivosDto.cs
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Elecciones.Core.DTOs; namespace Elecciones.Core.DTOs;
@@ -10,7 +10,13 @@ public class VotosPositivosDto
[JsonPropertyName("nombreAgrupacion")] [JsonPropertyName("nombreAgrupacion")]
public string NombreAgrupacion { get; set; } = null!; public string NombreAgrupacion { get; set; } = null!;
[JsonPropertyName("idAgrupacionTelegrama")]
public string? IdAgrupacionTelegrama { get; set; }
[JsonPropertyName("votos")] [JsonPropertyName("votos")]
public long Votos { get; set; } public long Votos { get; set; }
[JsonPropertyName("votosPorcentaje")]
public decimal VotosPorcentaje { get; set; }
} }

View File

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

View File

@@ -1,4 +1,4 @@
// src/Elecciones.Database/Entities/EstadoRecuento.cs // Archivo: Elecciones.Database/Entities/EstadoRecuento.cs
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@@ -21,4 +21,7 @@ public class EstadoRecuento
public long VotosNulos { get; set; } public long VotosNulos { get; set; }
public long VotosEnBlanco { get; set; } public long VotosEnBlanco { get; set; }
public long VotosRecurridos { get; set; } public long VotosRecurridos { get; set; }
public decimal VotosNulosPorcentaje { get; set; }
public decimal VotosEnBlancoPorcentaje { get; set; }
public decimal VotosRecurridosPorcentaje { get; set; }
} }

View File

@@ -1,4 +1,4 @@
// src/Elecciones.Database/Entities/ResultadoVoto.cs // Archivo: Elecciones.Database/Entities/ResultadoVoto.cs
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@@ -10,7 +10,6 @@ public class ResultadoVoto
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; } public long Id { get; set; }
// Relaciones
public int AmbitoGeograficoId { get; set; } public int AmbitoGeograficoId { get; set; }
[ForeignKey("AmbitoGeograficoId")] [ForeignKey("AmbitoGeograficoId")]
public AmbitoGeografico AmbitoGeografico { get; set; } = null!; public AmbitoGeografico AmbitoGeografico { get; set; } = null!;
@@ -21,4 +20,6 @@ public class ResultadoVoto
// Datos // Datos
public long CantidadVotos { get; set; } public long CantidadVotos { get; set; }
public decimal PorcentajeVotos { get; set; }
} }

View File

@@ -0,0 +1,344 @@
// <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("20250816155525_AddPercentageFieldsToResults")]
partial class AddPercentageFieldsToResults
{
/// <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")
.HasColumnType("decimal(18,2)");
b.Property<long>("VotosNulos")
.HasColumnType("bigint");
b.Property<decimal>("VotosNulosPorcentaje")
.HasColumnType("decimal(18,2)");
b.Property<long>("VotosRecurridos")
.HasColumnType("bigint");
b.Property<decimal>("VotosRecurridosPorcentaje")
.HasColumnType("decimal(18,2)");
b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentos");
});
modelBuilder.Entity("Elecciones.Database.Entities.EstadoRecuentoGeneral", b =>
{
b.Property<int>("AmbitoGeograficoId")
.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");
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")
.HasColumnType("decimal(18,2)");
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.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,62 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Elecciones.Database.Migrations
{
/// <inheritdoc />
public partial class AddPercentageFieldsToResults : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "PorcentajeVotos",
table: "ResultadosVotos",
type: "decimal(18,2)",
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "VotosEnBlancoPorcentaje",
table: "EstadosRecuentos",
type: "decimal(18,2)",
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "VotosNulosPorcentaje",
table: "EstadosRecuentos",
type: "decimal(18,2)",
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "VotosRecurridosPorcentaje",
table: "EstadosRecuentos",
type: "decimal(18,2)",
nullable: false,
defaultValue: 0m);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "PorcentajeVotos",
table: "ResultadosVotos");
migrationBuilder.DropColumn(
name: "VotosEnBlancoPorcentaje",
table: "EstadosRecuentos");
migrationBuilder.DropColumn(
name: "VotosNulosPorcentaje",
table: "EstadosRecuentos");
migrationBuilder.DropColumn(
name: "VotosRecurridosPorcentaje",
table: "EstadosRecuentos");
}
}
}

View File

@@ -129,12 +129,21 @@ namespace Elecciones.Database.Migrations
b.Property<long>("VotosEnBlanco") b.Property<long>("VotosEnBlanco")
.HasColumnType("bigint"); .HasColumnType("bigint");
b.Property<decimal>("VotosEnBlancoPorcentaje")
.HasColumnType("decimal(18,2)");
b.Property<long>("VotosNulos") b.Property<long>("VotosNulos")
.HasColumnType("bigint"); .HasColumnType("bigint");
b.Property<decimal>("VotosNulosPorcentaje")
.HasColumnType("decimal(18,2)");
b.Property<long>("VotosRecurridos") b.Property<long>("VotosRecurridos")
.HasColumnType("bigint"); .HasColumnType("bigint");
b.Property<decimal>("VotosRecurridosPorcentaje")
.HasColumnType("decimal(18,2)");
b.HasKey("AmbitoGeograficoId"); b.HasKey("AmbitoGeograficoId");
b.ToTable("EstadosRecuentos"); b.ToTable("EstadosRecuentos");
@@ -215,6 +224,9 @@ namespace Elecciones.Database.Migrations
b.Property<long>("CantidadVotos") b.Property<long>("CantidadVotos")
.HasColumnType("bigint"); .HasColumnType("bigint");
b.Property<decimal>("PorcentajeVotos")
.HasColumnType("decimal(18,2)");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("AgrupacionPoliticaId"); b.HasIndex("AgrupacionPoliticaId");

View File

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

View File

@@ -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+527839dd6deeb2314e8edc9f7f83edc264a0ea61")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")] [assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -174,116 +174,100 @@ public class Worker : BackgroundService
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. No se puede continuar con la sincronización."); _logger.LogWarning("No se recibieron datos del catálogo de Categorías.");
return; return;
} }
var distinctCategorias = categoriasApi var distinctCategorias = categoriasApi.GroupBy(c => c.CategoriaId).Select(g => g.First()).OrderBy(c => c.Orden).ToList();
.GroupBy(c => c.CategoriaId)
.Select(g => g.First())
.OrderBy(c => c.Orden)
.ToList();
_logger.LogInformation("Se procesarán {count} categorías electorales.", distinctCategorias.Count); _logger.LogInformation("Se procesarán {count} categorías electorales.", distinctCategorias.Count);
var categoriasEnDb = await dbContext.CategoriasElectorales.ToDictionaryAsync(c => c.Id, c => c, stoppingToken); var categoriasEnDb = await dbContext.CategoriasElectorales.ToDictionaryAsync(c => c.Id, c => c, stoppingToken);
foreach (var categoriaDto in distinctCategorias) foreach (var categoriaDto in distinctCategorias)
{ {
if (categoriasEnDb.TryGetValue(categoriaDto.CategoriaId, out var categoriaExistente)) if (!categoriasEnDb.ContainsKey(categoriaDto.CategoriaId))
{ {
// Actualizar por si cambia el nombre o el orden dbContext.CategoriasElectorales.Add(new CategoriaElectoral { Id = categoriaDto.CategoriaId, Nombre = categoriaDto.Nombre, Orden = categoriaDto.Orden });
categoriaExistente.Nombre = categoriaDto.Nombre;
categoriaExistente.Orden = categoriaDto.Orden;
}
else
{
// Añadir nueva categoría
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);
// --- 2. SINCRONIZAR ÁMBITOS Y AGRUPACIONES ---
// Cargamos los catálogos existentes en memoria UNA SOLA VEZ para eficiencia.
var ambitosEnDb = await dbContext.AmbitosGeograficos.ToDictionaryAsync(
a => (a.DistritoId, a.SeccionId, a.MunicipioId, a.CircuitoId, a.EstablecimientoId),
a => a, stoppingToken);
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
// --- 2. ITERAR PARA SINCRONIZAR ÁMBITOS Y AGRUPACIONES ---
foreach (var categoria in distinctCategorias) foreach (var categoria in distinctCategorias)
{ {
if (stoppingToken.IsCancellationRequested) break; if (stoppingToken.IsCancellationRequested) break;
_logger.LogInformation("--- Sincronizando Ámbitos y Agrupaciones 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
// Usamos una clave compuesta para identificar unívocamente cada ámbito geográfico
var ambitosEnDb = await dbContext.AmbitosGeograficos.ToDictionaryAsync(a => (a.DistritoId, a.SeccionId, a.MunicipioId), a => a, stoppingToken);
foreach (var ambitoDto in catalogoDto.Ambitos) foreach (var ambitoDto in catalogoDto.Ambitos)
{ {
var claveUnica = (ambitoDto.CodigoAmbitos.DistritoId, ambitoDto.CodigoAmbitos.SeccionId, ambitoDto.CodigoAmbitos.MunicipioId); // CLAVE ÚNICA CORREGIDA Y MÁS COMPLETA
if (ambitosEnDb.TryGetValue(claveUnica, out var ambitoExistente)) var claveUnica = (
ambitoDto.CodigoAmbitos.DistritoId,
ambitoDto.CodigoAmbitos.SeccionId,
ambitoDto.CodigoAmbitos.MunicipioId,
ambitoDto.CodigoAmbitos.CircuitoId,
ambitoDto.CodigoAmbitos.EstablecimientoId
);
if (!ambitosEnDb.ContainsKey(claveUnica))
{ {
// Actualizar datos descriptivos var nuevoAmbito = new AmbitoGeografico
ambitoExistente.Nombre = ambitoDto.Nombre;
ambitoExistente.NivelId = ambitoDto.NivelId;
}
else
{
// Añadir nuevo ámbito
dbContext.AmbitosGeograficos.Add(new AmbitoGeografico
{ {
Nombre = ambitoDto.Nombre, Nombre = ambitoDto.Nombre,
NivelId = ambitoDto.NivelId, NivelId = ambitoDto.NivelId,
DistritoId = ambitoDto.CodigoAmbitos.DistritoId, DistritoId = ambitoDto.CodigoAmbitos.DistritoId,
SeccionId = ambitoDto.CodigoAmbitos.SeccionId, SeccionId = ambitoDto.CodigoAmbitos.SeccionId,
MunicipioId = ambitoDto.CodigoAmbitos.MunicipioId, MunicipioId = ambitoDto.CodigoAmbitos.MunicipioId,
CircuitoId = ambitoDto.CodigoAmbitos.CircuitoId,
EstablecimientoId = ambitoDto.CodigoAmbitos.EstablecimientoId,
SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId SeccionProvincialId = ambitoDto.CodigoAmbitos.SeccionProvincialId
}); };
dbContext.AmbitosGeograficos.Add(nuevoAmbito);
ambitosEnDb.Add(claveUnica, nuevoAmbito); // Añadimos al diccionario en memoria para evitar duplicados en el mismo ciclo
} }
} }
_logger.LogInformation("Sincronización de Ámbitos Geográficos completada para la categoría actual.");
// SINCRONIZAR AGRUPACIONES // SINCRONIZAR AGRUPACIONES
var provincia = catalogoDto.Ambitos.FirstOrDefault(a => a.NivelId == 10); var provincia = catalogoDto.Ambitos.FirstOrDefault(a => a.NivelId == 10);
if (provincia != null && provincia.CodigoAmbitos.DistritoId != null) if (provincia != null && !string.IsNullOrEmpty(provincia.CodigoAmbitos.DistritoId))
{ {
var agrupacionesApi = await _apiService.GetAgrupacionesAsync(_authToken, provincia.CodigoAmbitos.DistritoId, categoria.CategoriaId); var agrupacionesApi = await _apiService.GetAgrupacionesAsync(_authToken, provincia.CodigoAmbitos.DistritoId, categoria.CategoriaId);
if (agrupacionesApi != null && agrupacionesApi.Any()) if (agrupacionesApi != null && agrupacionesApi.Any())
{ {
var agrupacionesEnDb = await dbContext.AgrupacionesPoliticas.ToDictionaryAsync(a => a.Id, a => a, stoppingToken);
foreach (var agrupacionDto in agrupacionesApi) foreach (var agrupacionDto in agrupacionesApi)
{ {
if (agrupacionesEnDb.TryGetValue(agrupacionDto.IdAgrupacion, out var agrupacionExistente)) if (!agrupacionesEnDb.ContainsKey(agrupacionDto.IdAgrupacion))
{ {
// Actualizar datos descriptivos var nuevaAgrupacion = new AgrupacionPolitica
agrupacionExistente.Nombre = agrupacionDto.NombreAgrupacion;
agrupacionExistente.IdTelegrama = agrupacionDto.IdAgrupacionTelegrama;
}
else
{
// Añadir nueva agrupación
dbContext.AgrupacionesPoliticas.Add(new AgrupacionPolitica
{ {
Id = agrupacionDto.IdAgrupacion, Id = agrupacionDto.IdAgrupacion,
IdTelegrama = agrupacionDto.IdAgrupacionTelegrama, IdTelegrama = agrupacionDto.IdAgrupacionTelegrama,
Nombre = agrupacionDto.NombreAgrupacion Nombre = agrupacionDto.NombreAgrupacion
}); };
dbContext.AgrupacionesPoliticas.Add(nuevaAgrupacion);
agrupacionesEnDb.Add(nuevaAgrupacion.Id, nuevaAgrupacion);
} }
} }
_logger.LogInformation("Sincronización de Agrupaciones Políticas completada para la categoría actual.");
} }
} }
} }
else
{
_logger.LogWarning("No se recibieron datos del catálogo de Ámbitos para la categoría {Id}.", categoria.CategoriaId);
}
} }
// --- 3. GUARDADO FINAL --- // --- 3. GUARDADO FINAL ---
int cambiosGuardados = await dbContext.SaveChangesAsync(stoppingToken); int cambiosGuardados = await dbContext.SaveChangesAsync(stoppingToken);
_logger.LogInformation("{count} cambios totales en los catálogos han sido guardados en la base de datos.", cambiosGuardados); _logger.LogInformation("{count} nuevos registros de catálogo han sido guardados en la base de datos.", cambiosGuardados);
_logger.LogInformation("Sincronización de catálogos maestros finalizada."); _logger.LogInformation("Sincronización de catálogos maestros finalizada.");
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -14,7 +14,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")] [assembly: System.Reflection.AssemblyCompanyAttribute("Elecciones.Worker")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+75ff9d5593b957c5ae0d08223a689a95181172d5")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+69ddf2b2d24d4968c618c6fd9f38c1143625cdcd")]
[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")]

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="GetEFProjectMetadata">
<MSBuild Condition=" '$(TargetFramework)' == '' "
Projects="$(MSBuildProjectFile)"
Targets="GetEFProjectMetadata"
Properties="TargetFramework=$(TargetFrameworks.Split(';')[0]);EFProjectMetadataFile=$(EFProjectMetadataFile)" />
<ItemGroup Condition=" '$(TargetFramework)' != '' ">
<EFProjectMetadata Include="AssemblyName: $(AssemblyName)" />
<EFProjectMetadata Include="Language: $(Language)" />
<EFProjectMetadata Include="OutputPath: $(OutputPath)" />
<EFProjectMetadata Include="Platform: $(Platform)" />
<EFProjectMetadata Include="PlatformTarget: $(PlatformTarget)" />
<EFProjectMetadata Include="ProjectAssetsFile: $(ProjectAssetsFile)" />
<EFProjectMetadata Include="ProjectDir: $(ProjectDir)" />
<EFProjectMetadata Include="RootNamespace: $(RootNamespace)" />
<EFProjectMetadata Include="RuntimeFrameworkVersion: $(RuntimeFrameworkVersion)" />
<EFProjectMetadata Include="TargetFileName: $(TargetFileName)" />
<EFProjectMetadata Include="TargetFrameworkMoniker: $(TargetFrameworkMoniker)" />
<EFProjectMetadata Include="Nullable: $(Nullable)" />
<EFProjectMetadata Include="TargetFramework: $(TargetFramework)" />
<EFProjectMetadata Include="TargetPlatformIdentifier: $(TargetPlatformIdentifier)" />
</ItemGroup>
<WriteLinesToFile Condition=" '$(TargetFramework)' != '' "
File="$(EFProjectMetadataFile)"
Lines="@(EFProjectMetadata)" />
</Target>
</Project>

View File

@@ -24,6 +24,10 @@ services:
- ./.env - ./.env
networks: networks:
- shared-net # Solo necesita acceso a la DB y a la API electoral (internet). - shared-net # Solo necesita acceso a la DB y a la API electoral (internet).
volumes:
# Mapea el directorio /app/logs de dentro del contenedor
# a un directorio llamado 'logs-worker' en la raíz de tu proyecto en la máquina local.
- ./logs-worker:/app/logs
# No se exponen puertos. # No se exponen puertos.
# Servicio del Frontend (servido por Nginx) # Servicio del Frontend (servido por Nginx)