Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Academies database connection - add search provider #168

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
315bae1
Add `DfE.FindInformationAcademiesTrusts.Data` project
dynamictulip Oct 3, 2023
ee5578b
Move shared types and interfaces to `DfE.FindInformationAcademiesTrus…
dynamictulip Oct 3, 2023
3430afb
Install test packages
lookupdaily Oct 3, 2023
aba13da
Add IAcadedemiesDb interface to AcademiesDB Context
lookupdaily Oct 3, 2023
5ec4e43
Create new Trust search provider in database project
lookupdaily Oct 3, 2023
439b11d
Add trust address to search response
lookupdaily Oct 6, 2023
0c6c7fb
Replace trust provider with new trust Search
lookupdaily Oct 6, 2023
02ae07d
Rename `Ukprn` property to `Uid`
lookupdaily Oct 6, 2023
a1bf819
Add `GroupId` and `Uid` to TrustSearch response
lookupdaily Oct 6, 2023
e85472d
Remove academy count from search result
lookupdaily Oct 6, 2023
6f80c3e
Change Ukprn to Uid on UI
lookupdaily Oct 6, 2023
ed09960
Add Group Id property to search results list
lookupdaily Oct 6, 2023
3cda7f5
Fix `TrustSearch` dependency injection
dynamictulip Oct 6, 2023
066f5de
Fix use of non-translatable LINQ query
lookupdaily Oct 6, 2023
a7d3eb5
Update and refactor tests
lookupdaily Oct 6, 2023
c77bc43
Update trust search to order by name
lookupdaily Oct 6, 2023
a891da0
Check that GroupID and GroupUID are not null and filter out non trust…
nwarms Oct 9, 2023
e4fc97c
Exlude the constuctor from code coverage as it cannot be tested
nwarms Oct 9, 2023
0089484
Fix existing tests by adding GroupId, GroupUid and group type to the …
nwarms Oct 9, 2023
bf7f364
Add test for single and multi-academy group filter
dynamictulip Oct 11, 2023
d9140cb
Add unit tests for prevention of null properties
dynamictulip Oct 11, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;

public interface IAcademiesDbContext
{
DbSet<Group> Groups { get; }
}

[ExcludeFromCodeCoverage]
public class AcademiesDbContext : DbContext
public class AcademiesDbContext : DbContext, IAcademiesDbContext
{
public AcademiesDbContext()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.11"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DfE.FindInformationAcademiesTrusts.Data\DfE.FindInformationAcademiesTrusts.Data.csproj"/>
</ItemGroup>

</Project>
23 changes: 23 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data.AcademiesDb/TrustHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.Models;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;

public interface ITrustHelper
{
string BuildAddressString(Group group);
}

public class TrustHelper : ITrustHelper
{
public string BuildAddressString(Group group)
{
return string.Join(", ", new[]
{
group.GroupContactStreet,
group.GroupContactLocality,
group.GroupContactTown,
group.GroupContactCounty,
group.GroupContactPostcode
}.Where(s => !string.IsNullOrWhiteSpace(s)));
}
}
52 changes: 52 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data.AcademiesDb/TrustSearch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Diagnostics.CodeAnalysis;

namespace DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;

public class TrustSearch : ITrustSearch
{
private readonly IAcademiesDbContext _academiesDbContext;
private readonly ITrustHelper _trustHelper;

[ExcludeFromCodeCoverage] // This constructor is used by the DI container and is not unit testable
public TrustSearch(AcademiesDbContext academiesDbContext, ITrustHelper trustHelper)
dynamictulip marked this conversation as resolved.
Show resolved Hide resolved
: this((IAcademiesDbContext)academiesDbContext, trustHelper)
{
}

public TrustSearch(IAcademiesDbContext academiesDbContext, ITrustHelper trustHelper)
{
_trustHelper = trustHelper;
_academiesDbContext = academiesDbContext;
}

public Task<IEnumerable<TrustSearchEntry>> SearchAsync(string searchTerm)
{
if (string.IsNullOrWhiteSpace(searchTerm))
{
return Task.FromResult(
Array.Empty<TrustSearchEntry>().AsEnumerable()
);
}

var trustSearchEntries = _academiesDbContext.Groups
.Where(g =>
g.GroupUid != null &&
g.GroupId != null &&
g.GroupName != null &&
g.GroupName.Contains(searchTerm) &&
g.GroupType != null &&
(g.GroupType == "Multi-academy trust" ||
dynamictulip marked this conversation as resolved.
Show resolved Hide resolved
g.GroupType == "Single-academy trust")
) //note that LINQ translates string.contains to case insensitive SQL
.OrderBy(g => g.GroupName)
.Take(20)
.Select(g =>
new TrustSearchEntry(g.GroupName!, _trustHelper.BuildAddressString(g), g.GroupUid!, g.GroupId!))
.AsEnumerable();


return Task.FromResult(
trustSearchEntries
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
6 changes: 6 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data/ITrustSearch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace DfE.FindInformationAcademiesTrusts.Data;

public interface ITrustSearch
{
public Task<IEnumerable<TrustSearchEntry>> SearchAsync(string searchTerm);
}
3 changes: 3 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data/Trust.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DfE.FindInformationAcademiesTrusts.Data;

public record Trust(string Name, string? Ukprn, string Type);
3 changes: 3 additions & 0 deletions DfE.FindInformationAcademiesTrusts.Data/TrustSearchEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DfE.FindInformationAcademiesTrusts.Data;

public record TrustSearchEntry(string Name, string Address, string Uid, string GroupId);
6 changes: 6 additions & 0 deletions DfE.FindInformationAcademiesTrusts.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DfE.FindInformationAcademie
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.UnitTests", "tests\DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.UnitTests\DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.UnitTests.csproj", "{E20D30CB-8EB1-453B-8C49-77CAFBF16A13}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DfE.FindInformationAcademiesTrusts.Data", "DfE.FindInformationAcademiesTrusts.Data\DfE.FindInformationAcademiesTrusts.Data.csproj", "{DC3BD883-B6DA-4C54-85CB-2CD5D261E8FE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -32,6 +34,10 @@ Global
{E20D30CB-8EB1-453B-8C49-77CAFBF16A13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E20D30CB-8EB1-453B-8C49-77CAFBF16A13}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E20D30CB-8EB1-453B-8C49-77CAFBF16A13}.Release|Any CPU.Build.0 = Release|Any CPU
{DC3BD883-B6DA-4C54-85CB-2CD5D261E8FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC3BD883-B6DA-4C54-85CB-2CD5D261E8FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC3BD883-B6DA-4C54-85CB-2CD5D261E8FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC3BD883-B6DA-4C54-85CB-2CD5D261E8FE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0A3416F9-33DB-4870-97EA-7CFF8D828031} = {E22DC2DA-4EB5-41C6-A41D-349692BCCDBE}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@

<ItemGroup>
<ProjectReference Include="..\DfE.FindInformationAcademiesTrusts.Data.AcademiesDb\DfE.FindInformationAcademiesTrusts.Data.AcademiesDb.csproj"/>
<ProjectReference Include="..\DfE.FindInformationAcademiesTrusts.Data\DfE.FindInformationAcademiesTrusts.Data.csproj"/>
</ItemGroup>
</Project>
12 changes: 6 additions & 6 deletions DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
@foreach (var trust in Model.Trusts)
{
<li class="govuk-!-margin-bottom-6">
<a asp-page="./Trusts/Details" asp-route-ukprn="@trust.Ukprn" class="govuk-!-font-weight-bold govuk-link govuk-!-font-size-24">@Html.DisplayFor(modelItem => trust.Name)</a>
<a asp-page="./Trusts/Details" asp-route-uid="@trust.Uid" class="govuk-!-font-weight-bold govuk-link govuk-!-font-size-24">@Html.DisplayFor(modelItem => trust.Name)</a>
<dl class="govuk-summary-list govuk-summary-list--no-border app-search-results-summary-list">
<div class="govuk-summary-list__row govuk-body app-search-results-summary-list__row">
<dt class="govuk-summary-list__key app-search-results-summary-list__key app-search-results-summary-list__item">
Expand All @@ -39,18 +39,18 @@
</div>
<div class="govuk-summary-list__row govuk-body app-search-results-summary-list__row">
<dt class="govuk-summary-list__key app-search-results-summary-list__key app-search-results-summary-list__item">
UKPRN:
Group ID/TRN:
</dt>
<dd class="govuk-summary-list__value app-search-results-summary-list__item">
@Html.DisplayFor(modelItem => trust.Ukprn)
@Html.DisplayFor(modelItem => trust.GroupId)
</dd>
</div>
<div class="govuk-summary-list__row app-search-results-summary-list__row">
<div class="govuk-summary-list__row govuk-body app-search-results-summary-list__row">
<dt class="govuk-summary-list__key app-search-results-summary-list__key app-search-results-summary-list__item">
Academies in this trust:
UID:
</dt>
<dd class="govuk-summary-list__value app-search-results-summary-list__item">
@Html.DisplayFor(modelItem => trust.AcademyCount)
@Html.DisplayFor(modelItem => trust.Uid)
</dd>
</div>
</dl>
Expand Down
11 changes: 7 additions & 4 deletions DfE.FindInformationAcademiesTrusts/Pages/Search.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using DfE.FindInformationAcademiesTrusts.Data;
using DfE.FindInformationAcademiesTrusts.Pages.Shared;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
Expand All @@ -9,10 +10,12 @@ public class SearchModel : PageModel, ISearchFormModel
public record AutocompleteEntry(string Address, string Name, string? TrustId);

private readonly ITrustProvider _trustProvider;
private readonly ITrustSearch _trustSearch;

public SearchModel(ITrustProvider trustProvider)
public SearchModel(ITrustProvider trustProvider, ITrustSearch trustSearch)
{
_trustProvider = trustProvider;
_trustSearch = trustSearch;
}

public string InputId => "search";
Expand All @@ -27,7 +30,7 @@ public async Task<IActionResult> OnGetAsync()
var trust = await _trustProvider.GetTrustByUkprnAsync(TrustId);
if (trust != null && string.Equals(trust.Name, KeyWords, StringComparison.CurrentCultureIgnoreCase))
{
return RedirectToPage("/Trusts/Details", new { Ukprn = TrustId });
return RedirectToPage("/Trusts/Details", new { Uid = TrustId });
}
}

Expand All @@ -38,7 +41,7 @@ public async Task<IActionResult> OnGetAsync()
private async Task<IEnumerable<TrustSearchEntry>> GetTrustsForKeywords()
{
return !string.IsNullOrEmpty(KeyWords)
? await _trustProvider.GetTrustsByNameAsync(KeyWords)
? await _trustSearch.SearchAsync(KeyWords)
: Array.Empty<TrustSearchEntry>();
}

Expand All @@ -49,7 +52,7 @@ public async Task<IActionResult> OnGetPopulateAutocompleteAsync()
new AutocompleteEntry(
trust.Address,
trust.Name,
trust.Ukprn
trust.Uid
)));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "{ukprn}"
@page "{uid}"
@model ContactsModel

@{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using DfE.FindInformationAcademiesTrusts.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

Expand All @@ -12,14 +13,14 @@ public ContactsModel(ITrustProvider trustProvider)
_trustProvider = trustProvider;
}

[BindProperty(SupportsGet = true)] public string Ukprn { get; set; } = "";
[BindProperty(SupportsGet = true)] public string Uid { get; set; } = "";
public Trust Trust { get; set; } = default!;
public string PageName => "Contacts";
public string Section => ViewConstants.AboutTheTrustSectionName;

public async Task<IActionResult> OnGetAsync()
{
var trust = await _trustProvider.GetTrustByUkprnAsync(Ukprn);
var trust = await _trustProvider.GetTrustByUkprnAsync(Uid);

if (trust == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "{ukprn}"
@page "{uid}"
@model DetailsModel
@{
Layout = "_TrustLayout";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using DfE.FindInformationAcademiesTrusts.Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

Expand All @@ -12,15 +13,15 @@ public DetailsModel(ITrustProvider trustProvider)
_trustProvider = trustProvider;
}

[BindProperty(SupportsGet = true)] public string Ukprn { get; set; } = "";
[BindProperty(SupportsGet = true)] public string Uid { get; set; } = "";
public Trust Trust { get; set; } = default!;
public string PageName => "Details";
public string Section => ViewConstants.AboutTheTrustSectionName;


public async Task<IActionResult> OnGetAsync()
{
var trust = await _trustProvider.GetTrustByUkprnAsync(Ukprn);
var trust = await _trustProvider.GetTrustByUkprnAsync(Uid);

if (trust == null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using DfE.FindInformationAcademiesTrusts.Data;

namespace DfE.FindInformationAcademiesTrusts.Pages.Trusts;

public interface ITrustsAreaModel
Expand Down
2 changes: 2 additions & 0 deletions DfE.FindInformationAcademiesTrusts/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using DfE.FindInformationAcademiesTrusts.Data;
using DfE.FindInformationAcademiesTrusts.Data.AcademiesDb;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Authentication.Cookies;
Expand Down Expand Up @@ -149,6 +150,7 @@ private static void AddDependenciesTo(WebApplicationBuilder builder)

builder.Services.AddScoped<ITrustSearch, TrustSearch>();
builder.Services.AddScoped<ITrustProvider, TrustProvider>();
builder.Services.AddScoped<ITrustHelper, TrustHelper>();

builder.Services.AddHttpClient("AcademiesApi", (provider, httpClient) =>
{
Expand Down
5 changes: 0 additions & 5 deletions DfE.FindInformationAcademiesTrusts/Trust.cs

This file was deleted.

56 changes: 1 addition & 55 deletions DfE.FindInformationAcademiesTrusts/TrustProvider.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.Net;
using System.Text.Json;
using DfE.FindInformationAcademiesTrusts.AcademiesApiResponseModels;
using DfE.FindInformationAcademiesTrusts.Data;

namespace DfE.FindInformationAcademiesTrusts;

public interface ITrustProvider
{
public Task<IEnumerable<TrustSearchEntry>> GetTrustsAsync();
public Task<IEnumerable<TrustSearchEntry>> GetTrustsByNameAsync(string name);
public Task<Trust?> GetTrustByUkprnAsync(string ukprn);
}

Expand All @@ -24,46 +23,6 @@ public TrustProvider(IHttpClientFactory httpClientFactory,
_httpClient = httpClientFactory.CreateClient("AcademiesApi");
}

public async Task<IEnumerable<TrustSearchEntry>> GetTrustsAsync()
{
var requestUri = "v3/trusts";

return await FetchTrustsAsync(requestUri);
}

public async Task<IEnumerable<TrustSearchEntry>> GetTrustsByNameAsync(string name)
{
var requestUri = $"v3/trusts?groupName={name}";

return await FetchTrustsAsync(requestUri);
}

private async Task<IEnumerable<TrustSearchEntry>> FetchTrustsAsync(string requestUri)
{
var httpResponseMessage = await _httpClient.GetAsync(requestUri);
if (httpResponseMessage.IsSuccessStatusCode)
{
await using var contentStream = await httpResponseMessage.Content.ReadAsStreamAsync();
var json = await JsonSerializer.DeserializeAsync<ApiResponseV3<TrustSummaryResponse>>(contentStream,
new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

if (json?.Data == null) throw new JsonException();
var transformedData = json.Data
.Where(t => t.GroupName != null)
.Select(t => new TrustSearchEntry(
t.GroupName!,
TrustAddressAsString(t.TrustAddress),
t.Ukprn,
t.Establishments?.Count ?? 0
));
return transformedData.OrderBy(t => t.Name);
}

var errorMessage = await httpResponseMessage.Content.ReadAsStringAsync();
LogHttpError(httpResponseMessage, errorMessage);
throw new HttpRequestException("Problem communicating with Academies API");
}

public async Task<Trust?> GetTrustByUkprnAsync(string ukprn)
{
var httpResponseMessage = await _httpClient.GetAsync($"v3/trust/{ukprn}");
Expand Down Expand Up @@ -104,17 +63,4 @@ private void LogHttpError(HttpResponseMessage httpResponseMessage, string errorM
httpResponseMessage.Headers
);
}

private static string TrustAddressAsString(AddressResponse? addressResponse)
{
if (addressResponse == null) return string.Empty;
return string.Join(", ", new[]
{
addressResponse.Street,
addressResponse.Locality,
addressResponse.AdditionalLine,
addressResponse.Town,
addressResponse.Postcode
}.Where(s => !string.IsNullOrWhiteSpace(s)));
}
}
Loading