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

@@ -1,6 +0,0 @@
namespace Elecciones.Infrastructure;
public class Class1
{
}

View File

@@ -4,6 +4,11 @@
<ProjectReference Include="..\Elecciones.Core\Elecciones.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.8" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.8" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>

View File

@@ -0,0 +1,86 @@
using Elecciones.Core.DTOs;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
namespace Elecciones.Infrastructure.Services;
public class ElectoralApiService : IElectoralApiService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
public ElectoralApiService(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
}
public async Task<string?> GetAuthTokenAsync()
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var username = _configuration["ElectoralApi:Username"];
var password = _configuration["ElectoralApi:Password"];
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return null;
var request = new HttpRequestMessage(HttpMethod.Get, "/api/createtoken");
request.Headers.Add("username", username);
request.Headers.Add("password", password);
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode) return null;
var tokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
return (tokenResponse is { Success: true, Data.AccessToken: not null })
? tokenResponse.Data.AccessToken
: null;
}
public async Task<List<CatalogoDto>?> GetCatalogoCompletoAsync(string authToken)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var request = new HttpRequestMessage(HttpMethod.Get, "/api/catalogo/getCatalogo?categoriald=5");
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<List<CatalogoDto>>()
: null;
}
public async Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
var requestUri = $"/api/catalogo/getAgrupaciones?distritold={distritoId}&categoriald={categoriaId}";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
return response.IsSuccessStatusCode
? await response.Content.ReadFromJsonAsync<List<AgrupacionDto>>()
: null;
}
public async Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId)
{
var client = _httpClientFactory.CreateClient("ElectoralApiClient");
// Construimos la URL con todos los parámetros requeridos. Usamos categoría 5 (Diputados) como ejemplo.
var requestUri = $"/api/resultados/getResultados?distritold={distritoId}&seccionld={seccionId}&municipiold={municipioId}&categoriald=5";
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
request.Headers.Add("Authorization", $"Bearer {authToken}");
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadFromJsonAsync<ResultadosDto>();
}
}

View File

@@ -0,0 +1,101 @@
// src/Elecciones.Infrastructure/Services/FakeElectoralApiService.cs
using Elecciones.Core.DTOs;
using Microsoft.Extensions.Logging;
namespace Elecciones.Infrastructure.Services;
public class FakeElectoralApiService : IElectoralApiService
{
private readonly ILogger<FakeElectoralApiService> _logger;
public FakeElectoralApiService(ILogger<FakeElectoralApiService> logger)
{
_logger = logger;
}
public Task<string?> GetAuthTokenAsync()
{
_logger.LogWarning("--- USANDO SERVICIO FALSO (FAKE) ---");
_logger.LogInformation("Simulando obtención de token...");
string fakeToken = "FAKE_TOKEN_FOR_DEVELOPMENT";
return Task.FromResult<string?>(fakeToken);
}
public Task<List<CatalogoDto>?> GetCatalogoCompletoAsync(string authToken)
{
_logger.LogInformation("Simulando obtención de Catálogo Completo...");
var catalogo = new List<CatalogoDto>
{
new() // Simulamos el catálogo para la categoría de Diputados (ID 5)
{
Version = 1,
CategoriaId = 5,
Ambitos =
[
new() { NivelId = 10, Nombre = "BUENOS AIRES", CodigoAmbitos = new() { DistritoId = "02" } },
new() { NivelId = 5, Nombre = "LA PLATA", CodigoAmbitos = new() { DistritoId = "02", SeccionId = "0001", MunicipioId = "056" } },
new() { NivelId = 5, Nombre = "MAR DEL PLATA", CodigoAmbitos = new() { DistritoId = "02", SeccionId = "0005", MunicipioId = "035" } }
],
Niveles =
[
new() { NivelId = 10, Nombre = "Provincia" },
new() { NivelId = 5, Nombre = "Municipio" }
]
}
};
return Task.FromResult<List<CatalogoDto>?>(catalogo);
}
public Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId)
{
_logger.LogInformation("Simulando obtención de Agrupaciones Políticas para el distrito {Distrito} y categoría {Categoria}...", distritoId, categoriaId);
var agrupaciones = new List<AgrupacionDto>
{
new() { IdAgrupacion = "018", IdAgrupacionTelegrama = "131", NombreAgrupacion = "FRENTE DE AVANZADA" },
new() { IdAgrupacion = "025", IdAgrupacionTelegrama = "132", NombreAgrupacion = "ALIANZA POR EL FUTURO" },
new() { IdAgrupacion = "031", IdAgrupacionTelegrama = "133", NombreAgrupacion = "UNION POPULAR" },
new() { IdAgrupacion = "045", IdAgrupacionTelegrama = "134", NombreAgrupacion = "PARTIDO VECINALISTA" }
};
return Task.FromResult<List<AgrupacionDto>?>(agrupaciones);
}
public Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId)
{
_logger.LogInformation("Simulando obtención de Resultados para el municipio {MunicipioId}...", municipioId);
// YA NO FILTRAMOS POR ID. DEVOLVEMOS DATOS SIMULADOS PARA CUALQUIER MUNICIPIO.
var random = new Random();
var resultados = new ResultadosDto
{
FechaTotalizacion = DateTime.Now.ToString("o"),
EstadoRecuento = new EstadoRecuentoDto
{
MesasEsperadas = random.Next(100, 2000), // Hacemos que varíe
MesasTotalizadas = random.Next(50, 100),
CantidadElectores = random.Next(50000, 600000),
ParticipacionPorcentaje = random.Next(60, 85) + (decimal)random.NextDouble()
},
ValoresTotalizadosPositivos =
[
// Usamos los IDs reales de nuestro catálogo de agrupaciones falsas
new() { IdAgrupacion = "018", NombreAgrupacion = "FRENTE DE AVANZADA", Votos = random.Next(10000, 20000) },
new() { IdAgrupacion = "025", NombreAgrupacion = "ALIANZA POR EL FUTURO", Votos = random.Next(15000, 25000) },
new() { IdAgrupacion = "031", NombreAgrupacion = "UNION POPULAR", Votos = random.Next(5000, 10000) },
new() { IdAgrupacion = "045", NombreAgrupacion = "PARTIDO VECINALISTA", Votos = random.Next(2000, 5000) }
],
ValoresTotalizadosOtros = new VotosOtrosDto
{
VotosEnBlanco = random.Next(1000, 2000),
VotosNulos = random.Next(500, 1000),
VotosRecurridos = random.Next(20, 50)
}
};
return Task.FromResult<ResultadosDto?>(resultados);
}
}

View File

@@ -0,0 +1,12 @@
// src/Elecciones.Infrastructure/Services/IElectoralApiService.cs
using Elecciones.Core.DTOs;
namespace Elecciones.Infrastructure.Services;
public interface IElectoralApiService
{
Task<string?> GetAuthTokenAsync();
Task<List<CatalogoDto>?> GetCatalogoCompletoAsync(string authToken);
Task<List<AgrupacionDto>?> GetAgrupacionesAsync(string authToken, string distritoId, int categoriaId);
Task<ResultadosDto?> GetResultadosAsync(string authToken, string distritoId, string seccionId, string municipioId);
}

View File

@@ -8,12 +8,167 @@
".NETCoreApp,Version=v9.0": {
"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": {}
}
},
"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"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"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"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.8": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"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",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Microsoft.Extensions.Options/9.0.8": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
},
"runtime": {
"lib/net9.0/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"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": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.825.36511"
}
}
},
"Elecciones.Core/1.0.0": {
"runtime": {
"Elecciones.Core.dll": {
@@ -30,6 +185,97 @@
"serviceable": false,
"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,
"sha512": "sha512-yNou2KM35RvzOh4vUFtl2l33rWPvOCoba+nzEDJ+BgD8aOL/jew4WPCibQvntRfOJ2pJU8ARygSMD+pdjvDHuA==",
"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,
"sha512": "sha512-JJjI2Fa+QtZcUyuNjbKn04OjIUX5IgFGFu/Xc+qvzh1rXdZHLcnqqVXhR4093bGirTwacRlHiVg1XYI9xum6QQ==",
"path": "microsoft.extensions.dependencyinjection/9.0.8",
"hashPath": "microsoft.extensions.dependencyinjection.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xY3lTjj4+ZYmiKIkyWitddrp1uL5uYiweQjqo4BKBw01ZC4HhcfgLghDpPZcUlppgWAFqFy9SgkiYWOMx365pw==",
"path": "microsoft.extensions.dependencyinjection.abstractions/9.0.8",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.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,
"sha512": "sha512-Z/7ze+0iheT7FJeZPqJKARYvyC2bmwu3whbm/48BJjdlGVvgDguoCqJIkI/67NkroTYobd5geai1WheNQvWrgA==",
"path": "microsoft.extensions.logging/9.0.8",
"hashPath": "microsoft.extensions.logging.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-pYnAffJL7ARD/HCnnPvnFKSIHnTSmWz84WIlT9tPeQ4lHNiu0Az7N/8itihWvcF8sT+VVD5lq8V+ckMzu4SbOw==",
"path": "microsoft.extensions.logging.abstractions/9.0.8",
"hashPath": "microsoft.extensions.logging.abstractions.9.0.8.nupkg.sha512"
},
"Microsoft.Extensions.Options/9.0.8": {
"type": "package",
"serviceable": true,
"sha512": "sha512-OmTaQ0v4gxGQkehpwWIqPoEiwsPuG/u4HUsbOFoWGx4DKET2AXzopnFe/fE608FIhzc/kcg2p8JdyMRCCUzitQ==",
"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,
"sha512": "sha512-tizSIOEsIgSNSSh+hKeUVPK7xmTIjR8s+mJWOu1KXV3htvNQiPMFRMO17OdI1y/4ZApdBVk49u/08QGC9yvLug==",
"path": "microsoft.extensions.primitives/9.0.8",
"hashPath": "microsoft.extensions.primitives.9.0.8.nupkg.sha512"
},
"Elecciones.Core/1.0.0": {
"type": "project",
"serviceable": false,

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

View File

@@ -118,6 +118,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

@@ -1,2 +1,8 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<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')" />
</ImportGroup>
</Project>