From 0c8f5bab683fd76fd6867ae56cd03c23f680e91d Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Tue, 7 Jan 2025 16:35:53 +0000 Subject: [PATCH 01/12] TD-5163 Process imported competencies --- .../DataServices/FrameworkDataService.cs | 26 +++-- .../Models/Frameworks/FrameworkCompetency.cs | 1 + .../ImportCompetencies.cs | 14 +++ .../Models/BulkCompetenciesData.cs | 2 + .../Services/FrameworkService.cs | 22 ++-- .../ImportCompetenciesFromFileService.cs | 106 +++++++++++++++--- 6 files changed, 132 insertions(+), 39 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index c50a8c32cf..9c853ea44f 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -131,10 +131,10 @@ bool zeroBased int InsertCompetency(string name, string? description, int adminId); - int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId); + int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool alwaysShowDescription = false); int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify); - void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); + int AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); void UpdateFrameworkCustomFlag(int frameworkId, int id, string flagName, string flagGroup, string flagTagClass); void AddFrameworkDefaultQuestion(int frameworkId, int assessmentQuestionId, int adminId, bool addToExisting); @@ -205,7 +205,7 @@ void UpdateFrameworkCompetencyGroup( int adminId ); - void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId); + void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription); void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction); @@ -656,7 +656,8 @@ public int InsertFrameworkCompetency( int competencyId, int? frameworkCompetencyGroupID, int adminId, - int frameworkId + int frameworkId, + bool alwaysShowDescription = false ) { if ((competencyId < 1) | (adminId < 1) | (frameworkId < 1)) @@ -978,7 +979,7 @@ FROM FrameworkCompetencyGroups AS fcg public FrameworkCompetency? GetFrameworkCompetencyById(int Id) { return connection.QueryFirstOrDefault( - @"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering + @"SELECT fc.ID, c.ID AS CompetencyID, c.Name, c.Description, fc.Ordering, c.AlwaysShowDescription FROM FrameworkCompetencies AS fc INNER JOIN Competencies AS c ON fc.CompetencyID = c.ID WHERE fc.ID = @Id", @@ -1045,7 +1046,7 @@ int adminId } } - public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId) + public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription) { if ((frameworkCompetencyId < 1) | (adminId < 1) | (name.Length < 3)) { @@ -1057,10 +1058,10 @@ public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, st //DO WE NEED SOMETHING IN HERE TO CHECK WHETHER IT IS USED ELSEWHERE AND WARN THE USER? var numberOfAffectedRows = connection.Execute( - @"UPDATE Competencies SET Name = @name, Description = @description, UpdatedByAdminID = @adminId + @"UPDATE Competencies SET Name = @name, Description = @description, UpdatedByAdminID = @adminId, AlwaysShowDescription = CASE WHEN @alwaysShowDescription IS NULL THEN AlwaysShowDescription ELSE @alwaysShowDescription END FROM Competencies INNER JOIN FrameworkCompetencies AS fc ON Competencies.ID = fc.CompetencyID WHERE (fc.Id = @frameworkCompetencyId)", - new { name, description, adminId, frameworkCompetencyId } + new { name, description, adminId, frameworkCompetencyId, alwaysShowDescription} ); if (numberOfAffectedRows < 1) { @@ -1094,11 +1095,12 @@ SELECT FlagID FROM CompetencyFlags new { competencyId, frameworkId }); } - public void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass) + public int AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass) { - connection.Execute( - @$"INSERT INTO Flags(FrameworkID, FlagName, FlagGroup, FlagTagClass) - VALUES(@frameworkId, @flagName, @flagGroup, @flagTagClass)", + return connection.QuerySingle( + @"INSERT INTO Flags(FrameworkID, FlagName, FlagGroup, FlagTagClass) + OUTPUT INSERTED.ID + VALUES(@frameworkId, @flagName, @flagGroup, @flagTagClass);", new { frameworkId, flagName, flagGroup, flagTagClass }); } diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs index 56bd78ddc1..a42d3d10fe 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/FrameworkCompetency.cs @@ -14,6 +14,7 @@ public class FrameworkCompetency : BaseSearchableItem public int AssessmentQuestions { get; set; } public int CompetencyLearningResourcesCount { get; set; } public string? FrameworkName { get; set; } + public bool? AlwaysShowDescription { get; set; } public override string SearchableName { diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs index 08ff1eb0ff..735db4a3e5 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs @@ -224,6 +224,20 @@ public IActionResult ImportSummary() var model = new ImportSummaryViewModel(data); return View("Developer/Import/ImportSummary", model); } + [HttpPost] + [Route("/Framework/{frameworkId}/{tabname}/Import/Summary")] + public IActionResult ImportSummarySubmit() + { + var data = GetBulkUploadData(); + var adminId = GetAdminId(); + var uploadDir = Path.Combine(webHostEnvironment.WebRootPath, "Uploads\\"); + var filePath = Path.Combine(uploadDir, data.CompetenciesFileName); + var workbook = new XLWorkbook(filePath); + var results = importCompetenciesFromFileService.ProcessCompetenciesFromFile(workbook, adminId, data.FrameworkId, data.FrameworkVocubulary, data.ReorderCompetenciesOption, data.AddAssessmentQuestionsOption, data.AddCustomAssessmentQuestion ? (int)data.CustomAssessmentQuestionID : 0, data.AddDefaultAssessmentQuestions ? data.DefaultQuestionIDs : []); + data.ImportCompetenciesResult = results; + setBulkUploadData(data); + return RedirectToAction("UploadResults", "Frameworks", new { frameworkId = data.FrameworkId, tabname = data.TabName }); + } [Route("CancelImport")] public IActionResult CancelImport() { diff --git a/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs b/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs index 2d3ce9e4ac..344c674a89 100644 --- a/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs +++ b/DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs @@ -1,4 +1,5 @@ using DigitalLearningSolutions.Data.Models.Frameworks; +using DigitalLearningSolutions.Data.Models.Frameworks.Import; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -42,5 +43,6 @@ public BulkCompetenciesData(DetailFramework framework, int adminUserId, string c public int SubtotalCompetenciesUpdated { get; set; } public int SubTotalSkipped { get; set; } public IEnumerable<(int RowNumber, string ErrorMessage)> Errors { get; set; } = Enumerable.Empty<(int, string)>(); + public ImportCompetenciesResult ImportCompetenciesResult { get;set;} } } diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs index c586fa071b..c4f7035ba9 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs @@ -119,16 +119,16 @@ bool zeroBased int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId); - int InsertFrameworkCompetencyGroup(int groupId, int frameworkID, int adminId); + int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool alwaysShowDescription = false); IEnumerable GetAllCompetenciesForAdminId(string name, int adminId); int InsertCompetency(string name, string? description, int adminId); - int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId); + int InsertFrameworkCompetencyGroup(int groupId, int frameworkID, int adminId); int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify); - void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); + int AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); void UpdateFrameworkCustomFlag(int frameworkId, int id, string flagName, string flagGroup, string flagTagClass); void AddFrameworkDefaultQuestion(int frameworkId, int assessmentQuestionId, int adminId, bool addToExisting); @@ -199,7 +199,7 @@ void UpdateFrameworkCompetencyGroup( int adminId ); - void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId); + void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription = false); void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction); @@ -277,9 +277,9 @@ public void AddCompetencyAssessmentQuestion(int frameworkCompetencyId, int asses frameworkDataService.AddCompetencyAssessmentQuestion(frameworkCompetencyId, assessmentQuestionId, adminId); } - public void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass) + public int AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass) { - frameworkDataService.AddCustomFlagToFramework(frameworkId, flagName, flagGroup, flagTagClass); + return frameworkDataService.AddCustomFlagToFramework(frameworkId, flagName, flagGroup, flagTagClass); } public void AddFrameworkDefaultQuestion(int frameworkId, int assessmentQuestionId, int adminId, bool addToExisting) @@ -602,9 +602,9 @@ public int InsertCompetencyGroup(string groupName, string? groupDescription, int return frameworkDataService.InsertCompetencyGroup(groupName, groupDescription, adminId); } - public int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId) + public int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool alwaysShowDescription = false) { - return frameworkDataService.InsertFrameworkCompetency(competencyId, frameworkCompetencyGroupID, adminId, frameworkId); + return frameworkDataService.InsertFrameworkCompetency(competencyId, frameworkCompetencyGroupID, adminId, frameworkId, alwaysShowDescription); } public int InsertFrameworkCompetencyGroup(int groupId, int frameworkID, int adminId) @@ -672,9 +672,9 @@ public void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selec return frameworkDataService.UpdateFrameworkBranding(frameworkId, brandId, categoryId, topicId, adminId); } - public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId) - { - frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name, description, adminId); + public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription) + { + frameworkDataService.UpdateFrameworkCompetency(frameworkCompetencyId, name, description, adminId, alwaysShowDescription); } public void UpdateFrameworkCompetencyGroup(int frameworkCompetencyGroupId, int competencyGroupId, string name, string? description, int adminId) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index a26793b651..2367bcbefd 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -12,12 +12,13 @@ namespace DigitalLearningSolutions.Web.Services using DigitalLearningSolutions.Data.Exceptions; using DigitalLearningSolutions.Data.Helpers; using DigitalLearningSolutions.Data.Models.Frameworks.Import; + using DocumentFormat.OpenXml.Office2010.Excel; public interface IImportCompetenciesFromFileService { byte[] GetCompetencyFileForFramework(int frameworkId, bool isBlank, string vocabulary); public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook, string vocabulary, int frameworkId); - public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary); + public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary, int reorderCompetenciesOption, int addAssessmentQuestionsOption, int customAssessmentQuestionID, List defaultQuestionIds); } public class ImportCompetenciesFromFileService : IImportCompetenciesFromFileService { @@ -71,12 +72,12 @@ private void PreProcessCompetencyRow(CompetencyTableRow competencyRow, List } competencyRow.Validate(); } - public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary) + public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary, int reorderCompetenciesOption, int addAssessmentQuestionsOption, int customAssessmentQuestionID, List defaultQuestionIds) { int maxFrameworkCompetencyId = frameworkService.GetMaxFrameworkCompetencyID(); int maxFrameworkCompetencyGroupId = frameworkService.GetMaxFrameworkCompetencyGroupID(); var table = OpenCompetenciesTable(workbook, vocabulary); - return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId); + return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, customAssessmentQuestionID, defaultQuestionIds); } internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook, string vocabulary) { @@ -93,7 +94,7 @@ internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook, string vocabulary) } return table; } - internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int adminUserId, int frameworkId, int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId) + internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int adminUserId, int frameworkId, int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId, int addAssessmentQuestionsOption, int customAssessmentQuestionID, List defaultQuestionIds) { var competenciesRows = table.Rows().Skip(1).Select(row => new CompetencyTableRow(table, row)).ToList(); @@ -104,7 +105,7 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a .Count(); foreach (var competencyRow in competenciesRows) { - maxFrameworkCompetencyGroupId = ProcessCompetencyRow(adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, competencyRow); + maxFrameworkCompetencyGroupId = ProcessCompetencyRow(adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, customAssessmentQuestionID, defaultQuestionIds, competencyRow); } return new ImportCompetenciesResult(competenciesRows); @@ -114,6 +115,9 @@ private int ProcessCompetencyRow( int frameworkId, int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId, + int addAssessmentQuestionsOption, + int customAssessmentQuestionID, + List defaultQuestionIds, CompetencyTableRow competencyRow ) { @@ -121,11 +125,13 @@ CompetencyTableRow competencyRow { return maxFrameworkCompetencyGroupId; } + int newCompetencyGroupId = 0; + int newCompetencyId = 0; //If competency group is set, check if competency group exists within framework and add if not and get the Framework Competency Group ID - int? frameworkCompetencyGroupId = null; + int ? frameworkCompetencyGroupId = null; if (competencyRow.CompetencyGroup != null) { - var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, null, adminUserId); + newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId); if (newCompetencyGroupId > 0) { frameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminUserId); @@ -136,21 +142,89 @@ CompetencyTableRow competencyRow } } } - - //Check if competency already exists in framework competency group and add if not - var newCompetencyId = frameworkService.InsertCompetency(competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId); - if (newCompetencyId > 0) + // If FrameworkCompetency ID is supplied, update the competency + if (competencyRow.ID != null) { - var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminUserId, frameworkId); - if (newFrameworkCompetencyId > maxFrameworkCompetencyId) + var frameworkCompetency = frameworkService.GetFrameworkCompetencyById((int)competencyRow.ID); + if (frameworkCompetency != null) { - competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyInserted : RowStatus.CompetencyInserted); + newCompetencyId = frameworkCompetency.CompetencyID; + if (frameworkCompetency.Name != competencyRow.Competency || frameworkCompetency.Description != competencyRow.CompetencyDescription || frameworkCompetency.AlwaysShowDescription != competencyRow.AlwaysShowDescription ) + { + frameworkService.UpdateFrameworkCompetency((int)competencyRow.ID, competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId, competencyRow.AlwaysShowDescription ?? false); + competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyUpdated: RowStatus.CompetencyUpdated); + } + else + { + competencyRow.RowStatus = RowStatus.Skipped; + } } - else + } + else + { + //Check if competency already exists in framework competency group and add if not + newCompetencyId = frameworkService.InsertCompetency(competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId); + if (newCompetencyId > 0) + { + var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminUserId, frameworkId, competencyRow.AlwaysShowDescription ?? false); //including always show desc flag + if (newFrameworkCompetencyId > maxFrameworkCompetencyId) + { + competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyInserted : RowStatus.CompetencyInserted); + } + else + { + competencyRow.RowStatus = RowStatus.Skipped; + } + } + } + + + // If flags are supplied, add them: + if (competencyRow.FlagsCsv != null) + { + var flags = competencyRow.FlagsCsv.Split(','); + int[] flagIds = []; + foreach (var flag in flags) { + int flagId = 0; + var frameworkFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, null); + if (frameworkFlags.Any()) + { + foreach (var frameworkFlag in frameworkFlags) + { + if (frameworkFlag.FlagName == flag) + { + flagId = frameworkFlag.FlagId; + } + } + } + if (flagId == 0) + { + flagId = frameworkService.AddCustomFlagToFramework(frameworkId, flag, "Flag", "nhsuk-tag--white"); + } + flagIds.Append(flagId); + } + if (flagIds.Any()) { + frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, flagIds); + } + } + + + // Add assessment questions if necessary: + if (defaultQuestionIds.Count > 0 | customAssessmentQuestionID > 0) + { + if (competencyRow.RowStatus == RowStatus.CompetencyInserted | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted || addAssessmentQuestionsOption == 2 && competencyRow.RowStatus == RowStatus.CompetencyUpdated | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyUpdated || addAssessmentQuestionsOption == 3) { - competencyRow.RowStatus = RowStatus.Skipped; + foreach(var id in defaultQuestionIds) + { + frameworkService.AddCompetencyAssessmentQuestion((int)competencyRow.ID, id, adminUserId); + } + if(customAssessmentQuestionID > 0) + { + frameworkService.AddCompetencyAssessmentQuestion((int)competencyRow.ID, customAssessmentQuestionID, adminUserId); + } } } + return maxFrameworkCompetencyGroupId; } From ca3ed5d0448c7b763f7ee4292baac16092c5b6cf Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Tue, 7 Jan 2025 17:27:55 +0000 Subject: [PATCH 02/12] TD-5163 Implements the upload results view --- .../DataServices/FrameworkDataService.cs | 8 +++++++- .../FrameworksController/ImportCompetencies.cs | 12 ++++++++++++ .../Services/ImportCompetenciesFromFileService.cs | 7 ++++--- .../Import/ImportCompetenciesResultsViewModel.cs | 15 ++++++++++++++- .../Developer/Import/ImportSummary.cshtml | 2 +- .../Developer/Import/UploadResults.cshtml | 7 ++++--- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index 9c853ea44f..9765a8cfd2 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -1516,7 +1516,13 @@ public void AddCompetencyAssessmentQuestion(int frameworkCompetencyId, int asses FROM [CompetencyAssessmentQuestions] WHERE ([CompetencyId] = fc.CompetencyID)), 0)+1 FROM FrameworkCompetencies AS fc - WHERE Id = @frameworkCompetencyId", + WHERE Id = @frameworkCompetencyId + AND NOT EXISTS ( + SELECT 1 + FROM CompetencyAssessmentQuestions AS caq + WHERE caq.CompetencyId = fc.CompetencyID + AND caq.AssessmentQuestionID = @assessmentQuestionId + );", new { frameworkCompetencyId, assessmentQuestionId } ); if (numberOfAffectedRows < 1) diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs index 735db4a3e5..31c34b8e0a 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs @@ -235,9 +235,21 @@ public IActionResult ImportSummarySubmit() var workbook = new XLWorkbook(filePath); var results = importCompetenciesFromFileService.ProcessCompetenciesFromFile(workbook, adminId, data.FrameworkId, data.FrameworkVocubulary, data.ReorderCompetenciesOption, data.AddAssessmentQuestionsOption, data.AddCustomAssessmentQuestion ? (int)data.CustomAssessmentQuestionID : 0, data.AddDefaultAssessmentQuestions ? data.DefaultQuestionIDs : []); data.ImportCompetenciesResult = results; + //TO DO apply ordering changes if required: + if (data.ReorderCompetenciesOption == 2 && data.CompetenciesToReorderCount > 0) + { + + } setBulkUploadData(data); return RedirectToAction("UploadResults", "Frameworks", new { frameworkId = data.FrameworkId, tabname = data.TabName }); } + [Route("/Framework/{frameworkId}/{tabname}/Import/Results")] + public IActionResult UploadResults() + { + var data = GetBulkUploadData(); + var model = new ImportCompetenciesResultsViewModel(data.ImportCompetenciesResult, data.FrameworkId, data.FrameworkName, data.FrameworkVocubulary); + return View("Developer/Import/UploadResults", model); + } [Route("CancelImport")] public IActionResult CancelImport() { diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 2367bcbefd..a235f0a991 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -127,6 +127,7 @@ CompetencyTableRow competencyRow } int newCompetencyGroupId = 0; int newCompetencyId = 0; + int newFrameworkCompetencyId = 0; //If competency group is set, check if competency group exists within framework and add if not and get the Framework Competency Group ID int ? frameworkCompetencyGroupId = null; if (competencyRow.CompetencyGroup != null) @@ -166,7 +167,7 @@ CompetencyTableRow competencyRow newCompetencyId = frameworkService.InsertCompetency(competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId); if (newCompetencyId > 0) { - var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminUserId, frameworkId, competencyRow.AlwaysShowDescription ?? false); //including always show desc flag + newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminUserId, frameworkId, competencyRow.AlwaysShowDescription ?? false); //including always show desc flag if (newFrameworkCompetencyId > maxFrameworkCompetencyId) { competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyInserted : RowStatus.CompetencyInserted); @@ -216,11 +217,11 @@ CompetencyTableRow competencyRow { foreach(var id in defaultQuestionIds) { - frameworkService.AddCompetencyAssessmentQuestion((int)competencyRow.ID, id, adminUserId); + frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, id, adminUserId); } if(customAssessmentQuestionID > 0) { - frameworkService.AddCompetencyAssessmentQuestion((int)competencyRow.ID, customAssessmentQuestionID, adminUserId); + frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, customAssessmentQuestionID, adminUserId); } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/Frameworks/Import/ImportCompetenciesResultsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Frameworks/Import/ImportCompetenciesResultsViewModel.cs index b47108fe15..48204ae0d1 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Frameworks/Import/ImportCompetenciesResultsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Frameworks/Import/ImportCompetenciesResultsViewModel.cs @@ -1,25 +1,38 @@ namespace DigitalLearningSolutions.Web.ViewModels.Frameworks.Import { using DigitalLearningSolutions.Data.Models.Frameworks.Import; + using DigitalLearningSolutions.Web.Helpers; using System.Collections.Generic; using System.Linq; public class ImportCompetenciesResultsViewModel { - public ImportCompetenciesResultsViewModel(ImportCompetenciesResult importCompetenciesResult) + public ImportCompetenciesResultsViewModel(ImportCompetenciesResult importCompetenciesResult, int frameworkId, string frameworkName, string frameworkVocabulary) { ProcessedCount = importCompetenciesResult.ProcessedCount; CompetenciesInsertedCount = importCompetenciesResult.CompetencyAddedCount; + CompetenciesUpdatedCount = importCompetenciesResult.CompetencyUpdatedCount; CompetencyGroupsInsertedCount = importCompetenciesResult.GroupAddedCount; + CompetencyGroupsUpdatedCount = importCompetenciesResult.GroupUpdatedCount; SkippedCount = importCompetenciesResult.SkippedCount; Errors = importCompetenciesResult.Errors.Select(x => (x.RowNumber, MapReasonToErrorMessage(x.Reason))); + FrameworkID = frameworkId; + FrameworkName = frameworkName; + FrameworkVocabularySingular = FrameworkVocabularyHelper.VocabularySingular(frameworkVocabulary); + FrameworkVocabularyPlural = FrameworkVocabularyHelper.VocabularyPlural(frameworkVocabulary); } public IEnumerable<(int RowNumber, string ErrorMessage)> Errors { get; set; } public int ErrorCount => Errors.Count(); public int ProcessedCount { get; set; } public int CompetenciesInsertedCount { get; set; } + public int CompetenciesUpdatedCount { get; set; } public int CompetencyGroupsInsertedCount { get; set; } + public int CompetencyGroupsUpdatedCount { get; set; } public int SkippedCount { get; set; } + public int FrameworkID { get; set; } + public string FrameworkName { get; set; } + public string FrameworkVocabularySingular { get; set; } + public string FrameworkVocabularyPlural { get; set; } private string MapReasonToErrorMessage(ImportCompetenciesResult.ErrorReason reason) { return reason switch diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml index 5a782d911f..fedee7029a 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml @@ -101,7 +101,7 @@ Important:

Once @Model.FrameworkVocabularySingular.ToLower() records are processed, changes cannot be undone.

-
+ Back
diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml index d83d3ca428..efb819495d 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml @@ -34,9 +34,10 @@

Summary of results:

  • @Model.ProcessedCount @(Model.ProcessedCount == 1 ? "line" : "lines") processed
  • -
  • @Model.CompetencyGroupsInsertedCount new @(Model.CompetencyGroupsInsertedCount == 1 ? "competency group" : "competency groups") inserted
  • -
  • @Model.CompetenciesInsertedCount new @(Model.CompetenciesInsertedCount == 1 ? "competency" : "competencies") inserted
  • -
  • @Model.SkippedCount rows @(Model.SkippedCount == 1 ? "record" : "records") skipped (nothing inserted but no errors)
  • +
  • @Model.CompetencyGroupsInsertedCount new @(Model.CompetencyGroupsInsertedCount == 1 ? $"{Model.FrameworkVocabularySingular.ToLower()} group" : $"{Model.FrameworkVocabularySingular.ToLower()} groups") inserted
  • +
  • @Model.CompetenciesInsertedCount new @(Model.CompetenciesInsertedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) inserted
  • +
  • @Model.CompetenciesUpdatedCount new @(Model.CompetenciesUpdatedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) updated
  • +
  • @Model.SkippedCount rows @(Model.SkippedCount == 1 ? "line" : "lines") skipped (nothing inserted but no errors)
  • @Model.ErrorCount @(Model.ErrorCount == 1 ? "line" : "lines") skipped due to errors
From 8c517bc8d409415a4027ee33ee66b2198f6fa622 Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Wed, 8 Jan 2025 09:08:17 +0000 Subject: [PATCH 03/12] TD-5163 Fixes flag assignment during upload processing --- .../ImportCompetenciesFromFileService.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index a235f0a991..9841c5e429 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -11,6 +11,7 @@ namespace DigitalLearningSolutions.Web.Services using ClosedXML.Excel; using DigitalLearningSolutions.Data.Exceptions; using DigitalLearningSolutions.Data.Helpers; + using DigitalLearningSolutions.Data.Models.Frameworks; using DigitalLearningSolutions.Data.Models.Frameworks.Import; using DocumentFormat.OpenXml.Office2010.Excel; @@ -125,14 +126,13 @@ CompetencyTableRow competencyRow { return maxFrameworkCompetencyGroupId; } - int newCompetencyGroupId = 0; int newCompetencyId = 0; int newFrameworkCompetencyId = 0; //If competency group is set, check if competency group exists within framework and add if not and get the Framework Competency Group ID int ? frameworkCompetencyGroupId = null; if (competencyRow.CompetencyGroup != null) { - newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId); + int newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId); if (newCompetencyGroupId > 0) { frameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminUserId); @@ -181,10 +181,10 @@ CompetencyTableRow competencyRow // If flags are supplied, add them: - if (competencyRow.FlagsCsv != null) + if (!string.IsNullOrWhiteSpace(competencyRow.FlagsCsv.Trim())) { var flags = competencyRow.FlagsCsv.Split(','); - int[] flagIds = []; + var flagIds = new List(); foreach (var flag in flags) { int flagId = 0; var frameworkFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, null); @@ -195,6 +195,7 @@ CompetencyTableRow competencyRow if (frameworkFlag.FlagName == flag) { flagId = frameworkFlag.FlagId; + break; } } } @@ -202,10 +203,14 @@ CompetencyTableRow competencyRow { flagId = frameworkService.AddCustomFlagToFramework(frameworkId, flag, "Flag", "nhsuk-tag--white"); } - flagIds.Append(flagId); + flagIds.Add(flagId); } - if (flagIds.Any()) { - frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, flagIds); + if (flagIds.Count > 0) { + frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, [.. flagIds]); + if (competencyRow.RowStatus == RowStatus.Skipped) + { + competencyRow.RowStatus = RowStatus.CompetencyUpdated; + } } } From b99f8164f2dfa7492d7e4950d25d7653f0ed539a Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Wed, 8 Jan 2025 09:08:51 +0000 Subject: [PATCH 04/12] TD-5163 General tweaks to improve wording of buttons and body text --- .../Views/Frameworks/Developer/Import/Index.cshtml | 2 +- .../Views/Frameworks/Developer/Import/UploadResults.cshtml | 2 +- .../Views/Frameworks/Developer/_Structure.cshtml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/Index.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/Index.cshtml index 78a5fb95bd..11fd4f4b48 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/Index.cshtml @@ -79,7 +79,7 @@
This Excel file will include all existing @Model.FrameworkVocabularyPlural.ToLower() whose details you can update.
- New @Model.FrameworkVocabularyPlural.ToLower() can be added by including their details on a blank row. + New @Model.FrameworkVocabularyPlural.ToLower() can be added by including their details on a blank row, leaving the ID column blank.
Download @Model.FrameworkVocabularyPlural.ToLower() diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml index efb819495d..b65e4026f1 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml @@ -36,7 +36,7 @@
  • @Model.ProcessedCount @(Model.ProcessedCount == 1 ? "line" : "lines") processed
  • @Model.CompetencyGroupsInsertedCount new @(Model.CompetencyGroupsInsertedCount == 1 ? $"{Model.FrameworkVocabularySingular.ToLower()} group" : $"{Model.FrameworkVocabularySingular.ToLower()} groups") inserted
  • @Model.CompetenciesInsertedCount new @(Model.CompetenciesInsertedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) inserted
  • -
  • @Model.CompetenciesUpdatedCount new @(Model.CompetenciesUpdatedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) updated
  • +
  • @Model.CompetenciesUpdatedCount existing @(Model.CompetenciesUpdatedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) updated
  • @Model.SkippedCount rows @(Model.SkippedCount == 1 ? "line" : "lines") skipped (nothing inserted but no errors)
  • @Model.ErrorCount @(Model.ErrorCount == 1 ? "line" : "lines") skipped due to errors
  • diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml index a1097f6e18..849b5121ba 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/_Structure.cshtml @@ -81,7 +81,7 @@ else
    } From 23a1901fa039022596eaea0ade1062589e017b0d Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Wed, 8 Jan 2025 16:55:34 +0000 Subject: [PATCH 05/12] TD-5163 Corrects heading --- .../Views/Frameworks/Developer/Import/ImportCompleted.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml index b127e53979..93abdd4983 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml @@ -28,7 +28,7 @@ }
    -

    Delegate file uploaded

    +

    @Model.FrameworkVocabularySingular.ToLower() 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:")

      From c7a6a084c5ed9939804a96574b403a1b2f57213a Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Wed, 8 Jan 2025 16:56:32 +0000 Subject: [PATCH 06/12] TD-5163 modifies the insert competency group method to take an optional framework id --- .../DataServices/FrameworkDataService.cs | 62 ++++++++++++------- .../Services/FrameworkService.cs | 6 +- .../ImportCompetenciesFromFileService.cs | 2 +- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index 9765a8cfd2..65541032f3 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -123,7 +123,7 @@ bool zeroBased //INSERT DATA BrandedFramework CreateFramework(DetailFramework detailFramework, int adminId); - int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId); + int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId); int InsertFrameworkCompetencyGroup(int groupId, int frameworkID, int adminId); @@ -475,7 +475,7 @@ public BrandedFramework CreateFramework(DetailFramework detailFramework, int adm return new BrandedFramework(); } - var existingFrameworks = (int)connection.ExecuteScalar( + var existingFrameworks = connection.QuerySingle( @"SELECT COUNT(*) FROM Frameworks WHERE FrameworkName = @frameworkName", new { frameworkName } ); @@ -548,7 +548,7 @@ int adminId return GetBrandedFrameworkByFrameworkId(frameworkId, adminId); } - public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId) + public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId) { if ((groupName.Length == 0) | (adminId < 1)) { @@ -558,9 +558,16 @@ public int InsertCompetencyGroup(string groupName, string? groupDescription, int return -2; } groupDescription = (groupDescription?.Trim() == "" ? null : groupDescription); - var existingId = (int)connection.ExecuteScalar( - @"SELECT COALESCE ((SELECT TOP(1)ID FROM CompetencyGroups WHERE [Name] = @groupName AND (@groupDescription IS NULL OR Description = @groupDescription)), 0) AS CompetencyGroupID", - new { groupName, groupDescription } + var existingId = connection.QuerySingle( + @"SELECT COALESCE + ((SELECT TOP (1) ID + FROM CompetencyGroups + WHERE (Name = @groupName) AND EXISTS + (SELECT 1 AS Expr1 + FROM FrameworkCompetencyGroups + WHERE (CompetencyGroupID = CompetencyGroups.ID) AND (FrameworkID = @frameworkId) OR + (CompetencyGroupID = CompetencyGroups.ID) AND (@frameworkId IS NULL))), 0) AS CompetencyGroupID", + new { groupName, groupDescription, frameworkId } ); if (existingId > 0) { @@ -581,8 +588,15 @@ public int InsertCompetencyGroup(string groupName, string? groupDescription, int return -1; } - existingId = (int)connection.ExecuteScalar( - @"SELECT COALESCE ((SELECT TOP(1)ID FROM CompetencyGroups WHERE [Name] = @groupName AND (@groupDescription IS NULL OR Description = @groupDescription)), 0) AS CompetencyGroupID", + existingId = connection.QuerySingle( + @"SELECT COALESCE + ((SELECT TOP (1) ID + FROM CompetencyGroups + WHERE (Name = @groupName) AND EXISTS + (SELECT 1 AS Expr1 + FROM FrameworkCompetencyGroups + WHERE (CompetencyGroupID = CompetencyGroups.ID) AND (FrameworkID = @frameworkId) OR + (CompetencyGroupID = CompetencyGroups.ID) AND (@frameworkId IS NULL))), 0) AS CompetencyGroupID", new { groupName, groupDescription } ); return existingId; @@ -598,7 +612,7 @@ public int InsertFrameworkCompetencyGroup(int groupId, int frameworkId, int admi return -2; } - var existingId = (int)connection.ExecuteScalar( + var existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencyGroups WHERE CompetencyGroupID = @groupID AND FrameworkID = @frameworkID), 0) AS FrameworkCompetencyGroupID", new { groupId, frameworkId } ); @@ -624,7 +638,7 @@ FROM [FrameworkCompetencyGroups] return -1; } - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencyGroups WHERE CompetencyGroupID = @groupID AND FrameworkID = @frameworkID), 0) AS FrameworkCompetencyGroupID", new { groupId, frameworkId } ); @@ -671,14 +685,14 @@ public int InsertFrameworkCompetency( var existingId = 0; if (frameworkCompetencyGroupID == null) { - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencies WHERE [CompetencyID] = @competencyId AND FrameworkCompetencyGroupID IS NULL), 0) AS FrameworkCompetencyID", new { competencyId, frameworkCompetencyGroupID } ); } else { - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencies WHERE [CompetencyID] = @competencyId AND FrameworkCompetencyGroupID = @frameworkCompetencyGroupID), 0) AS FrameworkCompetencyID", new { competencyId, frameworkCompetencyGroupID } ); @@ -707,14 +721,14 @@ FROM [FrameworkCompetencies] if (frameworkCompetencyGroupID == null) { - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencies WHERE [CompetencyID] = @competencyId AND FrameworkCompetencyGroupID IS NULL), 0) AS FrameworkCompetencyID", new { competencyId, frameworkCompetencyGroupID } ); } else { - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCompetencies WHERE [CompetencyID] = @competencyId AND FrameworkCompetencyGroupID = @frameworkCompetencyGroupID), 0) AS FrameworkCompetencyID", new { competencyId, frameworkCompetencyGroupID } ); @@ -765,7 +779,7 @@ public int AddCollaboratorToFramework(int frameworkId, string? userEmail, bool c return -3; } - var existingId = (int)connection.ExecuteScalar( + var existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCollaborators @@ -815,7 +829,7 @@ FROM FrameworkCollaborators ); } - existingId = (int)connection.ExecuteScalar( + existingId = connection.QuerySingle( @"SELECT COALESCE ((SELECT ID FROM FrameworkCollaborators @@ -907,7 +921,7 @@ public bool UpdateFrameworkName(int frameworkId, int adminId, string frameworkNa return false; } - var existingFrameworks = (int)connection.ExecuteScalar( + var existingFrameworks = connection.QuerySingle( @"SELECT COUNT(*) FROM Frameworks WHERE FrameworkName = @frameworkName AND ID <> @frameworkId", new { frameworkName, frameworkId } ); @@ -1003,7 +1017,7 @@ int adminId return; } - var usedElsewhere = (int)connection.ExecuteScalar( + var usedElsewhere = connection.QuerySingle( @"SELECT COUNT(*) FROM FrameworkCompetencyGroups WHERE CompetencyGroupId = @competencyGroupId AND ID <> @frameworkCompetencyGroupId", @@ -1171,14 +1185,14 @@ public void DeleteFrameworkCompetencyGroup(int frameworkCompetencyGroupId, int c } //Check if used elsewhere and delete competency group if not: - var usedElsewhere = (int)connection.ExecuteScalar( + var usedElsewhere = connection.QuerySingle( @"SELECT COUNT(*) FROM FrameworkCompetencyGroups WHERE CompetencyGroupId = @competencyGroupId", new { competencyGroupId } ); if (usedElsewhere == 0) { - usedElsewhere = (int)connection.ExecuteScalar( + usedElsewhere = connection.QuerySingle( @"SELECT COUNT(*) FROM SelfAssessmentStructure WHERE CompetencyGroupId = @competencyGroupId", new { competencyGroupId } @@ -1209,7 +1223,7 @@ public void DeleteFrameworkCompetencyGroup(int frameworkCompetencyGroupId, int c public void DeleteFrameworkCompetency(int frameworkCompetencyId, int adminId) { - var competencyId = (int)connection.ExecuteScalar( + var competencyId = connection.QuerySingle( @"SELECT CompetencyID FROM FrameworkCompetencies WHERE ID = @frameworkCompetencyId", new { frameworkCompetencyId } ); @@ -1240,14 +1254,14 @@ public void DeleteFrameworkCompetency(int frameworkCompetencyId, int adminId) } //Check if used elsewhere and delete competency group if not: - var usedElsewhere = (int)connection.ExecuteScalar( + var usedElsewhere = connection.QuerySingle( @"SELECT COUNT(*) FROM FrameworkCompetencies WHERE CompetencyID = @competencyId", new { competencyId } ); if (usedElsewhere == 0) { - usedElsewhere = (int)connection.ExecuteScalar( + usedElsewhere = connection.QuerySingle( @"SELECT COUNT(*) FROM SelfAssessmentStructure WHERE CompetencyID = @competencyId", new { competencyId } @@ -1834,7 +1848,7 @@ FROM Competencies AS C INNER JOIN public int GetAdminUserRoleForFrameworkId(int adminId, int frameworkId) { - return (int)connection.ExecuteScalar( + return connection.QuerySingle( @"SELECT CASE WHEN FW.OwnerAdminID = @adminId THEN 3 WHEN fwc.CanModify = 1 THEN 2 WHEN fwc.CanModify = 0 THEN 1 ELSE 0 END AS UserRole FROM Frameworks AS FW LEFT OUTER JOIN FrameworkCollaborators AS fwc ON fwc.FrameworkID = FW.ID AND fwc.AdminID = @adminId diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs index c4f7035ba9..2f47b79409 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs @@ -117,7 +117,7 @@ bool zeroBased //INSERT DATA BrandedFramework CreateFramework(DetailFramework detailFramework, int adminId); - int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId); + int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId = null); int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool alwaysShowDescription = false); @@ -597,9 +597,9 @@ public int InsertCompetency(string name, string? description, int adminId) return frameworkDataService.InsertCompetency(name, description, adminId); } - public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId) + public int InsertCompetencyGroup(string groupName, string? groupDescription, int adminId, int? frameworkId) { - return frameworkDataService.InsertCompetencyGroup(groupName, groupDescription, adminId); + return frameworkDataService.InsertCompetencyGroup(groupName, groupDescription, adminId, frameworkId); } public int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId, bool alwaysShowDescription = false) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 9841c5e429..2471f61605 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -132,7 +132,7 @@ CompetencyTableRow competencyRow int ? frameworkCompetencyGroupId = null; if (competencyRow.CompetencyGroup != null) { - int newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId); + int newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId, frameworkId); if (newCompetencyGroupId > 0) { frameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminUserId); From 4be6d5835ce07b0ab43d9e0c0a54c15e9a6bbc40 Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Wed, 8 Jan 2025 16:57:06 +0000 Subject: [PATCH 07/12] TD-5163 Clears down the uploaded sheet after processing --- .../Controllers/FrameworksController/ImportCompetencies.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs index 31c34b8e0a..43c91e9347 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs @@ -247,6 +247,8 @@ public IActionResult ImportSummarySubmit() public IActionResult UploadResults() { var data = GetBulkUploadData(); + FileHelper.DeleteFile(webHostEnvironment, data.CompetenciesFileName); + TempData.Clear(); var model = new ImportCompetenciesResultsViewModel(data.ImportCompetenciesResult, data.FrameworkId, data.FrameworkName, data.FrameworkVocubulary); return View("Developer/Import/UploadResults", model); } From fb87357691d166df1a68c51f2886a7a7e15fb084 Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Thu, 9 Jan 2025 16:59:26 +0000 Subject: [PATCH 08/12] TD-5163 Commits competency order changes if required --- .../DataServices/FrameworkDataService.cs | 18 ++++--- .../Frameworks/Import/CompetencyTableRow.cs | 1 + .../Services/FrameworkService.cs | 6 +-- .../ImportCompetenciesFromFileService.cs | 54 ++++++++++++++++--- .../Import/ApplyCompetencyOrdering.cshtml | 4 ++ .../Developer/Import/ImportCompleted.cshtml | 2 +- .../Developer/Import/UploadResults.cshtml | 2 +- 7 files changed, 68 insertions(+), 19 deletions(-) diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index 65541032f3..20dc80033e 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -206,7 +206,7 @@ int adminId ); void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription); - void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); + int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction); @@ -1025,7 +1025,7 @@ int adminId ); if (usedElsewhere > 0) { - var newCompetencyGroupId = InsertCompetencyGroup(name, description, adminId); + var newCompetencyGroupId = InsertCompetencyGroup(name, description, adminId, null); if (newCompetencyGroupId > 0) { var numberOfAffectedRows = connection.Execute( @@ -1086,13 +1086,14 @@ public void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, st } } - public void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds) + public int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds) { + int totalRowsAffected = 0; string? commaSeparatedSelectedFlagIds = null; if (selectedFlagIds?.Length > 0) { commaSeparatedSelectedFlagIds = String.Join(',', selectedFlagIds); - connection.Execute( + totalRowsAffected += connection.Execute( @$"INSERT INTO CompetencyFlags(CompetencyID, FlagID, Selected) SELECT @competencyId, f.ID, 1 FROM Flags f @@ -1102,11 +1103,12 @@ SELECT FlagID FROM CompetencyFlags )", new { competencyId, selectedFlagIds }); } - connection.Execute( + totalRowsAffected += connection.Execute( @$"UPDATE CompetencyFlags SET Selected = (CASE WHEN FlagID IN ({commaSeparatedSelectedFlagIds ?? "null"}) THEN 1 ELSE 0 END) - WHERE CompetencyID = @competencyId", + WHERE CompetencyID = @competencyId AND Selected <> (CASE WHEN FlagID IN ({commaSeparatedSelectedFlagIds ?? "null"}) THEN 1 ELSE 0 END)", new { competencyId, frameworkId }); + return totalRowsAffected; } public int AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass) @@ -1127,7 +1129,7 @@ public void UpdateFrameworkCustomFlag(int frameworkId, int id, string flagName, new { frameworkId, id, flagName, flagGroup, flagTagClass }); } - public void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction) + public void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction) // Valid directions are 'UP' and 'DOWN' { connection.Execute( "ReorderFrameworkCompetencyGroup", @@ -1136,7 +1138,7 @@ public void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool si ); } - public void MoveFrameworkCompetency(int frameworkCompetencyId, bool singleStep, string direction) + public void MoveFrameworkCompetency(int frameworkCompetencyId, bool singleStep, string direction) // Valid directions are 'UP' and 'DOWN' { connection.Execute( "ReorderFrameworkCompetency", diff --git a/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs b/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs index 16ea0b9e52..740ec961ca 100644 --- a/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs +++ b/DigitalLearningSolutions.Data/Models/Frameworks/Import/CompetencyTableRow.cs @@ -38,6 +38,7 @@ public CompetencyTableRow(IXLTable table, IXLRangeRow row) RowStatus = RowStatus.NotYetProcessed; } public int RowNumber { get; set; } + public int CompetencyOrderNumber { get; set; } public string? AlwaysShowDescriptionRaw { get; set; } public ImportCompetenciesResult.ErrorReason? Error { get; set; } public RowStatus RowStatus { get; set; } diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs index 2f47b79409..795e8ba966 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs @@ -200,7 +200,7 @@ int adminId ); void UpdateFrameworkCompetency(int frameworkCompetencyId, string name, string? description, int adminId, bool? alwaysShowDescription = false); - void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); + int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds); void MoveFrameworkCompetencyGroup(int frameworkCompetencyGroupId, bool singleStep, string direction); @@ -662,9 +662,9 @@ public void UpdateAssessmentQuestion(int id, string question, int assessmentQues frameworkDataService.UpdateAssessmentQuestion(id, question, assessmentQuestionInputTypeId, maxValueDescription, minValueDescription, scoringInstructions, minValue, maxValue, includeComments, adminId, commentsPrompt, commentsHint); } - public void UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds) + public int UpdateCompetencyFlags(int frameworkId, int competencyId, int[] selectedFlagIds) { - frameworkDataService.UpdateCompetencyFlags(frameworkId, competencyId, selectedFlagIds); + return frameworkDataService.UpdateCompetencyFlags(frameworkId, competencyId, selectedFlagIds); } public BrandedFramework? UpdateFrameworkBranding(int frameworkId, int brandId, int categoryId, int topicId, int adminId) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 2471f61605..7cec219ef9 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -78,7 +78,7 @@ public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook int maxFrameworkCompetencyId = frameworkService.GetMaxFrameworkCompetencyID(); int maxFrameworkCompetencyGroupId = frameworkService.GetMaxFrameworkCompetencyGroupID(); var table = OpenCompetenciesTable(workbook, vocabulary); - return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, customAssessmentQuestionID, defaultQuestionIds); + return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, reorderCompetenciesOption, customAssessmentQuestionID, defaultQuestionIds); } internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook, string vocabulary) { @@ -95,10 +95,28 @@ internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook, string vocabulary) } return table; } - internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int adminUserId, int frameworkId, int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId, int addAssessmentQuestionsOption, int customAssessmentQuestionID, List defaultQuestionIds) + internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int adminUserId, int frameworkId, int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId, int addAssessmentQuestionsOption, int reorderCompetenciesOption, int customAssessmentQuestionID, List defaultQuestionIds) { var competenciesRows = table.Rows().Skip(1).Select(row => new CompetencyTableRow(table, row)).ToList(); - + int rowCount = 0; + string currentGroup = null; + competenciesRows = competenciesRows + .OrderBy(row => row.CompetencyGroup) + .Select(row => + { + if (row.CompetencyGroup != currentGroup) + { + currentGroup = row.CompetencyGroup; + rowCount = 1; + } + else + { + rowCount++; + } + row.CompetencyOrderNumber = rowCount; + return row; + }) + .ToList(); var competencyGroupCount = competenciesRows .Where(row => !string.IsNullOrWhiteSpace(row.CompetencyGroup)) .Select(row => row.CompetencyGroup) @@ -106,7 +124,7 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a .Count(); foreach (var competencyRow in competenciesRows) { - maxFrameworkCompetencyGroupId = ProcessCompetencyRow(adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, customAssessmentQuestionID, defaultQuestionIds, competencyRow); + maxFrameworkCompetencyGroupId = ProcessCompetencyRow(adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, reorderCompetenciesOption, customAssessmentQuestionID, defaultQuestionIds, competencyRow); } return new ImportCompetenciesResult(competenciesRows); @@ -117,6 +135,7 @@ private int ProcessCompetencyRow( int maxFrameworkCompetencyId, int maxFrameworkCompetencyGroupId, int addAssessmentQuestionsOption, + int reorderCompetenciesOption, int customAssessmentQuestionID, List defaultQuestionIds, CompetencyTableRow competencyRow @@ -206,8 +225,8 @@ CompetencyTableRow competencyRow flagIds.Add(flagId); } if (flagIds.Count > 0) { - frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, [.. flagIds]); - if (competencyRow.RowStatus == RowStatus.Skipped) + var updated = frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, [.. flagIds]); + if (updated > 0 && competencyRow.RowStatus == RowStatus.Skipped) { competencyRow.RowStatus = RowStatus.CompetencyUpdated; } @@ -231,6 +250,29 @@ CompetencyTableRow competencyRow } } + // Reorder competencies if required: + if (reorderCompetenciesOption == 2) + { + var frameworkCompetencyId = (int)competencyRow.ID; + var frameworkCompetency = frameworkService.GetFrameworkCompetencyById(frameworkCompetencyId); + var placesToMove = Math.Abs(frameworkCompetency.Ordering - competencyRow.CompetencyOrderNumber); + + if (placesToMove > 0) + { + var direction = frameworkCompetency.Ordering > competencyRow.CompetencyOrderNumber ? "UP" : "DOWN"; + + for (int i = 0; i < placesToMove; i++) + { + frameworkService.MoveFrameworkCompetency(frameworkCompetencyId, true, direction); + } + + if (competencyRow.RowStatus == RowStatus.Skipped) + { + competencyRow.RowStatus = RowStatus.CompetencyUpdated; + } + } + } + return maxFrameworkCompetencyGroupId; } diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ApplyCompetencyOrdering.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ApplyCompetencyOrdering.cshtml index 398343f461..6b21e8b765 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ApplyCompetencyOrdering.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ApplyCompetencyOrdering.cshtml @@ -36,6 +36,10 @@ Apply @Model.FrameworkVocabularySingular.ToLower() sequence changes? +
      + Information: +

      We strongly recommend including all framework competencies in your uploaded sheet if reordering competencies to ensure that the correct sequence is applied.

      +
      Your uploaded file includes changes to the sequence of existing @Model.FrameworkVocabularyPlural.ToLower(). Choose whether to store the changes to @Model.FrameworkVocabularySingular.ToLower() sequence during update.
      diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml index 93abdd4983..059712b059 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml @@ -47,7 +47,7 @@ {
    • - @Model.CompetenciesToReorderCount existing @Model.FrameworkVocabularySingular.ToLower() @(Model.CompetenciesToReorderCount == 1 ? "record" : "records") have changed sequence in your uploaded sheet. You can choose whether to fix them in the new order next. + Some existing @Model.FrameworkVocabularySingular.ToLower() @(Model.CompetenciesToReorderCount == 1 ? "record" : "records") have changed sequence in your uploaded sheet. You can choose whether to fix them in the new order next.
    • }
    • No errors
    • diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml index b65e4026f1..3fa9b57408 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/UploadResults.cshtml @@ -37,7 +37,7 @@
    • @Model.CompetencyGroupsInsertedCount new @(Model.CompetencyGroupsInsertedCount == 1 ? $"{Model.FrameworkVocabularySingular.ToLower()} group" : $"{Model.FrameworkVocabularySingular.ToLower()} groups") inserted
    • @Model.CompetenciesInsertedCount new @(Model.CompetenciesInsertedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) inserted
    • @Model.CompetenciesUpdatedCount existing @(Model.CompetenciesUpdatedCount == 1 ? Model.FrameworkVocabularySingular.ToLower() : Model.FrameworkVocabularyPlural.ToLower()) updated
    • -
    • @Model.SkippedCount rows @(Model.SkippedCount == 1 ? "line" : "lines") skipped (nothing inserted but no errors)
    • +
    • @Model.SkippedCount rows @(Model.SkippedCount == 1 ? "line" : "lines") skipped (nothing inserted or updated but no errors)
    • @Model.ErrorCount @(Model.ErrorCount == 1 ? "line" : "lines") skipped due to errors
    From 81453b3516474421b56b6c1e8b895f8689e11cdb Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 10 Jan 2025 08:19:01 +0000 Subject: [PATCH 09/12] TD-5163 Reorders competency groups if needed --- .../ImportCompetenciesFromFileService.cs | 75 +++++++++++++------ 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index 7cec219ef9..b8b7883f23 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -61,7 +61,7 @@ private void PreProcessCompetencyRow(CompetencyTableRow competencyRow, List { int originalIndex = existingIds.IndexOf(id); int newIndex = newIds.IndexOf(id); - if(originalIndex == newIndex) + if (originalIndex == newIndex) { competencyRow.RowStatus = RowStatus.CompetencyUpdated; } @@ -101,7 +101,6 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a int rowCount = 0; string currentGroup = null; competenciesRows = competenciesRows - .OrderBy(row => row.CompetencyGroup) .Select(row => { if (row.CompetencyGroup != currentGroup) @@ -126,7 +125,32 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a { maxFrameworkCompetencyGroupId = ProcessCompetencyRow(adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId, addAssessmentQuestionsOption, reorderCompetenciesOption, customAssessmentQuestionID, defaultQuestionIds, competencyRow); } + // TO DO: Check for changes to competency group order and apply them if appropriate: + if (reorderCompetenciesOption == 2) + { + var distinctCompetencyGroups = competenciesRows + .Where(row => !string.IsNullOrWhiteSpace(row.CompetencyGroup)) + .Select(row => row.CompetencyGroup) + .Distinct() + .ToList(); + var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).Select(row => new { row.ID, row.Name }) + .Distinct() + .ToList(); + for (int i = 0; i < competencyGroupCount; i++) + { + var placesToMove = Math.Abs(existingGroups.FindIndex(group => group.Name == distinctCompetencyGroups[i])-i); + if (placesToMove > 0) + { + var thisGroup = existingGroups.FirstOrDefault(group => group.Name == distinctCompetencyGroups[i]); + var direction = existingGroups.FindIndex(group => group.Name == distinctCompetencyGroups[i]) > i ? "UP" : "DOWN"; + for (int p = 0; p < placesToMove; p++) + { + frameworkService.MoveFrameworkCompetencyGroup(thisGroup.ID, true, direction); + } + } + } + } return new ImportCompetenciesResult(competenciesRows); } private int ProcessCompetencyRow( @@ -148,7 +172,7 @@ CompetencyTableRow competencyRow int newCompetencyId = 0; int newFrameworkCompetencyId = 0; //If competency group is set, check if competency group exists within framework and add if not and get the Framework Competency Group ID - int ? frameworkCompetencyGroupId = null; + int? frameworkCompetencyGroupId = null; if (competencyRow.CompetencyGroup != null) { int newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, competencyRow.GroupDescription, adminUserId, frameworkId); @@ -169,10 +193,10 @@ CompetencyTableRow competencyRow if (frameworkCompetency != null) { newCompetencyId = frameworkCompetency.CompetencyID; - if (frameworkCompetency.Name != competencyRow.Competency || frameworkCompetency.Description != competencyRow.CompetencyDescription || frameworkCompetency.AlwaysShowDescription != competencyRow.AlwaysShowDescription ) + if (frameworkCompetency.Name != competencyRow.Competency || frameworkCompetency.Description != competencyRow.CompetencyDescription || frameworkCompetency.AlwaysShowDescription != competencyRow.AlwaysShowDescription) { frameworkService.UpdateFrameworkCompetency((int)competencyRow.ID, competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId, competencyRow.AlwaysShowDescription ?? false); - competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyUpdated: RowStatus.CompetencyUpdated); + competencyRow.RowStatus = (competencyRow.RowStatus == RowStatus.CompetencyGroupInserted ? RowStatus.CompetencyGroupAndCompetencyUpdated : RowStatus.CompetencyUpdated); } else { @@ -204,7 +228,8 @@ CompetencyTableRow competencyRow { var flags = competencyRow.FlagsCsv.Split(','); var flagIds = new List(); - foreach (var flag in flags) { + foreach (var flag in flags) + { int flagId = 0; var frameworkFlags = frameworkService.GetCompetencyFlagsByFrameworkId(frameworkId, null, null); if (frameworkFlags.Any()) @@ -224,7 +249,8 @@ CompetencyTableRow competencyRow } flagIds.Add(flagId); } - if (flagIds.Count > 0) { + if (flagIds.Count > 0) + { var updated = frameworkService.UpdateCompetencyFlags(frameworkId, newCompetencyId, [.. flagIds]); if (updated > 0 && competencyRow.RowStatus == RowStatus.Skipped) { @@ -233,23 +259,6 @@ CompetencyTableRow competencyRow } } - - // Add assessment questions if necessary: - if (defaultQuestionIds.Count > 0 | customAssessmentQuestionID > 0) - { - if (competencyRow.RowStatus == RowStatus.CompetencyInserted | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted || addAssessmentQuestionsOption == 2 && competencyRow.RowStatus == RowStatus.CompetencyUpdated | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyUpdated || addAssessmentQuestionsOption == 3) - { - foreach(var id in defaultQuestionIds) - { - frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, id, adminUserId); - } - if(customAssessmentQuestionID > 0) - { - frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, customAssessmentQuestionID, adminUserId); - } - } - } - // Reorder competencies if required: if (reorderCompetenciesOption == 2) { @@ -273,6 +282,24 @@ CompetencyTableRow competencyRow } } + // Add assessment questions if necessary: + if (defaultQuestionIds.Count > 0 | customAssessmentQuestionID > 0) + { + if (competencyRow.RowStatus == RowStatus.CompetencyInserted | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted || addAssessmentQuestionsOption == 2 && competencyRow.RowStatus == RowStatus.CompetencyUpdated | competencyRow.RowStatus == RowStatus.CompetencyGroupAndCompetencyUpdated || addAssessmentQuestionsOption == 3) + { + foreach (var id in defaultQuestionIds) + { + frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, id, adminUserId); + } + if (customAssessmentQuestionID > 0) + { + frameworkService.AddCompetencyAssessmentQuestion(competencyRow.ID ?? newFrameworkCompetencyId, customAssessmentQuestionID, adminUserId); + } + } + } + + + return maxFrameworkCompetencyGroupId; } From 817cda31be5ea06539d921bf10604b57d03a54aa Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 10 Jan 2025 08:46:33 +0000 Subject: [PATCH 10/12] TD-5163 Fixes navigation and titles --- .../Controllers/FrameworksController/ImportCompetencies.cs | 4 ++++ .../Views/Frameworks/Developer/Import/ImportCompleted.cshtml | 2 +- .../Views/Frameworks/Developer/Import/ImportSummary.cshtml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs index 43c91e9347..846eeca19a 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs @@ -194,6 +194,10 @@ public IActionResult AddAssessmentQuestions(AddAssessmentQuestionsFormData model public IActionResult AddQuestionsToWhichCompetencies() { var data = GetBulkUploadData(); + if (data.DefaultQuestionIDs.Count ==0 && data.CustomAssessmentQuestionID == null) + { + return RedirectToAction("ImportSummary", "Frameworks", new { frameworkId = data.FrameworkId, tabname = data.TabName }); + } var model = new AddQuestionsToWhichCompetenciesViewModel ( data.FrameworkId, diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml index 059712b059..3a5cd7aa8d 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml @@ -28,7 +28,7 @@ }
    -

    @Model.FrameworkVocabularySingular.ToLower() file uploaded

    +

    @Model.FrameworkVocabularySingular 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:")

      diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml index fedee7029a..b14052b460 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportSummary.cshtml @@ -102,7 +102,7 @@

      Once @Model.FrameworkVocabularySingular.ToLower() records are processed, changes cannot be undone.

    - Back + 0 ? "AddQuestionsToWhichCompetencies" : "AddAssessmentQuestions") role="button" class="nhsuk-button nhsuk-button--secondary">Back
    From 90a7bb1054f545623cade29568c18528c162b620 Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 10 Jan 2025 08:47:35 +0000 Subject: [PATCH 11/12] TD-5163 Fixes competency group reordering by reloading the committed order within the iterator --- .../Services/ImportCompetenciesFromFileService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs index b8b7883f23..c29bb1b177 100644 --- a/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs +++ b/DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs @@ -133,12 +133,11 @@ internal ImportCompetenciesResult ProcessCompetenciesTable(IXLTable table, int a .Select(row => row.CompetencyGroup) .Distinct() .ToList(); - - var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).Select(row => new { row.ID, row.Name }) - .Distinct() - .ToList(); for (int i = 0; i < competencyGroupCount; i++) { + var existingGroups = frameworkService.GetFrameworkCompetencyGroups(frameworkId).Select(row => new { row.ID, row.Name }) + .Distinct() + .ToList(); var placesToMove = Math.Abs(existingGroups.FindIndex(group => group.Name == distinctCompetencyGroups[i])-i); if (placesToMove > 0) { From 81817ba6ca1d5fa0f3a2a7eee5b813f23edc3c7a Mon Sep 17 00:00:00 2001 From: kevwhitt-hee Date: Fri, 10 Jan 2025 09:14:59 +0000 Subject: [PATCH 12/12] Applies BR DLSFW1 to standard insert competency group functionality --- .../Controllers/FrameworksController/Competencies.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs index 328082ec28..0d26aee6a3 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Competencies.cs @@ -71,7 +71,7 @@ public IActionResult AddEditFrameworkCompetencyGroup(int frameworkId, Competency (competencyGroupBase.Description), adminId); return new RedirectResult(Url.Action("ViewFramework", new { tabname = "Structure", frameworkId }) + "#fcgroup-" + frameworkCompetencyGroupId.ToString()); } - var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId); + var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyGroupBase.Name, SanitizerHelper.SanitizeHtmlData(competencyGroupBase.Description), adminId, frameworkId); if (newCompetencyGroupId > 0) { var newFrameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminId);