Feat: Implementar API de resultados y widget de prueba dinámico con selector

API (Backend):
Se crea el endpoint GET /api/resultados/municipio/{id} para servir los resultados detallados de un municipio específico.
Se añade el endpoint GET /api/catalogos/municipios para poblar selectores en el frontend.
Se incluye un endpoint simulado GET /api/resultados/provincia/{id} para facilitar el desarrollo futuro del frontend.
Worker (Servicio de Ingesta):
La lógica de sondeo se ha hecho dinámica. Ahora consulta todos los municipios presentes en la base de datos en lugar de uno solo.
El servicio falso (FakeElectoralApiService) se ha mejorado para generar datos aleatorios para cualquier municipio solicitado.
Frontend (React):
Se crea el componente <MunicipioSelector /> que se carga con datos desde la nueva API de catálogos.
Se integra el selector en la página principal, permitiendo al usuario elegir un municipio.
El componente <MunicipioWidget /> ahora recibe el ID del municipio como una prop y muestra los datos del municipio seleccionado, actualizándose en tiempo real.
Configuración:
Se ajusta la política de CORS en la API para permitir peticiones desde el servidor de desarrollo de Vite (localhost:5173), solucionando errores de conexión en el entorno local.
This commit is contained in:
2025-08-14 15:27:45 -03:00
parent b90baadeed
commit 1d58023113
70 changed files with 5563 additions and 89 deletions

View File

@@ -0,0 +1,38 @@
using Elecciones.Core.DTOs.ApiResponses;
using Elecciones.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elecciones.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class CatalogosController : ControllerBase
{
private readonly EleccionesDbContext _dbContext;
public CatalogosController(EleccionesDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpGet("municipios")]
public async Task<IActionResult> GetMunicipios()
{
// El NivelId 5 corresponde a "Municipio" según los datos que hemos visto.
var municipios = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.Where(a => a.NivelId == 5 && a.MunicipioId != null)
.Select(a => new MunicipioSimpleDto
{
Id = a.MunicipioId!,
Nombre = a.Nombre
})
.OrderBy(m => m.Nombre)
.ToListAsync();
return Ok(municipios);
}
}

View File

@@ -0,0 +1,116 @@
// src/Elecciones.Api/Controllers/ResultadosController.cs
using Elecciones.Core.DTOs.ApiResponses;
using Elecciones.Database;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Elecciones.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ResultadosController : ControllerBase
{
private readonly EleccionesDbContext _dbContext;
private readonly ILogger<ResultadosController> _logger;
public ResultadosController(EleccionesDbContext dbContext, ILogger<ResultadosController> logger)
{
_dbContext = dbContext;
_logger = logger;
}
[HttpGet("municipio/{municipioId}")]
public async Task<IActionResult> GetResultadosPorMunicipio(string municipioId)
{
// 1. Buscamos el ámbito geográfico correspondiente al municipio
var ambito = await _dbContext.AmbitosGeograficos
.AsNoTracking()
.FirstOrDefaultAsync(a => a.MunicipioId == municipioId);
if (ambito == null)
{
return NotFound(new { message = $"No se encontró el municipio con ID {municipioId}" });
}
// 2. Buscamos el estado del recuento para ese ámbito
var estadoRecuento = await _dbContext.EstadosRecuentos
.AsNoTracking()
.FirstOrDefaultAsync(e => e.AmbitoGeograficoId == ambito.Id);
if (estadoRecuento == null)
{
return NotFound(new { message = $"No se han encontrado resultados para el municipio {ambito.Nombre}" });
}
// 3. Buscamos todos los votos para ese ámbito, incluyendo el nombre de la agrupación
var resultadosVotos = await _dbContext.ResultadosVotos
.AsNoTracking()
.Include(rv => rv.AgrupacionPolitica) // ¡Crucial para obtener el nombre del partido!
.Where(rv => rv.AmbitoGeograficoId == ambito.Id)
.ToListAsync();
// 4. Calculamos el total de votos positivos para el porcentaje
long totalVotosPositivos = resultadosVotos.Sum(r => r.CantidadVotos);
// 5. Mapeamos todo a nuestro DTO de respuesta
var respuestaDto = new MunicipioResultadosDto
{
MunicipioNombre = ambito.Nombre,
UltimaActualizacion = estadoRecuento.FechaTotalizacion,
PorcentajeEscrutado = estadoRecuento.MesasTotalizadas * 100.0m / (estadoRecuento.MesasEsperadas > 0 ? estadoRecuento.MesasEsperadas : 1),
PorcentajeParticipacion = estadoRecuento.ParticipacionPorcentaje,
Resultados = resultadosVotos.Select(rv => new AgrupacionResultadoDto
{
Nombre = rv.AgrupacionPolitica.Nombre,
Votos = rv.CantidadVotos,
Porcentaje = totalVotosPositivos > 0 ? (rv.CantidadVotos * 100.0m / totalVotosPositivos) : 0
}).OrderByDescending(r => r.Votos).ToList(),
VotosAdicionales = new VotosAdicionalesDto
{
EnBlanco = estadoRecuento.VotosEnBlanco,
Nulos = estadoRecuento.VotosNulos,
Recurridos = estadoRecuento.VotosRecurridos
}
};
// Devolvemos el resultado
return Ok(respuestaDto);
}
[HttpGet("provincia/{distritoId}")]
public async Task<IActionResult> GetResultadosProvinciales(string distritoId)
{
// TODO: Esta lógica debe ser reemplazada para leer datos reales de la BD
// cuando el worker comience a ingestar los totales a nivel provincial.
// Por ahora, devolvemos datos simulados para permitir el desarrollo del frontend.
var ambito = await _dbContext.AmbitosGeograficos.AsNoTracking()
.FirstOrDefaultAsync(a => a.DistritoId == distritoId && a.NivelId == 10);
if (ambito == null)
{
return NotFound(new { message = "No se encontró la provincia" });
}
// Simulación
var random = new Random();
var respuestaSimulada = new ResumenProvincialDto
{
ProvinciaNombre = ambito.Nombre,
UltimaActualizacion = DateTime.UtcNow,
PorcentajeEscrutado = 78.45m,
PorcentajeParticipacion = 65.12m,
Resultados =
[
new() { Nombre = "ALIANZA POR EL FUTURO", Votos = 2500000 + random.Next(1, 1000), Porcentaje = 45.12m },
new() { Nombre = "FRENTE DE AVANZADA", Votos = 2100000 + random.Next(1, 1000), Porcentaje = 38.78m },
new() { Nombre = "UNION POPULAR", Votos = 800000 + random.Next(1, 1000), Porcentaje = 14.10m },
],
VotosAdicionales = new VotosAdicionalesDto { EnBlanco = 150000, Nulos = 80000, Recurridos = 1200 }
};
return Ok(await Task.FromResult(respuestaSimulada));
}
}

View File

@@ -18,7 +18,7 @@ builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("http://localhost:8600", "http://www.eldia.com", "http://elecciones2025.eldia.com") // Añade aquí los dominios que necesites
policy.WithOrigins("http://localhost:5173", "http://localhost:8600", "http://www.eldia.com", "http://elecciones2025.eldia.com") // Añade aquí los dominios que necesites
.AllowAnyHeader()
.AllowAnyMethod();
});

View File

@@ -540,6 +540,18 @@
}
}
},
"Microsoft.Extensions.Configuration/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Configuration.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.8"
@@ -551,6 +563,17 @@
}
}
},
"Microsoft.Extensions.Configuration.Binder/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.Binder.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.DependencyInjection/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
@@ -578,6 +601,47 @@
}
}
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "9.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Diagnostics.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Diagnostics.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Http/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Diagnostics": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Http.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Logging/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.8",
@@ -614,6 +678,21 @@
}
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Primitives/9.0.8": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.Primitives.dll": {
@@ -1090,7 +1169,9 @@
},
"Elecciones.Infrastructure/1.0.0": {
"dependencies": {
"Elecciones.Core": "1.0.0"
"Elecciones.Core": "1.0.0",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8"
},
"runtime": {
"Elecciones.Infrastructure.dll": {
@@ -1289,6 +1370,13 @@
"path": "microsoft.extensions.caching.memory/9.0.8",
"hashPath": "microsoft.extensions.caching.memory.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Configuration/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-6m+8Xgmf8UWL0p/oGqBM+0KbHE5/ePXbV1hKXgC59zEv0aa0DW5oiiyxDbK5kH5j4gIvyD5uWL0+HadKBJngvQ==",
"path": "microsoft.extensions.configuration/9.0.8",
"hashPath": "microsoft.extensions.configuration.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
@@ -1296,6 +1384,13 @@
"path": "microsoft.extensions.configuration.abstractions/9.0.8",
"hashPath": "microsoft.extensions.configuration.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Configuration.Binder/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-0vK9DnYrYChdiH3yRZWkkp4x4LbrfkWEdBc5HOsQ8t/0CLOWKXKkkhOE8A1shlex0hGydbGrhObeypxz/QTm+w==",
"path": "microsoft.extensions.configuration.binder/9.0.8",
"hashPath": "microsoft.extensions.configuration.binder.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/9.0.8": {
"type": "package",
"serviceable": true,
@@ -1317,6 +1412,27 @@
"path": "microsoft.extensions.dependencymodel/9.0.8",
"hashPath": "microsoft.extensions.dependencymodel.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Diagnostics/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-BKkLCFXzJvNmdngeYBf72VXoZqTJSb1orvjdzDLaGobicoGFBPW8ug2ru1nnEewMEwJzMgnsjHQY8EaKWmVhKg==",
"path": "microsoft.extensions.diagnostics/9.0.8",
"hashPath": "microsoft.extensions.diagnostics.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Diagnostics.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-UDY7blv4DCyIJ/8CkNrQKLaAZFypXQavRZ2DWf/2zi1mxYYKKw2t8AOCBWxNntyPZHPGhtEmL3snFM98ADZqTw==",
"path": "microsoft.extensions.diagnostics.abstractions/9.0.8",
"hashPath": "microsoft.extensions.diagnostics.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Http/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-jDj+4aDByk47oESlDDTtk6LWzlXlmoCsjCn6ihd+i9OntN885aPLszUII5+w0B/7wYSZcS3KdjqLAIhKLSiBXQ==",
"path": "microsoft.extensions.http/9.0.8",
"hashPath": "microsoft.extensions.http.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging/9.0.8": {
"type": "package",
"serviceable": true,
@@ -1338,6 +1454,13 @@
"path": "microsoft.extensions.options/9.0.8",
"hashPath": "microsoft.extensions.options.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Options.ConfigurationExtensions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eW2s6n06x0w6w4nsX+SvpgsFYkl+Y0CttYAt6DKUXeqprX+hzNqjSfOh637fwNJBg7wRBrOIRHe49gKiTgJxzQ==",
"path": "microsoft.extensions.options.configurationextensions/9.0.8",
"hashPath": "microsoft.extensions.options.configurationextensions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/9.0.8": {
"type": "package",
"serviceable": true,

View File

@@ -13,7 +13,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+d9bcfd70865a12c229e6a761639094b45de862bd")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+b90baadeedb870b5b1c9eeeb7022a0d211b61bec")]
[assembly: System.Reflection.AssemblyProductAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyTitleAttribute("Elecciones.Api")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]

View File

@@ -167,3 +167,9 @@ E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\obj\Debug\net9.0\refint\Ele
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\obj\Debug\net9.0\Elecciones.Api.pdb
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\obj\Debug\net9.0\Elecciones.Api.genruntimeconfig.cache
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\obj\Debug\net9.0\ref\Elecciones.Api.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Configuration.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Configuration.Binder.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Diagnostics.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Diagnostics.Abstractions.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Http.dll
E:\Elecciones-2025\Elecciones-Web\src\Elecciones.Api\bin\Debug\net9.0\Microsoft.Extensions.Options.ConfigurationExtensions.dll

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","lxebrAqmCStl1yDKQUlHmeuz9HkimDnS\u002B/df3f1dyII="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"b5T/+ta4fUd8qpIzUTm3KyEwAYYUsU5ASo+CSFM3ByE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","r5wfLIFTEo2\u002Bd7Tsx44bFIb0SPNdPvg4KbBRNmbWVFA="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +1 @@
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","lxebrAqmCStl1yDKQUlHmeuz9HkimDnS\u002B/df3f1dyII="],"CachedAssets":{},"CachedCopyCandidates":{}}
{"GlobalPropertiesHash":"tJTBjV/i0Ihkc6XuOu69wxL8PBac9c9Kak6srMso4pU=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["mhE0FuBM0BOF9SNOE0rY9setCw2ye3UUh7cEPjhgDdY=","AE1TAk4qas82IfXTyRHo\u002BfMlnTE4e7B1AJrEn9ZhBwo=","PA/Beu9jJpOBY5r5Y1CiSyqrARA2j7LHeWYUnEZpQO8=","ywKm3DCyXg4YCbZAIx3JUlT8N4Irff3GswYUVDST\u002BjQ=","P8JRhYPpULTLMAydvl3Ky\u002B92/tYDIjui0l66En4aXuQ=","r5wfLIFTEo2\u002Bd7Tsx44bFIb0SPNdPvg4KbBRNmbWVFA="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -290,6 +290,16 @@
"frameworks": {
"net9.0": {
"targetAlias": "net9.0",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": {
"target": "Package",
"version": "[9.0.8, )"
},
"Microsoft.Extensions.Http": {
"target": "Package",
"version": "[9.0.8, )"
}
},
"imports": [
"net461",
"net462",

View File

@@ -5,6 +5,7 @@
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\9.0.0\build\Microsoft.Extensions.ApiDescription.Server.targets')" />
<Import Project="$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets" Condition="Exists('$(NuGetPackageRoot)mono.texttemplating\3.0.0\buildTransitive\Mono.TextTemplating.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder\9.0.8\buildTransitive\netstandard2.0\Microsoft.Extensions.Configuration.Binder.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\9.0.8\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codeanalysis.analyzers\3.3.4\buildTransitive\Microsoft.CodeAnalysis.Analyzers.targets')" />
</ImportGroup>