Skip to content

Commit ac16f0f

Browse files
committed
TD-5153 Implements download competencies and templates functions and upload action
1 parent bc83b0d commit ac16f0f

File tree

11 files changed

+218
-25
lines changed

11 files changed

+218
-25
lines changed

DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,8 +2401,16 @@ FROM FrameworkDefaultQuestions
24012401

24022402
public IEnumerable<BulkCompetency> GetBulkCompetenciesForFramework(int frameworkId)
24032403
{
2404-
return connection.Query<BulkCompetency>(
2405-
@"SELECT fc.ID, cg.Name AS CompetencyGroup, cg.Description AS GroupDescription, c.Name AS Competency, c.Description AS CompetencyDescription, c.AlwaysShowDescription, STRING_AGG(f.FlagName, ', ') AS FlagsCsv
2404+
if(frameworkId < 1)
2405+
{
2406+
return connection.Query<BulkCompetency>(
2407+
@"SELECT NULL AS ID, '' AS CompetencyGroup, '' AS GroupDescription, '' AS Competency, '' AS CompetencyDescription, NULL AS AlwaysShowDescription, '' AS FlagsCsv"
2408+
);
2409+
}
2410+
else
2411+
{
2412+
return connection.Query<BulkCompetency>(
2413+
@"SELECT fc.ID, cg.Name AS CompetencyGroup, cg.Description AS GroupDescription, c.Name AS Competency, c.Description AS CompetencyDescription, c.AlwaysShowDescription, STRING_AGG(f.FlagName, ', ') AS FlagsCsv
24062414
FROM Flags AS f RIGHT OUTER JOIN
24072415
CompetencyFlags AS cf ON f.ID = cf.FlagID RIGHT OUTER JOIN
24082416
Competencies AS c INNER JOIN
@@ -2412,8 +2420,10 @@ Competencies AS c INNER JOIN
24122420
WHERE (fc.FrameworkID = @frameworkId)
24132421
GROUP BY fc.ID, cg.Name, cg.Description, c.Name, c.Description, c.AlwaysShowDescription, fcg.Ordering, fc.Ordering
24142422
ORDER BY fcg.Ordering, fc.Ordering",
2415-
new { frameworkId }
2416-
);
2423+
new { frameworkId }
2424+
);
2425+
}
2426+
24172427
}
24182428
}
24192429
}

DigitalLearningSolutions.Web/Controllers/FrameworksController/FrameworksController.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.Extensions.Logging;
1212
using GDS.MultiPageFormData;
1313
using DigitalLearningSolutions.Data.Utilities;
14+
using Microsoft.AspNetCore.Hosting;
1415

1516
[Authorize(Policy = CustomPolicies.UserFrameworksAdminOnly)]
1617
[SetDlsSubApplication(nameof(DlsSubApplication.Frameworks))]
@@ -27,7 +28,7 @@ public partial class FrameworksController : Controller
2728
private readonly ISearchSortFilterPaginateService searchSortFilterPaginateService;
2829
private readonly IMultiPageFormService multiPageFormService;
2930
private readonly IClockUtility clockUtility;
30-
31+
private readonly IWebHostEnvironment webHostEnvironment;
3132
public FrameworksController(
3233
IFrameworkService frameworkService,
3334
ICommonService commonService,
@@ -38,7 +39,8 @@ public FrameworksController(
3839
ILearningHubApiClient learningHubApiClient,
3940
ISearchSortFilterPaginateService searchSortFilterPaginateService,
4041
IMultiPageFormService multiPageFormService,
41-
IClockUtility clockUtility
42+
IClockUtility clockUtility,
43+
IWebHostEnvironment webHostEnvironment
4244
)
4345
{
4446
this.frameworkService = frameworkService;
@@ -51,6 +53,7 @@ IClockUtility clockUtility
5153
this.searchSortFilterPaginateService = searchSortFilterPaginateService;
5254
this.multiPageFormService = multiPageFormService;
5355
this.clockUtility = clockUtility;
56+
this.webHostEnvironment = webHostEnvironment;
5457
}
5558

5659
private int? GetCentreId()

DigitalLearningSolutions.Web/Controllers/FrameworksController/ImportCompetencies.cs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1-
using DigitalLearningSolutions.Data.Exceptions;
1+
using ClosedXML.Excel;
2+
using DigitalLearningSolutions.Data.Exceptions;
3+
using DigitalLearningSolutions.Data.Migrations;
24
using DigitalLearningSolutions.Data.Utilities;
35
using DigitalLearningSolutions.Web.Helpers;
6+
using DigitalLearningSolutions.Web.Models;
47
using DigitalLearningSolutions.Web.Services;
58
using DigitalLearningSolutions.Web.ViewModels.Frameworks;
9+
using GDS.MultiPageFormData.Enums;
10+
using Microsoft.AspNetCore.Hosting;
611
using Microsoft.AspNetCore.Mvc;
712

813
namespace DigitalLearningSolutions.Web.Controllers.FrameworksController
@@ -22,10 +27,9 @@ public IActionResult ImportCompetencies(int frameworkId, string tabname)
2227
};
2328
return View("Developer/ImportCompetencies", model);
2429
}
25-
[Route("DownloadDelegates")]
2630
public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption)
2731
{
28-
string fileName = DownloadOption == 2 ? $"DLS Competencies for Bulk Update {clockUtility.UtcToday:yyyy-MM-dd}.xlsx" : "DLS Delegates for Bulk Registration.xlsx";
32+
string fileName = DownloadOption == 2 ? $"DLS Competencies for Bulk Update {clockUtility.UtcToday:yyyy-MM-dd}.xlsx" : "DLS Competencies for Bulk Upload.xlsx";
2933
var content = importCompetenciesFromFileService.GetCompetencyFileForFramework(
3034
frameworkId, DownloadOption == 2 ? false : true
3135
);
@@ -37,24 +41,64 @@ public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption)
3741
}
3842
[HttpPost]
3943
[Route("/Framework/{frameworkId}/{tabname}/Import")]
40-
public IActionResult StartImport(ImportCompetenciesViewModel model)
44+
public IActionResult StartImport(ImportCompetenciesViewModel model, string tabname)
4145
{
4246
if (!ModelState.IsValid)
4347
return View("Developer/ImportCompetencies", model);
4448
try
4549
{
46-
var results = importCompetenciesFromFileService.ProcessCompetenciesFromFile(
47-
model.ImportFile!,
48-
GetAdminId(),
49-
model.FrameworkId
50-
);
51-
var resultsModel = new ImportCompetenciesResultsViewModel(results);
52-
return View("Developer/ImportCompleted", resultsModel);
50+
var adminUserID = User.GetAdminIdKnownNotNull();
51+
var workbook = new XLWorkbook(model.ImportFile.OpenReadStream());
52+
if (!workbook.Worksheets.Contains(ImportCompetenciesFromFileService.CompetenciesSheetName))
53+
{
54+
ModelState.AddModelError("InvalidWorksheet", CommonValidationErrorMessages.InvalidCompetenciesUploadExcelFile);
55+
return View("Developer/ImportCompetencies", model);
56+
}
57+
var competenciesFileName = FileHelper.UploadFile(webHostEnvironment, model.ImportFile);
58+
setupBulkUploadData(model.FrameworkId, adminUserID, competenciesFileName, tabname);
59+
60+
return RedirectToAction("ImportCompleted", "Frameworks", new { frameworkId = model.FrameworkId, tabname });
61+
}
62+
catch (DocumentFormat.OpenXml.Packaging.OpenXmlPackageException)
63+
{
64+
ModelState.AddModelError("DelegatesFile", "The Excel file has at least one cell containing an invalid hyperlink or email address.");
65+
return View("Index", model);
5366
}
5467
catch (InvalidHeadersException)
5568
{
5669
return View("Developer/ImportFailed");
5770
}
5871
}
72+
[Route("/Framework/{frameworkId}/{tabname}/ImportCompleted")]
73+
public IActionResult ImportCompleted()
74+
{
75+
return View("Developer/ImportCompleted");
76+
}
77+
78+
private void setupBulkUploadData(int frameworkId, int adminUserID, string competenciessFileName, string tabName)
79+
{
80+
TempData.Clear();
81+
multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("BulkCompetencyDataCWF"), TempData);
82+
var today = clockUtility.UtcToday;
83+
var bulkUploadData = new BulkCompetenciesData(frameworkId, adminUserID, competenciessFileName, tabName);
84+
setBulkUploadData(bulkUploadData);
85+
}
86+
private void setBulkUploadData(BulkCompetenciesData bulkUploadData)
87+
{
88+
multiPageFormService.SetMultiPageFormData(
89+
bulkUploadData,
90+
MultiPageFormDataFeature.AddCustomWebForm("BulkCompetencyDataCWF"),
91+
TempData
92+
);
93+
}
94+
95+
private BulkCompetenciesData GetBulkUploadData()
96+
{
97+
var data = multiPageFormService.GetMultiPageFormData<BulkCompetenciesData>(
98+
MultiPageFormDataFeature.AddCustomWebForm("BulkCompetencyDataCWF"),
99+
TempData
100+
).GetAwaiter().GetResult();
101+
return data;
102+
}
59103
}
60104
}

DigitalLearningSolutions.Web/Helpers/CommonValidationErrorMessages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public static class CommonValidationErrorMessages
3636
public const string CentreNameAlreadyExist = "The centre name you have entered already exists, please enter a different centre name";
3737
public const string MaxBulkUploadRowsLimit = "File must contain no more than {0} rows";
3838
public const string InvalidBulkUploadExcelFile = "The uploaded file must contain a \"DelegatesBulkUpload\" worksheet. Use the \"Download delegates\" button to generate a template.";
39+
public const string InvalidCompetenciesUploadExcelFile = "The uploaded file must contain a \"CompetenciesBulkUpload\" worksheet. Use the \"Download competencies\" button to generate a template.";
3940
public const string ReportFilterReturnsTooManyRows = "The report frequency is too high for the date range. Choose a lower report frequency (or shorten the date range)";
4041
}
4142
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace DigitalLearningSolutions.Web.Models
5+
{
6+
public class BulkCompetenciesData
7+
{
8+
public BulkCompetenciesData() { }
9+
public BulkCompetenciesData(int frameworkId, int adminUserId, string competenciesFileName, string tabName)
10+
{
11+
FrameworkId = frameworkId;
12+
AdminUserId = adminUserId;
13+
CompetenciesFileName = competenciesFileName;
14+
TabName = tabName;
15+
}
16+
public int FrameworkId { get; set; }
17+
public string TabName { get; set; }
18+
public int AdminUserId { get; set; }
19+
public string CompetenciesFileName { get; set; }
20+
public List<int> AssessmentQuestionIDs { get; set; }
21+
public int? AddAssessmentQuestionOption { get; set; }
22+
public int ToProcessCount { get; set; }
23+
public int ToAddCount { get; set; }
24+
public int ToUpdateCount { get; set; }
25+
public int LastRowProcessed { get; set; }
26+
public int SubtotalCompetenciesAdded { get; set; }
27+
public int SubtotalCompetenciesUpdated { get; set; }
28+
public int SubTotalSkipped { get; set; }
29+
public IEnumerable<(int RowNumber, string ErrorMessage)> Errors { get; set; } = Enumerable.Empty<(int, string)>();
30+
}
31+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace DigitalLearningSolutions.Web.Models
2+
{
3+
public class BulkCompetenciesResult
4+
{
5+
}
6+
}

DigitalLearningSolutions.Web/Services/FrameworkService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using DigitalLearningSolutions.Data.Models.Common;
33
using DigitalLearningSolutions.Data.Models.Email;
44
using DigitalLearningSolutions.Data.Models.Frameworks;
5+
using DigitalLearningSolutions.Data.Models.Frameworks.Import;
56
using DigitalLearningSolutions.Data.Models.SelfAssessments;
67
using System.Collections.Generic;
78
using AssessmentQuestion = DigitalLearningSolutions.Data.Models.Frameworks.AssessmentQuestion;
@@ -55,6 +56,7 @@ public interface IFrameworkService
5556
int GetMaxFrameworkCompetencyID();
5657

5758
int GetMaxFrameworkCompetencyGroupID();
59+
IEnumerable<BulkCompetency> GetBulkCompetenciesForFramework(int frameworkId);
5860

5961
// Assessment questions:
6062
IEnumerable<AssessmentQuestion> GetAllCompetencyQuestions(int adminId);
@@ -379,6 +381,11 @@ public IEnumerable<GenericSelectList> GetAssessmentQuestionsForCompetency(int fr
379381
return frameworkDataService.GetBrandedFrameworkByFrameworkId(frameworkId, adminId);
380382
}
381383

384+
public IEnumerable<BulkCompetency> GetBulkCompetenciesForFramework(int frameworkId)
385+
{
386+
return frameworkDataService.GetBulkCompetenciesForFramework(frameworkId);
387+
}
388+
382389
public CollaboratorNotification? GetCollaboratorNotification(int id, int invitedByAdminId)
383390
{
384391
return frameworkDataService.GetCollaboratorNotification(id, invitedByAdminId);

DigitalLearningSolutions.Web/Services/ImportCompetenciesFromFileService.cs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
namespace DigitalLearningSolutions.Web.Services
66
{
7+
using System;
78
using System.Collections.Generic;
89
using System.IO;
910
using System.Linq;
1011
using ClosedXML.Excel;
11-
using DigitalLearningSolutions.Data.DataServices;
1212
using DigitalLearningSolutions.Data.Exceptions;
1313
using DigitalLearningSolutions.Data.Helpers;
14-
using DigitalLearningSolutions.Data.Models.Centres;
1514
using DigitalLearningSolutions.Data.Models.Frameworks.Import;
1615
using Microsoft.AspNetCore.Http;
1716

@@ -23,6 +22,8 @@ public interface IImportCompetenciesFromFileService
2322
public class ImportCompetenciesFromFileService : IImportCompetenciesFromFileService
2423
{
2524
private readonly IFrameworkService frameworkService;
25+
private static readonly XLTableTheme TableTheme = XLTableTheme.TableStyleLight9;
26+
public const string CompetenciesSheetName = "CompetenciesBulkUpload";
2627
public ImportCompetenciesFromFileService(
2728
IFrameworkService frameworkService
2829
)
@@ -76,9 +77,9 @@ CompetencyTableRow competencyRow
7677
}
7778
//If competency group is set, check if competency group exists within framework and add if not and get the Framework Competency Group ID
7879
int? frameworkCompetencyGroupId = null;
79-
if (competencyRow.CompetencyGroupName != null)
80+
if (competencyRow.CompetencyGroup != null)
8081
{
81-
var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroupName, null, adminUserId);
82+
var newCompetencyGroupId = frameworkService.InsertCompetencyGroup(competencyRow.CompetencyGroup, null, adminUserId);
8283
if (newCompetencyGroupId > 0)
8384
{
8485
frameworkCompetencyGroupId = frameworkService.InsertFrameworkCompetencyGroup(newCompetencyGroupId, frameworkId, adminUserId);
@@ -91,7 +92,7 @@ CompetencyTableRow competencyRow
9192
}
9293

9394
//Check if competency already exists in framework competency group and add if not
94-
var newCompetencyId = frameworkService.InsertCompetency(competencyRow.CompetencyName, competencyRow.CompetencyDescription, adminUserId);
95+
var newCompetencyId = frameworkService.InsertCompetency(competencyRow.Competency, competencyRow.CompetencyDescription, adminUserId);
9596
if (newCompetencyId > 0)
9697
{
9798
var newFrameworkCompetencyId = frameworkService.InsertFrameworkCompetency(newCompetencyId, frameworkCompetencyGroupId, adminUserId, frameworkId);
@@ -125,11 +126,31 @@ public byte[] GetCompetencyFileForFramework(int frameworkId, bool blank)
125126
PopulateCompetenciesSheet(workbook, frameworkId, blank);
126127
if (blank)
127128
{
128-
ClosedXmlHelper.HideWorkSheetColumn(workbook, "DelegateID");
129+
ClosedXmlHelper.HideWorkSheetColumn(workbook, "ID");
129130
}
130131
using var stream = new MemoryStream();
131132
workbook.SaveAs(stream);
132133
return stream.ToArray();
133134
}
135+
private void PopulateCompetenciesSheet(IXLWorkbook workbook, int frameworkId, bool blank)
136+
{
137+
138+
139+
var competencyRecords = frameworkService.GetBulkCompetenciesForFramework(blank ? 0 : frameworkId);
140+
var competencies = competencyRecords.Select(
141+
x => new
142+
{
143+
ID = x.id,
144+
x.CompetencyGroup,
145+
x.GroupDescription,
146+
x.Competency,
147+
x.CompetencyDescription,
148+
x.AlwaysShowDescription,
149+
FlagsCSV = x.FlagsCsv,
150+
}
151+
);
152+
153+
ClosedXmlHelper.AddSheetToWorkbook(workbook, CompetenciesSheetName, competencies, TableTheme);
154+
}
134155
}
135156
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace DigitalLearningSolutions.Web.ViewModels.Frameworks
2+
{
3+
public class ImportCompetenciesPreProcessViewModel
4+
{
5+
}
6+
}

DigitalLearningSolutions.Web/Views/Frameworks/Developer/ImportCompetencies.cshtml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
This Excel file will be empty.<br />
6060
New competencies can be added by including their details on a blank row.
6161
</div>
62-
<a class="nhsuk-button nhsuk-button--secondary" role="button" asp-route-DownloadOption="1" asp-controller="FrameworksController" asp-action="DownloadCompetencies" target="_blank">
62+
<a class="nhsuk-button nhsuk-button--secondary" role="button" asp-route-frameworkId="@ViewContext.RouteData.Values["frameworkId"]" asp-route-DownloadOption="1" asp-controller="Frameworks" asp-action="DownloadCompetencies" target="_blank">
6363
Download template
6464
</a>
6565
</div>
@@ -74,7 +74,7 @@
7474
This Excel file will include all existing competencies whose details you can update.<br />
7575
New competencies can be added by including their details on a blank row.
7676
</div>
77-
<a class="nhsuk-button nhsuk-button--secondary" role="button" asp-route-DownloadOption="2" asp-controller="FrameworksController" asp-action="DownloadCompetencies" target="_blank">
77+
<a class="nhsuk-button nhsuk-button--secondary" role="button" asp-route-frameworkId="@ViewContext.RouteData.Values["frameworkId"]" asp-route-DownloadOption="2" asp-controller="Frameworks" asp-action="DownloadCompetencies" target="_blank">
7878
Download competencies
7979
</a>
8080
</div>

0 commit comments

Comments
 (0)