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

Feature/add bulk contentful upload app #26

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cc09fab
added qualification model and new console app to populate contentful
DanielClarkeEducation Feb 16, 2024
fa31b68
fix to add in display field
DanielClarkeEducation Feb 16, 2024
8db4bda
versioning of content model and entries in place
DanielClarkeEducation Feb 21, 2024
15d4a7e
Added contentful app prototype
DanielClarkeEducation Feb 23, 2024
e6e7247
updated package versions
DanielClarkeEducation Feb 26, 2024
ab74a07
updating postcss version
DanielClarkeEducation Feb 26, 2024
21a8c7c
Merge branch 'main' into feature/add-bulk-contentful-upload-app
DanielClarkeEducation Feb 26, 2024
57774c0
Merge branch 'main' into feature/add-bulk-contentful-upload-app
sam-c-dfe Feb 29, 2024
0838c11
overhauled the contentful app - padgination implemented and refined b…
DanielClarkeEducation Feb 29, 2024
034fb66
Updated to add in 2 additional fields. Refactored the code to use the…
sam-c-dfe Mar 1, 2024
7e12236
Updated to add in the help text for fields
sam-c-dfe Mar 5, 2024
210be06
Updated to change the qualification level to be an int and updated a …
sam-c-dfe Mar 6, 2024
e13d1a4
cleaned up some of the tsx files
DanielClarkeEducation Mar 11, 2024
9d352cc
Updated to add in the widgetIds for the editor interface updates
sam-c-dfe Mar 12, 2024
5287d7a
Bump azure/login from 1 to 2 (#48)
dependabot[bot] Mar 8, 2024
cdcda50
Added the qualification details page (#49)
sam-c-dfe Mar 12, 2024
db05eb3
Bump coverlet.collector from 6.0.1 to 6.0.2 (#51)
dependabot[bot] Mar 14, 2024
1e66910
Bump contentful.csharp from 7.5.0 to 7.5.1 (#53)
dependabot[bot] Mar 14, 2024
a9e47b2
Bump contentful.aspnetcore from 7.5.0 to 7.5.1 (#55)
dependabot[bot] Mar 14, 2024
6abcdba
EYQB: Architectural Decision Logs (#50)
sunny-sidhu-and Mar 15, 2024
963a76b
EYQB-133: Start page content (#54)
sam-c-dfe Mar 15, 2024
34d9528
Merge branch 'main' into feature/add-bulk-contentful-upload-app
DanielClarkeEducation Apr 5, 2024
a175fdc
Merge branch 'main' into feature/add-bulk-contentful-upload-app
DanielClarkeEducation Jun 4, 2024
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
Prev Previous commit
Next Next commit
Added the qualification details page (#49)
* Initial commit of the QualificationsDetails controller & view.

* EYQB-110: Updated to add in unit tests for the controller. Refactored the content service slightly to use a generic method for retrieving the content.

* EYQB-110: Updated to encode qualificationId

* EYQB-110: Updated the view to add in the additional qualification data

* EYQB-110: Added in a temporary page to link to the qualification details for a number of hardcoded qualificationIds

* Added in the qualification details e2e check

* Removed as not needed at this point

* Added a readme file to say how to run the e2e tests locally.

* EYQB-110: Testing the urls config to see if it picks up the new page as a relative path

* EYQB-110: Added absolute paths
  • Loading branch information
sam-c-dfe authored and DanielClarkeEducation committed Mar 18, 2024
commit cdcda50441b3c1326ccc8193a40bf57155a1c5b2
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@

<ItemGroup>
<PackageReference Include="contentful.csharp" Version="7.5.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>

</Project>
56 changes: 33 additions & 23 deletions src/Dfe.EarlyYearsQualification.Content/Entities/Qualification.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
using Contentful.Core.Models;

namespace Dfe.EarlyYearsQualification.Content.Entities;

public class Qualification(
string qualificationId,
string qualificationName,
string awardingOrganisationTitle,
int qualificationLevel,
string? fromWhichYear,
string? toWhichYear,
string? qualificationNumber,
string? notes,
string? additionalRequirements)
public class Qualification
{
// Required Fields
public string QualificationId { get; set; } = qualificationId;
public string QualificationName { get; set; } = qualificationName;
public string AwardingOrganisationTitle { get; set; } = awardingOrganisationTitle;
public int QualificationLevel { get; set; } = qualificationLevel;
public Qualification(
string qualificationId,
string qualificationName,
string awardingOrganisationTitle,
int qualificationLevel,
string? fromWhichYear,
string? toWhichYear,
string? qualificationNumber,
string? notes,
string? additionalRequirements)
{
QualificationId = qualificationId;
QualificationName = qualificationName;
AwardingOrganisationTitle = awardingOrganisationTitle;
QualificationLevel = qualificationLevel;
FromWhichYear = fromWhichYear;
ToWhichYear = toWhichYear;
QualificationNumber = qualificationNumber;
Notes = notes;
AdditionalRequirements = additionalRequirements;
}
// Required Fields
public string QualificationId { get; set; }
public string QualificationName { get; set; }
public string AwardingOrganisationTitle { get; set; }
public int QualificationLevel { get; set; }

// Optional Fields
public string? FromWhichYear { get; set; } = fromWhichYear;
public string? ToWhichYear { get; set; } = toWhichYear;
public string? QualificationNumber { get; set; } = qualificationNumber;
public string? Notes { get; set; } = notes;
public string? AdditionalRequirements { get; set; } = additionalRequirements;
// Optional Fields
public string? FromWhichYear { get; set; }
public string? ToWhichYear { get; set; }
public string? QualificationNumber { get; set; }
public string? Notes { get; set; }
public string? AdditionalRequirements { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,82 @@
using Contentful.Core;
using Contentful.Core.Models;
using Contentful.Core.Search;
using Dfe.EarlyYearsQualification.Content.Entities;
using Dfe.EarlyYearsQualification.Content.Renderers;
using Microsoft.Extensions.Logging;
using System.Web;

namespace Dfe.EarlyYearsQualification.Content.Services;

public class ContentfulContentService : IContentService
{
private readonly IContentfulClient _contentfulClient;
private readonly ILogger<ContentfulContentService> _logger;

public ContentfulContentService(IContentfulClient contentfulClient)
private Dictionary<object, string> ContentTypes = new()
{
{typeof(LandingPage), "landingPage"},
{typeof(NavigationLink), "navigationLink"},
{typeof(Qualification), "Qualification"}
};

public ContentfulContentService(IContentfulClient contentfulClient, ILogger<ContentfulContentService> logger)
{
_contentfulClient = contentfulClient;
_logger = logger;
}

public async Task<LandingPage> GetLandingPage()
public async Task<LandingPage?> GetLandingPage()
{
var landingPageEntries = await _contentfulClient.GetEntriesByType<LandingPage>("landingPage");
var landingPageEntries = await GetEntriesByType<LandingPage>();
if (landingPageEntries is null || !landingPageEntries.Any())
{
_logger.LogWarning($"No landing page entry returned");
return default;
}
var landingPageContent = landingPageEntries.First();
var htmlRenderer = new HtmlRenderer();
htmlRenderer.AddRenderer(new UnorderedListRenderer() { Order = 10 });
landingPageContent.ServiceIntroductionHtml = await htmlRenderer.ToHtml(landingPageContent.ServiceIntroduction);
return landingPageContent;
}

public async Task<List<NavigationLink>> GetNavigationLinks()
public async Task<List<NavigationLink>?> GetNavigationLinks()
{
var navigationLinkEntries = await _contentfulClient.GetEntriesByType<NavigationLink>("navigationLink");
var navigationLinkEntries = await GetEntriesByType<NavigationLink>();
if (navigationLinkEntries is null || !navigationLinkEntries.Any())
{
_logger.LogWarning($"No navigation links returned");
return default;
}
return navigationLinkEntries.ToList();
}

public async Task<Qualification?> GetQualificationById(string qualificationId)
{
var queryBuilder = new QueryBuilder<Qualification>().ContentTypeIs(ContentTypes[typeof(Qualification)]).FieldEquals("fields.qualificationId", qualificationId.ToUpper());
var qualifications = await GetEntriesByType<Qualification>(queryBuilder);

if (qualifications is null || !qualifications.Any())
{
_logger.LogWarning($"No qualifications returned for qualificationId: {HttpUtility.HtmlEncode(qualificationId)}");
return default;
}
var qualification = qualifications.First();
return qualification;
}

private async Task<ContentfulCollection<T>?> GetEntriesByType<T>(QueryBuilder<T>? queryBuilder = null)
{
try
{
var results = await _contentfulClient.GetEntriesByType<T>(ContentTypes[typeof(T)], queryBuilder);
return results;
}
catch (Exception ex)
{
_logger.LogError($"Exception trying to retrieve {typeof(T)} from Contentful. Error: {ex}");
return default;
}
}
}
Original file line number Diff line number Diff line change
@@ -4,7 +4,9 @@ namespace Dfe.EarlyYearsQualification.Content.Services;

public interface IContentService
{
Task<LandingPage> GetLandingPage();
Task<LandingPage?> GetLandingPage();

Task<List<NavigationLink>> GetNavigationLinks();
Task<List<NavigationLink>?> GetNavigationLinks();

Task<Qualification?> GetQualificationById(string qualificationId);
}
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ public HomeController(ILogger<HomeController> logger, IContentService contentSer
public async Task<IActionResult> Index()
{
var landingPageContent = await _contentService.GetLandingPage();
if (landingPageContent is null) return RedirectToAction("Error");
var model = new LandingPageModel()
{
Header = landingPageContent.Header,
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Mvc;
using Dfe.EarlyYearsQualification.Content.Services;
using Dfe.EarlyYearsQualification.Web.Models.Content;
using Dfe.EarlyYearsQualification.Content.Entities;
using Microsoft.AspNetCore.Http.Extensions;

namespace Dfe.EarlyYearsQualification.Web.Controllers;

[Route("/qualifications")]
public class QualificationDetailsController : Controller
{
private readonly ILogger<QualificationDetailsController> _logger;
private readonly IContentService _contentService;

public QualificationDetailsController(ILogger<QualificationDetailsController> logger, IContentService contentService)
{
_logger = logger;
_contentService = contentService;
}

[HttpGet]
public IActionResult Get()
{
return View();
}

[HttpGet("qualification-details/{qualificationId}")]
public async Task<IActionResult> Index(string qualificationId)
{
if (string.IsNullOrEmpty(qualificationId)) return BadRequest();

var qualification = await _contentService.GetQualificationById(qualificationId);
if (qualification is null)
{
return RedirectToAction("Error", "Home");
}
var model = Map(qualification);
return View(model);
}

private QualificationDetailsModel Map(Qualification qualification)
{
return new QualificationDetailsModel()
{
QualificationId = qualification.QualificationId,
QualificationLevel = qualification.QualificationLevel,
QualificationName = qualification.QualificationName,
QualificationNumber = qualification.QualificationNumber,
AwardingOrganisationTitle = qualification.AwardingOrganisationTitle,
FromWhichYear = qualification.FromWhichYear,
ToWhichYear = qualification.ToWhichYear,
Notes = qualification.Notes,
AdditionalRequirements = qualification.AdditionalRequirements,
BookmarkUrl = HttpContext.Request.GetDisplayUrl()
};
}
}
Original file line number Diff line number Diff line change
@@ -23,6 +23,18 @@ public static IServiceCollection AddMockContentful(this IServiceCollection servi
new NavigationLink() { DisplayText = "Privacy notice", Href="#"}
});

mockContentfulService.Setup(x => x.GetQualificationById(It.IsAny<string>())).ReturnsAsync(new Qualification(
"EYQ-240",
"T Level Technical Qualification in Education and Childcare (Specialism - Early Years Educator)",
"NCFE",
3,
"2020",
"2021",
"603/5829/4",
"The course must be assessed within the EYFS in an Early Years setting in England. Please note that the name of this qualification changed in February 2023. Qualifications achieved under either name are full and relevant provided that the start date for the qualification aligns with the date of the name change.",
"Additional notes"
));

services.AddSingleton(mockContentfulService.Object);
return services;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Dfe.EarlyYearsQualification.Web.Models.Content;

public class QualificationDetailsModel
{
public string QualificationId { get; set; } = string.Empty;
public string QualificationName { get; set; } = string.Empty;
public string AwardingOrganisationTitle { get; set; } = string.Empty;
public int QualificationLevel { get; set; }

public string? FromWhichYear { get; set; }
public string? ToWhichYear { get; set; }
public string? QualificationNumber { get; set; }
public string? Notes { get; set; }
public string? AdditionalRequirements { get; set; }

public string BookmarkUrl {get; set;} = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -27,12 +27,14 @@ private async Task<IEnumerable<NavigationLink>> GetNavigationLinksAsync()
try
{
var navigationLinks = await _contentService.GetNavigationLinks();
if (navigationLinks is null || !navigationLinks.Any())
return await Task.FromResult(Array.Empty<NavigationLink>().AsEnumerable());

return navigationLinks;
}
catch (Exception ex)
{
_logger.LogCritical(ex, "Error retrieving navigation links for footer");
_logger.LogError(ex, "Error retrieving navigation links for footer");

return await Task.FromResult(Array.Empty<NavigationLink>().AsEnumerable());
}
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">@Model.Header</h1>
@Html.Raw(Model.ServiceIntroduction)
<a href="#" role="button" draggable="false" class="govuk-button govuk-button--start" data-module="govuk-button">
<a href="@Url.Action("Get", "QualificationDetails")" role="button" draggable="false" class="govuk-button govuk-button--start" data-module="govuk-button">
@Model.StartButtonText
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" aria-hidden="true" focusable="false">
<path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@{
ViewData["Title"] = "Qualifications";
string[] qualifications = { "eyq-140", "eyq-150", "eyq-160", "eyq-170", "eyq-180", "eyq-220", "eyq-240" };
}

<a href="javascript:window.history.back()" class="govuk-back-link">Back</a>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl">Temporary page</h1>
<table class="govuk-table">
<thead class="govuk-table__head">
<tr class="govuk-table__row">
<th scope="col" class="govuk-table__header">Qualification link</th>
</tr>
</thead>
<tbody class="govuk-table__body">
@foreach (var qualification in qualifications){
<tr class="govuk-table__row">
<td class="govuk-table__cell">@Html.ActionLink(qualification, "Index", new {qualificationId = qualification})</td>
</tr>
}
</tbody>
</table>
</div>
</div>
Loading