Skip to content

Commit

Permalink
Add district, municipality, and state repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickdewid committed Apr 25, 2024
1 parent 1498672 commit a7518c4
Show file tree
Hide file tree
Showing 10 changed files with 852 additions and 2 deletions.
37 changes: 37 additions & 0 deletions src/FunderMaps.Core/Interfaces/Repositories/IDistrictRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using FunderMaps.Core.Entities;

namespace FunderMaps.Core.Interfaces.Repositories;

/// <summary>
/// District repository.
/// </summary>
public interface IDistrictRepository : IAsyncRepository<District, string>
{
/// <summary>
/// Get district by external identifier.
/// </summary>
/// <param name="id">External identifier.</param>
/// <returns>A single district.</returns>
Task<District> GetByExternalIdAsync(string id);

/// <summary>
/// Get district by external address id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single district.</returns>
Task<District> GetByExternalAddressIdAsync(string id);

/// <summary>
/// Get district by external building id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single district.</returns>
Task<District> GetByExternalBuildingIdAsync(string id);

/// <summary>
/// Get district by external neighborhood id.
/// </summary>
/// <param name="id">External neighborhood identifier.</param>
/// <returns>A single district.</returns>
Task<District> GetByExternalNeighborhoodIdAsync(string id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using FunderMaps.Core.Entities;

namespace FunderMaps.Core.Interfaces.Repositories;

/// <summary>
/// Municipality repository.
/// </summary>
public interface IMunicipalityRepository : IAsyncRepository<Municipality, string>
{
/// <summary>
/// Get municipality by external identifier.
/// </summary>
/// <param name="id">External identifier.</param>
/// <returns>A single municipality.</returns>
Task<Municipality> GetByExternalIdAsync(string id);

/// <summary>
/// Get municipality by external address id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single municipality.</returns>
Task<Municipality> GetByExternalAddressIdAsync(string id);

/// <summary>
/// Get municipality by external building id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single municipality.</returns>
Task<Municipality> GetByExternalBuildingIdAsync(string id);

/// <summary>
/// Get municipality by external neighborhood id.
/// </summary>
/// <param name="id">External neighborhood identifier.</param>
/// <returns>A single municipality.</returns>
Task<Municipality> GetByExternalNeighborhoodIdAsync(string id);

/// <summary>
/// Get municipality by external district id.
/// </summary>
/// <param name="id">External district identifier.</param>
/// <returns>A single municipality.</returns>
Task<Municipality> GetByExternalDistrictIdAsync(string id);
}
51 changes: 51 additions & 0 deletions src/FunderMaps.Core/Interfaces/Repositories/IStateRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using FunderMaps.Core.Entities;

namespace FunderMaps.Core.Interfaces.Repositories;

/// <summary>
/// State repository.
/// </summary>
public interface IStateRepository : IAsyncRepository<State, string>
{
/// <summary>
/// Get state by external identifier.
/// </summary>
/// <param name="id">External identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalIdAsync(string id);

/// <summary>
/// Get state by external address id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalAddressIdAsync(string id);

/// <summary>
/// Get state by external building id.
/// </summary>
/// <param name="id">External address identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalBuildingIdAsync(string id);

/// <summary>
/// Get state by external neighborhood id.
/// </summary>
/// <param name="id">External neighborhood identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalNeighborhoodIdAsync(string id);

/// <summary>
/// Get state by external district id.
/// </summary>
/// <param name="id">External district identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalDistrictIdAsync(string id);

/// <summary>
/// Get state by external municipality id.
/// </summary>
/// <param name="id">External municipality identifier.</param>
/// <returns>A single state.</returns>
Task<State> GetByExternalMunicipalityIdAsync(string id);
}
38 changes: 37 additions & 1 deletion src/FunderMaps.Core/Services/GeocoderTranslation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ namespace FunderMaps.Core.Services;
public class GeocoderTranslation(
IAddressRepository addressRepository,
IBuildingRepository buildingRepository,
INeighborhoodRepository neighborhoodRepository)
INeighborhoodRepository neighborhoodRepository,
IDistrictRepository districtRepository,
IMunicipalityRepository municipalityRepository,
IStateRepository stateRepository)
{
/// <summary>
/// Identify the geocoder datasource from the input.
Expand Down Expand Up @@ -158,4 +161,37 @@ private static GeocoderDatasource FromIdentifier(string input, out string output
GeocoderDatasource.NlCbsNeighborhood => await neighborhoodRepository.GetByExternalIdAsync(id),
_ => throw new EntityNotFoundException("Requested neighborhood entity could not be found."),
};

public async Task<District> GetDistrictIdAsync(string input) => FromIdentifier(input, out string id) switch
{
GeocoderDatasource.FunderMaps => await districtRepository.GetByIdAsync(id),
GeocoderDatasource.NlBagAddress => await districtRepository.GetByExternalAddressIdAsync(id),
GeocoderDatasource.NlBagBuilding => await districtRepository.GetByExternalBuildingIdAsync(id),
GeocoderDatasource.NlCbsNeighborhood => await districtRepository.GetByExternalNeighborhoodIdAsync(id),
GeocoderDatasource.NlCbsDistrict => await districtRepository.GetByExternalIdAsync(id),
_ => throw new EntityNotFoundException("Requested district entity could not be found."),
};

public async Task<Municipality> GetMunicipalityIdAsync(string input) => FromIdentifier(input, out string id) switch
{
GeocoderDatasource.FunderMaps => await municipalityRepository.GetByIdAsync(id),
GeocoderDatasource.NlBagAddress => await municipalityRepository.GetByExternalAddressIdAsync(id),
GeocoderDatasource.NlBagBuilding => await municipalityRepository.GetByExternalBuildingIdAsync(id),
GeocoderDatasource.NlCbsNeighborhood => await municipalityRepository.GetByExternalNeighborhoodIdAsync(id),
GeocoderDatasource.NlCbsDistrict => await municipalityRepository.GetByExternalDistrictIdAsync(id),
GeocoderDatasource.NlCbsMunicipality => await municipalityRepository.GetByExternalIdAsync(id),
_ => throw new EntityNotFoundException("Requested municipality entity could not be found."),
};

public async Task<State> GetStateIdAsync(string input) => FromIdentifier(input, out string id) switch
{
GeocoderDatasource.FunderMaps => await stateRepository.GetByIdAsync(id),
GeocoderDatasource.NlBagAddress => await stateRepository.GetByExternalAddressIdAsync(id),
GeocoderDatasource.NlBagBuilding => await stateRepository.GetByExternalBuildingIdAsync(id),
GeocoderDatasource.NlCbsNeighborhood => await stateRepository.GetByExternalNeighborhoodIdAsync(id),
GeocoderDatasource.NlCbsDistrict => await stateRepository.GetByExternalDistrictIdAsync(id),
GeocoderDatasource.NlCbsMunicipality => await stateRepository.GetByExternalMunicipalityIdAsync(id),
GeocoderDatasource.NlCbsState => await stateRepository.GetByExternalIdAsync(id),
_ => throw new EntityNotFoundException("Requested state entity could not be found."),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,20 @@ public static IServiceCollection AddFunderMapsDataServices(this IServiceCollecti
services.AddContextRepository<IBuildingRepository, BuildingRepository>();
services.AddContextRepository<IBundleRepository, BundleRepository>();
services.AddContextRepository<IContractorRepository, ContractorRepository>();
services.AddContextRepository<IDistrictRepository, DistrictRepository>();
services.AddContextRepository<IIncidentRepository, IncidentRepository>();
services.AddContextRepository<IInquiryRepository, InquiryRepository>();
services.AddContextRepository<IInquirySampleRepository, InquirySampleRepository>();
services.AddContextRepository<IKeystoreRepository, KeystoreRepository>();
services.AddContextRepository<IMapsetRepository, MapsetRepository>();
services.AddContextRepository<IMunicipalityRepository, MunicipalityRepository>();
services.AddContextRepository<INeighborhoodRepository, NeighborhoodRepository>();
services.AddContextRepository<IOperationRepository, OperationRepository>();
services.AddContextRepository<IOrganizationRepository, OrganizationRepository>();
services.AddContextRepository<IOrganizationUserRepository, OrganizationUserRepository>();
services.AddContextRepository<IRecoveryRepository, RecoveryRepository>();
services.AddContextRepository<IRecoverySampleRepository, RecoverySampleRepository>();
services.AddContextRepository<IStateRepository, StateRepository>();
services.AddContextRepository<IStatisticsRepository, StatisticsRepository>();
services.AddContextRepository<ITelemetryRepository, TelemetryRepository>();
services.AddContextRepository<IUserRepository, UserRepository>();
Expand Down
186 changes: 186 additions & 0 deletions src/FunderMaps.Data/Repositories/DistrictRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
using Dapper;
using FunderMaps.Core;
using FunderMaps.Core.Entities;
using FunderMaps.Core.Exceptions;
using FunderMaps.Core.Interfaces.Repositories;

namespace FunderMaps.Data.Repositories;

/// <summary>
/// District repository.
/// </summary>
internal class DistrictRepository : RepositoryBase<District, string>, IDistrictRepository
{
/// <summary>
/// Retrieve number of entities.
/// </summary>
/// <returns>Number of entities.</returns>
public override async Task<long> CountAsync()
{
var sql = @"
SELECT COUNT(*)
FROM geocoder.district";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

return await connection.ExecuteScalarAsync<long>(sql);
}

public async Task<District> GetByExternalIdAsync(string id)
{
if (TryGetEntity(id, out District? entity))
{
return entity ?? throw new InvalidOperationException();
}

var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.district AS d
WHERE d.external_id = upper(@external_id)
LIMIT 1";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

var district = await connection.QuerySingleOrDefaultAsync<District>(sql, new { external_id = id });
return district is null ? throw new EntityNotFoundException(nameof(District)) : CacheEntity(district);
}

public async Task<District> GetByExternalAddressIdAsync(string id)
{
if (TryGetEntity(id, out District? entity))
{
return entity ?? throw new InvalidOperationException();
}

var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.address AS a
JOIN geocoder.address_building AS ab ON ab.address_id = a.id
JOIN geocoder.building_active AS ba ON ba.id = ab.building_id
JOIN geocoder.neighborhood AS n ON n.id = ba.neighborhood_id
JOIN geocoder.district d ON d.id = n.district_id
WHERE a.external_id = upper(@external_id)
LIMIT 1";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

var district = await connection.QuerySingleOrDefaultAsync<District>(sql, new { external_id = id });
return district is null ? throw new EntityNotFoundException(nameof(District)) : CacheEntity(district);
}

public async Task<District> GetByExternalBuildingIdAsync(string id)
{
if (TryGetEntity(id, out District? entity))
{
return entity ?? throw new InvalidOperationException();
}

var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.building_active AS ba
JOIN geocoder.neighborhood AS n on n.id = ba.neighborhood_id
JOIN geocoder.district d ON d.id = n.district_id
WHERE ba.external_id = upper(@external_id)
LIMIT 1";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

var district = await connection.QuerySingleOrDefaultAsync<District>(sql, new { external_id = id });
return district is null ? throw new EntityNotFoundException(nameof(District)) : CacheEntity(district);
}

public async Task<District> GetByExternalNeighborhoodIdAsync(string id)
{
if (TryGetEntity(id, out District? entity))
{
return entity ?? throw new InvalidOperationException();
}

var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.neighborhood AS n
JOIN geocoder.district d ON d.id = n.district_id
WHERE n.external_id = upper(@external_id)
LIMIT 1";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

var district = await connection.QuerySingleOrDefaultAsync<District>(sql, new { external_id = id });
return district is null ? throw new EntityNotFoundException(nameof(District)) : CacheEntity(district);
}


/// <summary>
/// Retrieve <see cref="District"/> by id.
/// </summary>
/// <param name="id">Unique identifier.</param>
/// <returns><see cref="District"/>.</returns>
public override async Task<District> GetByIdAsync(string id)
{
if (TryGetEntity(id, out District? entity))
{
return entity ?? throw new InvalidOperationException();
}

var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.district AS d
WHERE d.id = @id
LIMIT 1";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

var district = await connection.QuerySingleOrDefaultAsync<District>(sql, new { id });
return district is null ? throw new EntityNotFoundException(nameof(District)) : CacheEntity(district);
}

/// <summary>
/// Retrieve all <see cref="District"/>.
/// </summary>
/// <returns>List of <see cref="District"/>.</returns>
public override async IAsyncEnumerable<District> ListAllAsync(Navigation navigation)
{
var sql = @"
SELECT -- District
d.id,
d.name,
d.water,
d.external_id,
d.municipality_id
FROM geocoder.district AS d
OFFSET @offset
LIMIT @limit";

await using var connection = DbContextFactory.DbProvider.ConnectionScope();

await foreach (var item in connection.QueryUnbufferedAsync<District>(sql, navigation))
{
yield return CacheEntity(item);
}
}
}
Loading

0 comments on commit a7518c4

Please sign in to comment.