Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2404,7 +2404,7 @@ public IEnumerable<BulkCompetency> GetBulkCompetenciesForFramework(int framework
if (frameworkId < 1)
{
return connection.Query<BulkCompetency>(
@"SELECT NULL AS ID, '' AS CompetencyGroup, '' AS GroupDescription, '' AS Competency, '' AS CompetencyDescription, NULL AS AlwaysShowDescription, '' AS FlagsCsv"
@"SELECT NULL AS ID, '' AS CompetencyGroup, '' AS GroupDescription, '' AS Competency, '' AS CompetencyDescription, 0 AS AlwaysShowDescription, '' AS FlagsCsv"
);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ public CompetencyTableRow(IXLTable table, IXLRangeRow row)

RowNumber = row.RowNumber();
id = row.Cell(1).GetValue<int?>();
CompetencyGroup = FindFieldValue("CompetencyGroup");
Competency = FindFieldValue("Competency");
CompetencyDescription = FindFieldValue("CompetencyDescription");
GroupDescription = FindFieldValue("GroupDescription");
CompetencyGroup = row.Cell(2).GetValue<string?>();
GroupDescription = row.Cell(3).GetValue<string?>();
Competency = row.Cell(4).GetValue<string?>();
CompetencyDescription = row.Cell(5).GetValue<string?>();
AlwaysShowDescriptionRaw = FindFieldValue("AlwaysShowDescription");
AlwaysShowDescription = bool.TryParse(AlwaysShowDescriptionRaw, out var hasPrn) ? hasPrn : (bool?)null;
FlagsCsv = FindFieldValue("FlagsCSV");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ IReadOnlyCollection<CompetencyTableRow> competencyTableRows
{
ProcessedCount = competencyTableRows.Count;
CompetencyAddedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted);
CompetencyUpdatedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyUpdated);
GroupAddedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.CompetencyGroupInserted | dr.RowStatus == RowStatus.CompetencyGroupAndCompetencyInserted);
SkippedCount = competencyTableRows.Count(dr => dr.RowStatus == RowStatus.Skipped);
Errors = competencyTableRows.Where(dr => dr.Error.HasValue).Select(dr => (dr.RowNumber, dr.Error!.Value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using DigitalLearningSolutions.Web.Helpers;
using DigitalLearningSolutions.Web.Models;
using DigitalLearningSolutions.Web.Services;
using DigitalLearningSolutions.Web.ViewModels.Frameworks;
using DigitalLearningSolutions.Web.ViewModels.Frameworks.Import;
using GDS.MultiPageFormData.Enums;
using Microsoft.AspNetCore.Mvc;
using System.IO;
Expand All @@ -16,21 +16,19 @@ public partial class FrameworksController
public IActionResult ImportCompetencies(int frameworkId, string tabname, bool isNotBlank)
{
var adminId = GetAdminId();
var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminId);
var userRole = frameworkService.GetAdminUserRoleForFrameworkId(adminId, frameworkId);
if (userRole < 2)
return StatusCode(403);
var model = new ImportCompetenciesViewModel()
{
FrameworkId = frameworkId,
IsNotBlank = isNotBlank
};
return View("Developer/ImportCompetencies", model);
var model = new ImportCompetenciesViewModel(framework, isNotBlank);

return View("Developer/Import/Index", model);
}
public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption)
public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption, string vocabulary)
{
string fileName = DownloadOption == 2 ? $"DLS Competencies for Bulk Update {clockUtility.UtcToday:yyyy-MM-dd}.xlsx" : "DLS Competencies for Bulk Upload.xlsx";
string fileName = DownloadOption == 2 ? $"DLS {FrameworkVocabularyHelper.VocabularyPlural(vocabulary)} for Bulk Update {clockUtility.UtcToday:yyyy-MM-dd}.xlsx" : $"DLS {FrameworkVocabularyHelper.VocabularyPlural(vocabulary)} for Bulk Upload.xlsx";
var content = importCompetenciesFromFileService.GetCompetencyFileForFramework(
frameworkId, DownloadOption == 2 ? false : true
frameworkId, DownloadOption == 2 ? false : true, vocabulary
);
return File(
content,
Expand All @@ -40,24 +38,24 @@ public IActionResult DownloadCompetencies(int frameworkId, int DownloadOption)
}
[HttpPost]
[Route("/Framework/{frameworkId}/{tabname}/Import")]
[Route("/Framework/{frameworkId}/{tabname}/ImportCompleted")]
public IActionResult StartImport(ImportCompetenciesViewModel model, string tabname, bool isNotBlank)
[Route("/Framework/{frameworkId}/{tabname}/Import/Uploaded")]
public IActionResult StartImport(ImportCompetenciesFormData model, int frameworkId, string tabname, bool isNotBlank)
{
if (!ModelState.IsValid)
return View("Developer/ImportCompetencies", model);
return View("Developer/Import/Index", model);
try
{
var adminUserID = User.GetAdminIdKnownNotNull();
var workbook = new XLWorkbook(model.ImportFile.OpenReadStream());
if (!workbook.Worksheets.Contains(ImportCompetenciesFromFileService.CompetenciesSheetName))
{
ModelState.AddModelError("ImportFile", CommonValidationErrorMessages.InvalidCompetenciesUploadExcelFile);
return View("Developer/ImportCompetencies", model);
return View("Developer/Import/Index", model);
}
var competenciesFileName = FileHelper.UploadFile(webHostEnvironment, model.ImportFile);
setupBulkUploadData(model.FrameworkId, adminUserID, competenciesFileName, tabname, isNotBlank);
setupBulkUploadData(frameworkId, adminUserID, competenciesFileName, tabname, isNotBlank);

return RedirectToAction("ImportCompleted", "Frameworks", new { frameworkId = model.FrameworkId, tabname });
return RedirectToAction("ImportCompleted", "Frameworks", new { frameworkId, tabname });
}
catch (DocumentFormat.OpenXml.Packaging.OpenXmlPackageException)
{
Expand All @@ -66,10 +64,10 @@ public IActionResult StartImport(ImportCompetenciesViewModel model, string tabna
}
catch (InvalidHeadersException)
{
return View("Developer/ImportFailed");
return View("Developer/Import/ImportFailed");
}
}
[Route("/Framework/{frameworkId}/{tabname}/ImportCompleted")]
[Route("/Framework/{frameworkId}/{tabname}/Import/Uploaded")]
public IActionResult ImportCompleted()
{
var data = GetBulkUploadData();
Expand All @@ -78,27 +76,34 @@ public IActionResult ImportCompleted()
var workbook = new XLWorkbook(filePath);
try
{
var results = importCompetenciesFromFileService.PreProcessCompetenciesTable(workbook);
var resultsModel = new ImportCompetenciesPreProcessViewModel(results) { IsNotBlank = data.IsNotBlank, TabName = data.TabName };
var results = importCompetenciesFromFileService.PreProcessCompetenciesTable(workbook, data.FrameworkVocubulary);
var resultsModel = new ImportCompetenciesPreProcessViewModel(results, data) { IsNotBlank = data.IsNotBlank, TabName = data.TabName };
data.CompetenciesToProcessCount = resultsModel.ToProcessCount;
data.CompetenciesToAddCount = resultsModel.CompetenciesToAddCount;
data.CompetenciesToUpdateCount = resultsModel.CompetenciesToUpdateCount;
setBulkUploadData(data);
return View("Developer/ImportCompleted", resultsModel);
return View("Developer/Import/ImportCompleted", resultsModel);
}
catch (InvalidHeadersException)
{
FileHelper.DeleteFile(webHostEnvironment, data.CompetenciesFileName);
return View("ImportFailed");
}
}
[Route("/Framework/{frameworkId}/{tabname}/Import/AssessmentQuestions")]
public IActionResult AddAssessmentQuestions()
{
var data = GetBulkUploadData();

return View();
}
private void setupBulkUploadData(int frameworkId, int adminUserID, string competenciessFileName, string tabName, bool isNotBlank)
{
TempData.Clear();
multiPageFormService.ClearMultiPageFormData(MultiPageFormDataFeature.AddCustomWebForm("BulkCompetencyDataCWF"), TempData);
var framework = frameworkService.GetFrameworkDetailByFrameworkId(frameworkId, adminUserID);
var today = clockUtility.UtcToday;
var bulkUploadData = new BulkCompetenciesData(frameworkId, adminUserID, competenciessFileName, tabName, isNotBlank);
var bulkUploadData = new BulkCompetenciesData(framework, adminUserID, competenciessFileName, tabName, isNotBlank);
setBulkUploadData(bulkUploadData);
}
private void setBulkUploadData(BulkCompetenciesData bulkUploadData)
Expand Down
14 changes: 11 additions & 3 deletions DigitalLearningSolutions.Web/Models/BulkCompetenciesData.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
using System.Collections.Generic;
using DigitalLearningSolutions.Data.Models.Frameworks;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace DigitalLearningSolutions.Web.Models
{
public class BulkCompetenciesData
{
public BulkCompetenciesData() { }
public BulkCompetenciesData(int frameworkId, int adminUserId, string competenciesFileName, string tabName, bool isNotBlank)
public BulkCompetenciesData(DetailFramework framework, int adminUserId, string competenciesFileName, string tabName, bool isNotBlank)
{
FrameworkId = frameworkId;
FrameworkId = framework.ID;
FrameworkName = framework.FrameworkName;
PublishStatusID = framework.PublishStatusID;
FrameworkVocubulary = framework.FrameworkConfig;
AdminUserId = adminUserId;
CompetenciesFileName = competenciesFileName;
TabName = tabName;
IsNotBlank = isNotBlank;
}
public int FrameworkId { get; set; }
public string? FrameworkName { get; set; }
public int PublishStatusID { get; set; }
public string FrameworkVocubulary { get; set; }
public string TabName { get; set; }
public int AdminUserId { get; set; }
public bool IsNotBlank { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,27 @@ namespace DigitalLearningSolutions.Web.Services
using DigitalLearningSolutions.Data.Exceptions;
using DigitalLearningSolutions.Data.Helpers;
using DigitalLearningSolutions.Data.Models.Frameworks.Import;
using DigitalLearningSolutions.Web.Models;
using Microsoft.AspNetCore.Http;

public interface IImportCompetenciesFromFileService
{
byte[] GetCompetencyFileForFramework(int frameworkId, bool v);
public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook);
public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId);
byte[] GetCompetencyFileForFramework(int frameworkId, bool isBlank, string vocabulary);
public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook, string vocabulary);
public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary);
}
public class ImportCompetenciesFromFileService : IImportCompetenciesFromFileService
{
private readonly IFrameworkService frameworkService;
private static readonly XLTableTheme TableTheme = XLTableTheme.TableStyleLight9;
public const string CompetenciesSheetName = "CompetenciesBulkUpload";
public const string CompetenciesSheetName = "FrameworkBulkUpload";
public ImportCompetenciesFromFileService(
IFrameworkService frameworkService
)
{
this.frameworkService = frameworkService;
}
public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook)
public ImportCompetenciesResult PreProcessCompetenciesTable(IXLWorkbook workbook, string vocabulary)
{
var table = OpenCompetenciesTable(workbook);
var table = OpenCompetenciesTable(workbook, vocabulary);
var competencyRows = table.Rows().Skip(1).Select(row => new CompetencyTableRow(table, row)).ToList();
foreach (var competencyRow in competencyRows)
{
Expand All @@ -54,14 +52,14 @@ private void PreProcessCompetencyRow(CompetencyTableRow competencyRow)
competencyRow.RowStatus = RowStatus.CompetencyUpdated;
}
}
public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId)
public ImportCompetenciesResult ProcessCompetenciesFromFile(IXLWorkbook workbook, int adminUserId, int frameworkId, string vocabulary)
{
int maxFrameworkCompetencyId = frameworkService.GetMaxFrameworkCompetencyID();
int maxFrameworkCompetencyGroupId = frameworkService.GetMaxFrameworkCompetencyGroupID();
var table = OpenCompetenciesTable(workbook);
var table = OpenCompetenciesTable(workbook, vocabulary);
return ProcessCompetenciesTable(table, adminUserId, frameworkId, maxFrameworkCompetencyId, maxFrameworkCompetencyGroupId);
}
internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook)
internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook, string vocabulary)
{
var worksheet = workbook.Worksheet(1);
worksheet.Columns(1, 15).Unhide();
Expand All @@ -70,7 +68,7 @@ internal IXLTable OpenCompetenciesTable(IXLWorkbook workbook)
throw new InvalidHeadersException();
}
var table = worksheet.Tables.Table(0);
if (!ValidateHeaders(table))
if (!ValidateHeaders(table, vocabulary))
{
throw new InvalidHeadersException();
}
Expand Down Expand Up @@ -132,26 +130,26 @@ CompetencyTableRow competencyRow
return maxFrameworkCompetencyGroupId;
}

private static bool ValidateHeaders(IXLTable table)
private static bool ValidateHeaders(IXLTable table, string Vocabulary)
{
var expectedHeaders = new List<string>
{
"ID",
"CompetencyGroup",
Vocabulary + "Group",
"GroupDescription",
"Competency",
"CompetencyDescription",
Vocabulary,
Vocabulary + "Description",
"AlwaysShowDescription",
"FlagsCSV"
}.OrderBy(x => x);
var actualHeaders = table.Fields.Select(x => x.Name).OrderBy(x => x);
return actualHeaders.SequenceEqual(expectedHeaders);
}

public byte[] GetCompetencyFileForFramework(int frameworkId, bool blank)
public byte[] GetCompetencyFileForFramework(int frameworkId, bool blank, string vocabulary)
{
using var workbook = new XLWorkbook();
PopulateCompetenciesSheet(workbook, frameworkId, blank);
PopulateCompetenciesSheet(workbook, frameworkId, blank, vocabulary);
if (blank)
{
ClosedXmlHelper.HideWorkSheetColumn(workbook, "ID");
Expand All @@ -167,7 +165,7 @@ public byte[] GetCompetencyFileForFramework(int frameworkId, bool blank)
workbook.SaveAs(stream);
return stream.ToArray();
}
private void PopulateCompetenciesSheet(IXLWorkbook workbook, int frameworkId, bool blank)
private void PopulateCompetenciesSheet(IXLWorkbook workbook, int frameworkId, bool blank, string vocabulary)
{


Expand All @@ -184,8 +182,10 @@ private void PopulateCompetenciesSheet(IXLWorkbook workbook, int frameworkId, bo
FlagsCSV = x.FlagsCsv,
}
);

ClosedXmlHelper.AddSheetToWorkbook(workbook, CompetenciesSheetName, competencies, TableTheme);
ClosedXmlHelper.RenameWorksheetColumn(workbook, "CompetencyGroup", vocabulary + "Group");
ClosedXmlHelper.RenameWorksheetColumn(workbook, "Competency", vocabulary);
ClosedXmlHelper.RenameWorksheetColumn(workbook, "CompetencyDescription", vocabulary + "Description");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
namespace DigitalLearningSolutions.Web.ViewModels.Frameworks
namespace DigitalLearningSolutions.Web.ViewModels.Frameworks.Import
{
using DigitalLearningSolutions.Web.Attributes;
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;

public class ImportCompetenciesViewModel
public class ImportCompetenciesFormData
{
public int FrameworkId { get; set; }
public bool IsNotBlank { get; set; }
[Required(ErrorMessage = "Import competencies file is required")]
[AllowedExtensions(new[] { ".xlsx" }, "Import competencies file must be in xlsx format")]
[MaxFileSize(5 * 1024 * 1024, "Maximum allowed file size is 5MB")]
Expand Down
Loading
Loading