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
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions src/Dfe.EarlyYearsQualification.ContentfulUpload/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
csv/*
*.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="contentful.csharp" Version="7.5.0" />
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Dfe.EarlyYearsQualification.Content\Dfe.EarlyYearsQualification.Content.csproj" />
</ItemGroup>

</Project>
292 changes: 292 additions & 0 deletions src/Dfe.EarlyYearsQualification.ContentfulUpload/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
using Contentful.Core;
using Contentful.Core.Configuration;
using Contentful.Core.Models;
using Contentful.Core.Models.Management;
using Contentful.Core.Search;
using Dfe.EarlyYearsQualification.Content.Entities;
using Microsoft.VisualBasic.FileIO;

namespace Dfe.EarlyYearsQualification.ContentfulUpload;

public class Program
{
public const string locale = "en-US";
public const string SpaceId = "";
public const string ManagementApiKey = "";

public static async Task Main(string[] args)
{
var client = new ContentfulManagementClient(new HttpClient(), new ContentfulOptions
{
ManagementApiKey = ManagementApiKey,
SpaceId = SpaceId,
MaxNumberOfRateLimitRetries = 10,
});

await SetUpContentModels(client);

await PopulateContentfulWithQualifications(client);
}

private static async Task PopulateContentfulWithQualifications(ContentfulManagementClient client)
{
// Check existing entries and identify those to update, and those to create.
var currentEntries = await client.GetEntriesForLocale(new QueryBuilder<Entry<dynamic>>().ContentTypeIs("Qualification").Limit(1000), locale);

var qualificationsToAddOrUpdate = GetQualificationsToAddOrUpdate();

foreach (var qualification in qualificationsToAddOrUpdate)
{
var entryToAddOrUpdate = BuildEntryFromQualification(qualification);
var existingEntry = currentEntries.Where(x => x.SystemProperties.Id == qualification.QualificationId.ToString()).FirstOrDefault();

if (existingEntry != null)
{
// Update existing entry
entryToAddOrUpdate.SystemProperties.Version = existingEntry!.SystemProperties.Version!.Value;
}

// Create new entry
var entryToPublish = await client.CreateOrUpdateEntry(entryToAddOrUpdate, contentTypeId: "Qualification", version: entryToAddOrUpdate.SystemProperties.Version);

await client.PublishEntry(entryToPublish.SystemProperties.Id, entryToPublish.SystemProperties.Version!.Value);
}
}

private static async Task SetUpContentModels(ContentfulManagementClient client)
{
// Check current version of model
var currentModels = await client.GetContentTypes();

var currentModel = currentModels.Where(x => x.SystemProperties.Id == "Qualification").FirstOrDefault();

var version = currentModel != null && currentModel.SystemProperties.Version != null ? currentModel.SystemProperties.Version!.Value : 1;

var contentType = new ContentType
{
SystemProperties = new SystemProperties
{
Id = "Qualification",
},
Name = "Qualification",
Description = "Model for storing all the early years qualifications",
DisplayField = "qualificationName",
Fields =
[
new Field()
{
Name = "Qualification ID",
Id = "qualificationId",
Type = "Symbol",
Required = true,
Validations = new List<IFieldValidator>() {
new UniqueValidator()
}
},
new Field()
{
Name = "Qualification Name",
Id = "qualificationName",
Type = "Symbol",
Required = true,
},
new Field()
{
Name = "Qualification Level",
Id = "qualificationLevel",
Type = "Integer",
Required = true,
},
new Field()
{
Name = "Awarding Organisation Title",
Id = "awardingOrganisationTitle",
Type = "Symbol",
Required = true,
},
new Field()
{
Name = "From Which Year",
Id = "fromWhichYear",
Type = "Symbol",
},
new Field()
{
Name = "To Which Year",
Id = "toWhichYear",
Type = "Symbol",
},
new Field()
{
Name = "Qualification Number",
Id = "qualificationNumber",
Type = "Symbol",
},
new Field()
{
Name = "Notes",
Id = "notes",
Type = "Text",
},
new Field()
{
Name = "Additional Requirements",
Id = "additionalRequirements",
Type = "Text",
},
]
};

var typeToActivate = await client.CreateOrUpdateContentType(contentType, version: version);
await client.ActivateContentType("Qualification", version: typeToActivate.SystemProperties.Version!.Value);

Thread.Sleep(2000); // Allows the API time to activate the content type
await SetHelpText(client, typeToActivate);
}

private static async Task SetHelpText(ContentfulManagementClient client, ContentType typeToActivate)
{
var editorInterface = await client.GetEditorInterface("Qualification");
SetHelpTextForField(editorInterface, "qualificationId", "The unique identifier used to reference the qualification");
SetHelpTextForField(editorInterface, "qualificationName", "The name of the qualification");
SetHelpTextForField(editorInterface, "qualificationLevel", "The level of the qualification", SystemWidgetIds.NumberEditor);
SetHelpTextForField(editorInterface, "awardingOrganisationTitle", "The name of the awarding organisation");
SetHelpTextForField(editorInterface, "fromWhichYear", "The date from which the qualification is considered full and relevant");
SetHelpTextForField(editorInterface, "toWhichYear", "The date on which the qualification stops being considered full and relevant");
SetHelpTextForField(editorInterface, "qualificationNumber", "The number of the qualification");
SetHelpTextForField(editorInterface, "notes", "The corresponding notes for the qualification", SystemWidgetIds.MultipleLine);
SetHelpTextForField(editorInterface, "additionalRequirements", "The additional requirements for the qualification", SystemWidgetIds.MultipleLine);

await client.UpdateEditorInterface(editorInterface, "Qualification", typeToActivate.SystemProperties.Version!.Value);
}

private static void SetHelpTextForField(EditorInterface editorInterface, string fieldId, string helpText, string widgetId = SystemWidgetIds.SingleLine)
{
editorInterface.Controls.First(x => x.FieldId == fieldId).Settings = new EditorInterfaceControlSettings() { HelpText = helpText };
editorInterface.Controls.First(x => x.FieldId == fieldId).WidgetId = widgetId;
}

private static Entry<dynamic> BuildEntryFromQualification(Qualification qualification)
{
var entry = new Entry<dynamic>
{
SystemProperties = new SystemProperties
{
Id = qualification.QualificationId.ToString(),
Version = 1
},

Fields = new
{
qualificationId = new Dictionary<string, string>()
{
{ locale, qualification.QualificationId}
},

qualificationName = new Dictionary<string, string>()
{
{ locale, qualification.QualificationName }
},

awardingOrganisationTitle = new Dictionary<string, string>()
{
{ locale, qualification.AwardingOrganisationTitle }
},

qualificationLevel = new Dictionary<string, int>()
{
{ locale, qualification.QualificationLevel }
},

fromWhichYear = new Dictionary<string, string>()
{
{ locale, qualification.FromWhichYear ?? "" }
},

toWhichYear = new Dictionary<string, string>()
{
{ locale, qualification.ToWhichYear ?? "" }
},

qualificationNumber = new Dictionary<string, string>()
{
{ locale, qualification.QualificationNumber ?? "" }
},

notes = new Dictionary<string, string>()
{
{ locale, qualification.Notes ?? "" }
},

additionalRequirements = new Dictionary<string, string>()
{
{ locale, qualification.AdditionalRequirements ?? "" }
},
}
};

return entry;
}

private static List<Qualification> GetQualificationsToAddOrUpdate()
{
// var csv = new List<string[]>();
// var lines = System.IO.File.ReadAllLines(@"./csv/ey-quals-full.csv");

// foreach (string line in lines)
// csv.Add(line.Split(','));
var lines = ReadCsvFile(@"./csv/ey-quals-full-updated.csv");

var listObjResult = new List<Qualification>();

for (int i = 0; i < lines.Count; i++)
{
var qualificationId = lines[i][0];
var qualificationName = lines[i][4];
var awardingOrganisationTitle = lines[i][5];
var qualificationLevel = int.Parse(lines[i][1]);
var fromWhichYear = lines[i][2];
var toWhichYear = lines[i][3];
var qualificationNumber = lines[i][6];
var notes = lines[i][7];
var additionalRequirements = lines[i][8];

listObjResult.Add(new Qualification(
qualificationId,
qualificationName,
awardingOrganisationTitle,
qualificationLevel,
fromWhichYear,
toWhichYear,
qualificationNumber,
notes,
additionalRequirements
));
}

return listObjResult;
}

private static List<string[]> ReadCsvFile(string filePath)
{
var result = new List<string[]>();
using (TextFieldParser csvParser = new TextFieldParser(filePath))
{
csvParser.SetDelimiters(new string[] { "," });
csvParser.HasFieldsEnclosedInQuotes = true;

// Skip the row with the column names
csvParser.ReadLine();

while (!csvParser.EndOfData)
{
// Read current line fields, pointer moves to the next line.
string[] fields = csvParser.ReadFields()!;
result.Add(fields);
}
}
return result;
}
}


25 changes: 25 additions & 0 deletions src/contentful-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

.env
Loading
Loading