diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs b/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs index bace71a8c4..ab79a40b43 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs @@ -1,6 +1,7 @@ namespace DigitalLearningSolutions.Data.Models.Frameworks.Import { using ClosedXML.Excel; + public enum RowStatus { NotYetProcessed, @@ -10,7 +11,8 @@ public enum RowStatus CompetencyUpdated, CompetencyGroupInserted, CompetencyGroupUpdated, - CompetencyGroupAndCompetencyUpdated + CompetencyGroupAndCompetencyUpdated, + InvalidAlwaysShowDescription } public class CompetencyTableRow : BulkCompetency { @@ -18,20 +20,23 @@ public CompetencyTableRow(IXLTable table, IXLRangeRow row) { string? FindFieldValue(string name) { - var colNumber = table.FindColumn(col => col.FirstCell().Value.ToString()?.ToLower() == name).ColumnNumber(); + var colNumber = table.FindColumn(col => col.FirstCell().Value.ToString()?.ToLower() == name.ToLower()).ColumnNumber(); return row.Cell(colNumber).GetValue(); } RowNumber = row.RowNumber(); - id = int.Parse(FindFieldValue("ID")); + id = row.Cell(1).GetValue(); CompetencyGroup = FindFieldValue("CompetencyGroup"); Competency = FindFieldValue("Competency"); CompetencyDescription = FindFieldValue("CompetencyDescription"); - GroupDescription = FindFieldValue("CompetencyGroupDescription"); + GroupDescription = FindFieldValue("GroupDescription"); + AlwaysShowDescriptionRaw = FindFieldValue("AlwaysShowDescription"); + AlwaysShowDescription = bool.TryParse(AlwaysShowDescriptionRaw, out var hasPrn) ? hasPrn : (bool?)null; FlagsCsv = FindFieldValue("FlagsCSV"); RowStatus = RowStatus.NotYetProcessed; } public int RowNumber { get; set; } + public string? AlwaysShowDescriptionRaw { get; set; } public ImportCompetenciesResult.ErrorReason? Error { get; set; } public RowStatus RowStatus { get; set; } public bool Validate() @@ -48,6 +53,10 @@ public bool Validate() { Error = ImportCompetenciesResult.ErrorReason.TooLongCompetencyName; } + else if (!string.IsNullOrWhiteSpace(AlwaysShowDescriptionRaw) && !bool.TryParse(AlwaysShowDescriptionRaw, out _)) + { + Error = ImportCompetenciesResult.ErrorReason.InvalidAlwaysShowDescription; + } return !Error.HasValue; } diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/Import/ImportCompetenciesResult.cs b/DigitalLearningSolutions.Data/Models/Frameworks/Import/ImportCompetenciesResult.cs index 7f78d6f883..3c2bc8e287 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/Import/ImportCompetenciesResult.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/Import/ImportCompetenciesResult.cs @@ -8,10 +8,11 @@ public class ImportCompetenciesResult { public enum ErrorReason { + InvalidId, MissingCompetencyName, - TooLongCompetencyGroupName, TooLongCompetencyName, - AlreadyExists + TooLongCompetencyGroupName, + InvalidAlwaysShowDescription } public ImportCompetenciesResult() { } @@ -20,16 +21,18 @@ IReadOnlyCollection competencyTableRows ) { ProcessedCount = competencyTableRows.Count; - CompetenciesInsertedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); - CompetencyGroupsInsertedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyGroupInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); + CompetencyAddedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); + GroupAddedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyGroupInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); SkippedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.Skipped); Errors = competencyTableRows.Where(dr => dr.Error.HasValue).Select(dr => (dr.RowNumber, dr.Error!.Value)); } public IEnumerable<(int RowNumber, ErrorReason Reason)>? Errors { get; set; } public int ProcessedCount { get; set; } - public int CompetenciesInsertedCount { get; set; } - public int CompetencyGroupsInsertedCount { get; set; } + public int CompetencyAddedCount { get; set; } + public int CompetencyUpdatedCount { get; set; } + public int GroupAddedCount { get; set; } + public int GroupUpdatedCount { get; set; } public int SkippedCount { get; set; } } } diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs index d1d8034f09..d713235f4f 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs @@ -1,21 +1,19 @@ using ClosedXML.Excel; using DigitalLearningSolutions.Data.Exceptions; -using DigitalLearningSolutions.Data.Migrations; -using DigitalLearningSolutions.Data.Utilities; using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.Models; using DigitalLearningSolutions.Web.Services; using DigitalLearningSolutions.Web.ViewModels.Frameworks; using GDS.MultiPageFormData.Enums; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; +using System.IO; namespace DigitalLearningSolutions.Web.Controllers.FrameworksController { public partial class FrameworksController { [Route("/Framework/{frameworkId}/{tabname}/Import")] - public IActionResult ImportCompetencies(int frameworkId, string tabname) + public IActionResult ImportCompetencies(int frameworkId, string tabname, bool isNotBlank) { var adminId = GetAdminId(); var userRole = frameworkService.GetAdminUserRoleForFrameworkId(adminId, frameworkId); @@ -23,7 +21,8 @@ public IActionResult ImportCompetencies(int frameworkId, string tabname) return StatusCode(403); var model = new ImportCompetenciesViewModel() { - FrameworkId = frameworkId + FrameworkId = frameworkId, + IsNotBlank = isNotBlank }; return View("Developer/ImportCompetencies", model); } @@ -41,7 +40,8 @@ public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption) } [HttpPost] [Route("/Framework/{frameworkId}/{tabname}/Import")] - public IActionResult StartImport(ImportCompetenciesViewModel model, string tabname) + [Route("/Framework/{frameworkId}/{tabname}/ImportCompleted")] + public IActionResult StartImport(ImportCompetenciesViewModel model, string tabname, bool isNotBlank) { if (!ModelState.IsValid) return View("Developer/ImportCompetencies", model); @@ -51,11 +51,11 @@ public IActionResult StartImport(ImportCompetenciesViewModel model, string tabna var workbook = new XLWorkbook(model.ImportFile.OpenReadStream()); if (!workbook.Worksheets.Contains(ImportCompetenciesFromFileService.CompetenciesSheetName)) { - ModelState.AddModelError("InvalidWorksheet", CommonValidationErrorMessages.InvalidCompetenciesUploadExcelFile); + ModelState.AddModelError("ImportFile", CommonValidationErrorMessages.InvalidCompetenciesUploadExcelFile); return View("Developer/ImportCompetencies", model); } var competenciesFileName = FileHelper.UploadFile(webHostEnvironment, model.ImportFile); - setupBulkUploadData(model.FrameworkId, adminUserID, competenciesFileName, tabname); + setupBulkUploadData(model.FrameworkId, adminUserID, competenciesFileName, tabname, isNotBlank); return RedirectToAction("ImportCompleted", "Frameworks", new { frameworkId = model.FrameworkId, tabname }); } @@ -72,15 +72,33 @@ public IActionResult StartImport(ImportCompetenciesViewModel model, string tabna [Route("/Framework/{frameworkId}/{tabname}/ImportCompleted")] public IActionResult ImportCompleted() { - return View("Developer/ImportCompleted"); + var data = GetBulkUploadData(); + var uploadDir = Path.Combine(webHostEnvironment.WebRootPath, "Uploads\\"); + var filePath = Path.Combine(uploadDir, data.CompetenciesFileName); + var workbook = new XLWorkbook(filePath); + try + { + var results = importCompetenciesFromFileService.PreProcessCompetenciesTable(workbook); + var resultsModel = new ImportCompetenciesPreProcessViewModel(results) { IsNotBlank = data.IsNotBlank, TabName = data.TabName }; + data.CompetenciesToProcessCount = resultsModel.ToProcessCount; + data.CompetenciesToAddCount = resultsModel.CompetenciesToAddCount; + data.CompetenciesToUpdateCount = resultsModel.CompetenciesToUpdateCount; + setBulkUploadData(data); + return View("Developer/ImportCompleted", resultsModel); + } + catch (InvalidHeadersException) + { + FileHelper.DeleteFile(webHostEnvironment, data.CompetenciesFileName); + return View("ImportFailed"); + } } - private void setupBulkUploadData(int frameworkId, int adminUserID, string competenciessFileName, string tabName) + private void setupBulkUploadData(int frameworkId, int adminUserID, string competenciessFileName, string tabName, bool isNotBlank) { TempData.Clear(); multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("BulkCompetencyDataCWF"), TempData); var today = clockUtility.UtcToday; - var bulkUploadData = new BulkCompetenciesData(frameworkId, adminUserID, competenciessFileName, tabName); + var bulkUploadData = new BulkCompetenciesData(frameworkId, adminUserID, competenciessFileName, tabName, isNotBlank); setBulkUploadData(bulkUploadData); } private void setBulkUploadData(BulkCompetenciesData bulkUploadData) diff --git a/DigitalLearningSolutions.Web/Helpers/CommonValidationErrorMessages.cs b/DigitalLearningSolutions.Web/Helpers/CommonValidationErrorMessages.cs index e76eec613c..531a528e98 100644 --- a/DigitalLearningSolutions.Web/Helpers/CommonValidationErrorMessages.cs +++ b/DigitalLearningSolutions.Web/Helpers/CommonValidationErrorMessages.cs @@ -36,7 +36,7 @@ public static class CommonValidationErrorMessages public const string CentreNameAlreadyExist = "The centre name you have entered already exists, please enter a different centre name"; public const string MaxBulkUploadRowsLimit = "File must contain no more than {0} rows"; public const string InvalidBulkUploadExcelFile = "The uploaded file must contain a \"DelegatesBulkUpload\" worksheet. Use the \"Download delegates\" button to generate a template."; - public const string InvalidCompetenciesUploadExcelFile = "The uploaded file must contain a \"CompetenciesBulkUpload\" worksheet. Use the \"Download competencies\" button to generate a template."; + public const string InvalidCompetenciesUploadExcelFile = "The uploaded file must contain a \"CompetenciesBulkUpload\" worksheet. Use the \"Download template\" button to generate a template."; public const string ReportFilterReturnsTooManyRows = "The report frequency is too high for the date range. Choose a lower report frequency (or shorten the date range)"; } } diff --git a/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs b/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs index 173b1d0a52..290e263477 100644 --- a/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs +++ b/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs @@ -6,22 +6,24 @@ namespace DigitalLearningSolutions.Web.Models public class BulkCompetenciesData { public BulkCompetenciesData() { } - public BulkCompetenciesData(int frameworkId, int adminUserId, string competenciesFileName, string tabName) + public BulkCompetenciesData(int frameworkId, int adminUserId, string competenciesFileName, string tabName, bool isNotBlank) { FrameworkId = frameworkId; AdminUserId = adminUserId; CompetenciesFileName = competenciesFileName; TabName = tabName; + IsNotBlank = isNotBlank; } public int FrameworkId { get; set; } public string TabName { get; set; } public int AdminUserId { get; set; } + public bool IsNotBlank { get; set; } public string CompetenciesFileName { get; set; } public List AssessmentQuestionIDs { get; set; } public int? AddAssessmentQuestionOption { get; set; } - public int ToProcessCount { get; set; } - public int ToAddCount { get; set; } - public int ToUpdateCount { get; set; } + public int CompetenciesToProcessCount { get; set; } + public int CompetenciesToAddCount { get; set; } + public int CompetenciesToUpdateCount { get; set; } public int LastRowProcessed { get; set; } public int SubtotalCompetenciesAdded { get; set; } public int SubtotalCompetenciesUpdated { get; set; } diff --git a/DigitalLearningSolutions.Web/Models/BulkCompetenciesResult.cs b/DigitalLearningSolutions.Web/Models/BulkCompetenciesResult.cs deleted file mode 100644 index e770ac39b7..0000000000 --- a/DigitalLearningSolutions.Web/Models/BulkCompetenciesResult.cs +++ /dev/null @@ -1,40 +0,0 @@ -using DigitalLearningSolutions.Data.Models.Frameworks.Import; -using System.Collections.Generic; -using System.Linq; - -namespace DigitalLearningSolutions.Web.Models -{ - public class BulkCompetenciesResult - { - public enum ErrorReason - { - InvalidId, - MissingCompetencyName, - TooLongCompetencyName, - TooLongCompetencyGroupName, - InvalidAlwaysShowDescription - } - - public BulkCompetenciesResult() { } - - public BulkCompetenciesResult( - IReadOnlyCollection competencyRows - ) - { - ProcessedCount = competencyRows.Count; - CompetencyAddedCount = competencyRows.Count(cr => cr.RowStatus == RowStatus.CompetencyInserted || cr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); - GroupAddedCount = competencyRows.Count(cr => cr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted); - GroupUpdatedCount = competencyRows.Count(cr => cr.RowStatus == RowStatus.CompetencyGroupUpdated || cr.RowStatus == RowStatus.CompetencyGroupAndCompetencyUpdated); - SkippedCount = competencyRows.Count(cr => cr.RowStatus == RowStatus.Skipped); - Errors = (IEnumerable<(int RowNumber, ErrorReason Reason)>)competencyRows.Where(cr => cr.Error.HasValue).Select(cr => (cr.RowNumber, cr.Error!.Value)); - } - - public IEnumerable<(int RowNumber, ErrorReason Reason)>? Errors { get; set; } - public int ProcessedCount { get; set; } - public int CompetencyAddedCount { get; set; } - public int CompetencyUpdatedCount { get; set; } - public int GroupAddedCount { get; set; } - public int GroupUpdatedCount { get; set; } - public int SkippedCount { get; set; } - } -} diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 0ba48b08d7..1746ebe904 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -12,12 +12,14 @@ namespace DigitalLearningSolutions.Web.Services using DigitalLearningSolutions.Data.Exceptions; using DigitalLearningSolutions.Data.Helpers; using DigitalLearningSolutions.Data.Models.Frameworks.Import; + using DigitalLearningSolutions.Web.Models; using Microsoft.AspNetCore.Http; public interface IImportCompetenciesFromFileService { byte[] GetCompetencyFileForFramework(int frameworkId, bool v); - public ImportCompetenciesResult ProcessCompetenciesFromFile(IFormFile file, int adminUserId, int frameworkId); + public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook); + public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId); } public class ImportCompetenciesFromFileService : IImportCompetenciesFromFileService { @@ -30,17 +32,39 @@ IFrameworkService frameworkService { this.frameworkService = frameworkService; } - public ImportCompetenciesResult ProcessCompetenciesFromFile(IFormFile file, int adminUserId, int frameworkId) + public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook) + { + var table = OpenCompetenciesTable(workbook); + var competencyRows = table.Rows().Skip(1).Select(row => new CompetencyTableRow(table, row)).ToList(); + foreach (var competencyRow in competencyRows) + { + PreProcessCompetencyRow(competencyRow); + } + return new ImportCompetenciesResult(competencyRows); + } + private void PreProcessCompetencyRow(CompetencyTableRow competencyRow) + { + competencyRow.Validate(); + if (competencyRow.id == null) + { + competencyRow.RowStatus = RowStatus.CompetencyInserted; + } + else + { + competencyRow.RowStatus = RowStatus.CompetencyUpdated; + } + } + public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId) { int maxFrameworkCompetencyId = frameworkService.GetMaxFrameworkCompetencyID(); int maxFrameworkCompetencyGroupId = frameworkService.GetMaxFrameworkCompetencyGroupID(); - var table = OpenCompetenciesTable(file); + var table = OpenCompetenciesTable(workbook); return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId); } - internal IXLTable OpenCompetenciesTable(IFormFile file) + internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook) { - var workbook = new XLWorkbook(file.OpenReadStream()); var worksheet = workbook.Worksheet(1); + worksheet.Columns(1, 15).Unhide(); if (worksheet.Tables.Count() == 0) { throw new InvalidHeadersException(); @@ -112,11 +136,15 @@ private static bool ValidateHeaders(IXLTable table) { var expectedHeaders = new List { - "competency group", - "competency name", - "competency description" + "ID", + "CompetencyGroup", + "GroupDescription", + "Competency", + "CompetencyDescription", + "AlwaysShowDescription", + "FlagsCSV" }.OrderBy(x => x); - var actualHeaders = table.Fields.Select(x => x.Name.ToLower()).OrderBy(x => x); + var actualHeaders = table.Fields.Select(x => x.Name).OrderBy(x => x); return actualHeaders.SequenceEqual(expectedHeaders); } @@ -128,6 +156,13 @@ public byte[] GetCompetencyFileForFramework(int frameworkId, bool blank) { ClosedXmlHelper.HideWorkSheetColumn(workbook, "ID"); } + var options = new List { "TRUE", "FALSE" }; + ClosedXmlHelper.AddValidationListToWorksheetColumn(workbook, 6, options); + var rowCount = workbook.Worksheet(1).RangeUsed().RowCount(); + ClosedXmlHelper.AddValidationRangeToWorksheetColumn(workbook, 1, 1, rowCount, 1); + // Calculate the workbook + workbook.CalculateMode = XLCalculateMode.Auto; + workbook.RecalculateAllFormulas(); using var stream = new MemoryStream(); workbook.SaveAs(stream); return stream.ToArray(); diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesPreProcessViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesPreProcessViewModel.cs index 45f72c2bca..fac6c884c1 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesPreProcessViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesPreProcessViewModel.cs @@ -2,12 +2,13 @@ using System; using DigitalLearningSolutions.Web.Models; using System.Linq; +using DigitalLearningSolutions.Data.Models.Frameworks.Import; namespace DigitalLearningSolutions.Web.ViewModels.Frameworks { public class ImportCompetenciesPreProcessViewModel { - public ImportCompetenciesPreProcessViewModel(BulkCompetenciesResult bulkCompetenciesResult) + public ImportCompetenciesPreProcessViewModel(ImportCompetenciesResult bulkCompetenciesResult) { ToProcessCount = bulkCompetenciesResult.ProcessedCount; CompetenciesToAddCount = bulkCompetenciesResult.CompetencyAddedCount; @@ -25,21 +26,24 @@ public ImportCompetenciesPreProcessViewModel(BulkCompetenciesResult bulkCompeten public int CompetencyGroupsToAddCount { get; set; } public int CompetencyGroupsToUpdateCount { get; set; } public int ToUpdateOrSkipCount { get; set; } + public string? ImportFile { get; set; } + public bool IsNotBlank { get; set; } + public string TabName { get; set; } - private static string MapReasonToErrorMessage(BulkCompetenciesResult.ErrorReason reason) + private static string MapReasonToErrorMessage(ImportCompetenciesResult.ErrorReason reason) { return reason switch { - BulkCompetenciesResult.ErrorReason.TooLongCompetencyGroupName => + ImportCompetenciesResult.ErrorReason.TooLongCompetencyGroupName => "Group name must be 255 characters or less.", - BulkCompetenciesResult.ErrorReason.MissingCompetencyName => + ImportCompetenciesResult.ErrorReason.MissingCompetencyName => "Competency is blank. Competency is a required field and cannot be left blank", - BulkCompetenciesResult.ErrorReason.InvalidId => + ImportCompetenciesResult.ErrorReason.InvalidId => "The ID provided does not match a Competency ID in this Framework", - BulkCompetenciesResult.ErrorReason.TooLongCompetencyName => + ImportCompetenciesResult.ErrorReason.TooLongCompetencyName => "Competency must be 255 characters or less.", - BulkCompetenciesResult.ErrorReason.InvalidAlwaysShowDescription => - "Always show description is invalid. The Always show description field must contain 'TRUE' or 'FALSE'", + ImportCompetenciesResult.ErrorReason.InvalidAlwaysShowDescription => + "Always show description is invalid. The Always show description field must contain 'TRUE' or 'FALSE'", _ => "Unspecified error.", }; } diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesResultsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesResultsViewModel.cs index 8267146c71..d76b0338ae 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesResultsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesResultsViewModel.cs @@ -9,8 +9,8 @@ public class ImportCompetenciesResultsViewModel public ImportCompetenciesResultsViewModel(ImportCompetenciesResult importCompetenciesResult) { ProcessedCount = importCompetenciesResult.ProcessedCount; - CompetenciesInsertedCount = importCompetenciesResult.CompetenciesInsertedCount; - CompetencyGroupsInsertedCount = importCompetenciesResult.CompetencyGroupsInsertedCount; + CompetenciesInsertedCount = importCompetenciesResult.CompetencyAddedCount; + CompetencyGroupsInsertedCount = importCompetenciesResult.GroupAddedCount; SkippedCount = importCompetenciesResult.SkippedCount; Errors = importCompetenciesResult.Errors.Select(x => (x.RowNumber, MapReasonToErrorMessage(x.Reason))); } diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesViewModel.cs index eb44330a20..27fa9f38b6 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/ImportCompetenciesViewModel.cs @@ -7,6 +7,7 @@ public class ImportCompetenciesViewModel { public int FrameworkId { get; set; } + public bool IsNotBlank { get; set; } [Required(ErrorMessage = "Import competencies file is required")] [AllowedExtensions(new[] { ".xlsx" }, "Import competencies file must be in xlsx format")] [MaxFileSize(5 * 1024 * 1024, "Maximum allowed file size is 5MB")] diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompetencies.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompetencies.cshtml index c91ec018ca..56ae470de0 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompetencies.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompetencies.cshtml @@ -30,59 +30,72 @@ { } -

Bulk upload or update competencies

+

Bulk upload @(Model.IsNotBlank ? "or update" : "") competencies

-

- To bulk add and/or update competencies in your framework, download an Excel workbook using one of the options below. -

-
-
+
+ @if (Model.IsNotBlank) + { -
- -

- What would you like to download? -

-
-
- Select one option -
-
-
- - +

+ To bulk add and/or update competencies in your framework, download an Excel workbook using one of the options below. +

+ +
+ +

+ What would you like to download? +

+
+
+ Select one option
-
-
- This Excel file will be empty.
- New competencies can be added by including their details on a blank row. +
+
+ +
- - Download template - -
-
- - -
-
-
- This Excel file will include all existing competencies whose details you can update.
- New competencies can be added by including their details on a blank row. +
+
+ This Excel file will be empty.
+ New competencies can be added by including their details on a blank row. +
+ + Download template + +
+
+ + +
+
+
+ This Excel file will include all existing competencies whose details you can update.
+ New competencies can be added by including their details on a blank row. +
+ + Download competencies +
- - Download competencies -
-
-
+
+ + } + else + { +

+ Download a blank template for bulk adding competencies to your framework. This Excel file will be empty. New competencies can be added by including their details on a blank row. +

+ + Download template + + } + +
-
-

Upload file

Once you have an Excel competencies workbook, add or update competencies to the worksheet, save and start the upload process. diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompleted.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompleted.cshtml index 716575afc8..5b355f572e 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompleted.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompleted.cshtml @@ -26,3 +26,58 @@ } +

Delegate file uploaded

+

@(Model.ErrorCount == 0 ? "Your file is error free and ready to be processed. Check the information below looks, correct before processing" : "Your file contains the following, including some errors:")

+
    +
  • @Model.ToProcessCount @(Model.ToProcessCount == 1 ? "row" : "rows") to process
  • +
  • @Model.CompetenciesToAddCount new @(Model.CompetenciesToAddCount == 1 ? "competency" : "competencies") to add
  • +
  • @Model.ToUpdateOrSkipCount delegate @(Model.ToUpdateOrSkipCount == 1 ? "record" : "records") to update (or skip if unchanged)
  • + @if (Model.ErrorCount > 0) + { +
  • @Model.ErrorCount @(Model.ErrorCount == 1 ? "row" : "rows") containing errors that cannot be processed
  • + } + else + { +
  • No errors
  • + } +
+@if (Model.ErrorCount == 0) +{ + Continue +} +else +{ +

Check the information below. You will need fix these errors before continuing or remove the rows with errors from your spreadsheet:

+
+ + Error: @Model.ErrorCount delegate @(Model.ErrorCount == 1 ? "row" : "rows") contain errors and cannot be processed + +
+ @foreach (var (rowNumber, errorMessage) in Model.Errors) + { +
+
+ Row @rowNumber +
+
+ @errorMessage +
+ +
+ } +
+
+

Upload corrected file

+

+ Once you have made corrections to the Excel competency workbook to address the errors above, save and restart the upload process. +

+
+ + + + + + + +} + diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml index 7fa159981d..a1097f6e18 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml @@ -81,7 +81,7 @@ else }