diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/DCSAReportDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/DCSAReportDataService.cs new file mode 100644 index 0000000000..c3d47bcdf4 --- /dev/null +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/DCSAReportDataService.cs @@ -0,0 +1,118 @@ +namespace DigitalLearningSolutions.Data.DataServices.SelfAssessmentDataService +{ + using Dapper; + using DigitalLearningSolutions.Data.Models.SelfAssessments.Export; + using Microsoft.Extensions.Logging; + using System.Collections.Generic; + using System.Data; + + public interface IDCSAReportDataService + { + IEnumerable GetDelegateCompletionStatusForCentre(int centreId); + IEnumerable GetOutcomeSummaryForCentre(int centreId); + } + public partial class DCSAReportDataService : IDCSAReportDataService + { + private readonly IDbConnection connection; + private readonly ILogger logger; + + public DCSAReportDataService(IDbConnection connection, ILogger logger) + { + this.connection = connection; + this.logger = logger; + } + public IEnumerable GetOutcomeSummaryForCentre(int centreId) + { + return connection.Query( + @"SELECT DATEPART(month, caa.StartedDate) AS EnrolledMonth, DATEPART(yyyy, caa.StartedDate) AS EnrolledYear, jg.JobGroupName AS JobGroup, ca.Answer1 AS CentreField1, ca.Answer2 AS CentreField2, ca.Answer3 AS CentreField3, CASE WHEN (caa.SubmittedDate IS NOT NULL) + THEN 'Submitted' WHEN (caa.UserBookmark LIKE N'/LearningPortal/SelfAssessment/1/Review' AND caa.SubmittedDate IS NULL) THEN 'Reviewing' ELSE 'Incomplete' END AS Status, + (SELECT COUNT(*) AS Expr1 + FROM FilteredLearningActivity AS fla + WHERE (CandidateId = ca.CandidateID)) AS LearningLaunched, + (SELECT COUNT(*) AS Expr1 + FROM FilteredLearningActivity AS fla + WHERE (CandidateId = ca.CandidateID) AND (CompletedDate IS NOT NULL)) AS LearningCompleted, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 1) AND (sar.CandidateID = ca.CandidateID)) AS DataInformationAndContentConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 1) AND (sar.CandidateID = ca.CandidateID)) AS DataInformationAndContentRelevance, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 2) AND (sar.CandidateID = ca.CandidateID)) AS TeachingLearningAndSelfDevelopmentConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 2) AND (sar.CandidateID = ca.CandidateID)) AS TeachingLearningAndSelfDevelopmentRelevance, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 3) AND (sar.CandidateID = ca.CandidateID)) AS CommunicationCollaborationParticipationConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 3) AND (sar.CandidateID = ca.CandidateID)) AS CommunicationCollaborationParticipationRelevance, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 4) AND (sar.CandidateID = ca.CandidateID)) AS TechnicalProficiencyConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 4) AND (sar.CandidateID = ca.CandidateID)) AS TechnicalProficiencyRelevance, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 5) AND (sar.CandidateID = ca.CandidateID)) AS CreationInnovationResearchConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 5) AND (sar.CandidateID = ca.CandidateID)) AS CreationInnovationResearchRelevance, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 1) AND (sas.CompetencyGroupID = 6) AND (sar.CandidateID = ca.CandidateID)) AS DigitalIdentityWellbeingSafetyAndSecurityConfidence, + (SELECT AVG(sar.Result) AS AvgConfidence + FROM SelfAssessmentResults AS sar INNER JOIN + Competencies AS co ON sar.CompetencyID = co.ID INNER JOIN + SelfAssessmentStructure AS sas ON co.ID = sas.CompetencyID + WHERE (sar.AssessmentQuestionID = 2) AND (sas.CompetencyGroupID = 6) AND (sar.CandidateID = ca.CandidateID)) AS DigitalIdentityWellbeingSafetyAndSecurityRelevance + FROM Candidates AS ca INNER JOIN + CandidateAssessments AS caa ON ca.CandidateID = caa.CandidateID INNER JOIN + JobGroups AS jg ON ca.JobGroupID = jg.JobGroupID + WHERE (ca.Active = 1) AND (ca.CentreID = @centreId) AND (caa.SelfAssessmentID = 1) + ORDER BY EnrolledYear DESC, EnrolledMonth DESC, JobGroup, CentreField1, CentreField2, CentreField3, Status", + new { centreId } + ); + } + + public IEnumerable GetDelegateCompletionStatusForCentre(int centreId) + { + return connection.Query( + @"SELECT DATEPART(month, caa.StartedDate) AS EnrolledMonth, DATEPART(yyyy, caa.StartedDate) AS EnrolledYear, ca.FirstName, ca.LastName, ca.EmailAddress AS Email, ca.Answer1 AS CentreField1, ca.Answer2 AS CentreField2, ca.Answer3 AS CentreField3, CASE WHEN (caa.SubmittedDate IS NOT NULL) + THEN 'Submitted' WHEN (caa.UserBookmark LIKE N'/LearningPortal/SelfAssessment/1/Review' AND caa.SubmittedDate IS NULL) THEN 'Reviewing' ELSE 'Incomplete' END AS Status + FROM Candidates AS ca INNER JOIN + CandidateAssessments AS caa ON ca.CandidateID = caa.CandidateID INNER JOIN + JobGroups AS jg ON ca.JobGroupID = jg.JobGroupID + WHERE (ca.Active = 1) AND (ca.CentreID = @centreId) AND (caa.SelfAssessmentID = 1) + ORDER BY EnrolledYear DESC, EnrolledMonth DESC, ca.LastName, ca.FirstName", + new { centreId } + ); + } + } +} diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSADelegateCompletionStatus.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSADelegateCompletionStatus.cs new file mode 100644 index 0000000000..31a69d5bc1 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSADelegateCompletionStatus.cs @@ -0,0 +1,16 @@ +namespace DigitalLearningSolutions.Data.Models.SelfAssessments.Export +{ + using System; + public class DCSADelegateCompletionStatus + { + public int? EnrolledMonth { get; set; } + public int? EnrolledYear { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? CentreField1 { get; set; } + public string? CentreField2 { get; set; } + public string? CentreField3 { get; set; } + public string? Status { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSAOutcomeSummary.cs b/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSAOutcomeSummary.cs new file mode 100644 index 0000000000..2f8c0d1ef2 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/SelfAssessments/Export/DCSAOutcomeSummary.cs @@ -0,0 +1,28 @@ +namespace DigitalLearningSolutions.Data.Models.SelfAssessments.Export +{ + using System; + public class DCSAOutcomeSummary + { + public int? EnrolledMonth { get; set; } + public int? EnrolledYear { get; set; } + public string? JobGroup { get; set; } + public string? CentreField1 { get; set; } + public string? CentreField2 { get; set; } + public string? CentreField3 { get; set; } + public string? Status { get; set; } + public int? LearningLaunched { get; set; } + public int? LearningCompleted { get; set; } + public int? DataInformationAndContentConfidence { get; set; } + public int? DataInformationAndContentRelevance { get; set; } + public int? TeachinglearningAndSelfDevelopmentConfidence { get; set; } + public int? TeachinglearningAndSelfDevelopmentRelevance { get; set; } + public int? CommunicationCollaborationAndParticipationConfidence { get; set; } + public int? CommunicationCollaborationAndParticipationRelevance { get; set; } + public int? TechnicalProficiencyConfidence { get; set; } + public int? TechnicalProficiencyRelevance { get; set; } + public int? CreationInnovationAndResearchConfidence { get; set; } + public int? CreationInnovationAndResearchRelevance { get; set; } + public int? DigitalIdentityWellbeingSafetyAndSecurityConfidence { get; set; } + public int? DigitalIdentityWellbeingSafetyAndSecurityRelevance { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Services/SelfAssessmentReportService.cs b/DigitalLearningSolutions.Data/Services/SelfAssessmentReportService.cs new file mode 100644 index 0000000000..dadc3bfb98 --- /dev/null +++ b/DigitalLearningSolutions.Data/Services/SelfAssessmentReportService.cs @@ -0,0 +1,82 @@ +namespace DigitalLearningSolutions.Data.Services +{ + using ClosedXML.Excel; + using DigitalLearningSolutions.Data.DataServices.SelfAssessmentDataService; + using System.Collections.Generic; + using System.IO; + using System.Linq; + + public interface ISelfAssessmentReportService + { + byte[] GetDigitalCapabilityExcelExportForCentre(int centreId); + } + public class SelfAssessmentReportService : ISelfAssessmentReportService + { + private readonly IDCSAReportDataService dcsaReportDataService; + + public SelfAssessmentReportService( + IDCSAReportDataService dcsaReportDataService + ) + { + this.dcsaReportDataService = dcsaReportDataService; + } + public byte[] GetDigitalCapabilityExcelExportForCentre(int centreId) + { + var delegateCompletionStatus = dcsaReportDataService.GetDelegateCompletionStatusForCentre(centreId); + var outcomeSummary = dcsaReportDataService.GetOutcomeSummaryForCentre(centreId); + var summary = delegateCompletionStatus.Select( + x => new + { + x.EnrolledMonth, + x.EnrolledYear, + x.FirstName, + x.LastName, + x.Email, + x.CentreField1, + x.CentreField2, + x.CentreField3, + x.Status + } + ); + var details = outcomeSummary.Select( + x => new + { + x.EnrolledMonth, + x.EnrolledYear, + x.JobGroup, + x.CentreField1, + x.CentreField2, + x.CentreField3, + x.Status, + x.LearningLaunched, + x.LearningCompleted, + x.DataInformationAndContentConfidence, + x.DataInformationAndContentRelevance, + x.TeachinglearningAndSelfDevelopmentConfidence, + x.TeachinglearningAndSelfDevelopmentRelevance, + x.CommunicationCollaborationAndParticipationConfidence, + x.CommunicationCollaborationAndParticipationRelevance, + x.TechnicalProficiencyConfidence, + x.TechnicalProficiencyRelevance, + x.CreationInnovationAndResearchConfidence, + x.CreationInnovationAndResearchRelevance, + x.DigitalIdentityWellbeingSafetyAndSecurityConfidence, + x.DigitalIdentityWellbeingSafetyAndSecurityRelevance + } + ); + using var workbook = new XLWorkbook(); + AddSheetToWorkbook(workbook, "Delegate Completion Status", delegateCompletionStatus); + AddSheetToWorkbook(workbook, "Assessment Outcome Summary", outcomeSummary); + using var stream = new MemoryStream(); + workbook.SaveAs(stream); + return stream.ToArray(); + } + private static void AddSheetToWorkbook(IXLWorkbook workbook, string sheetName, IEnumerable? dataObjects) + { + var sheet = workbook.Worksheets.Add(sheetName); + var table = sheet.Cell(1, 1).InsertTable(dataObjects); + table.Theme = XLTableTheme.TableStyleLight9; + sheet.Columns().AdjustToContents(); + } + } +} diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Reports/ReportsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Reports/ReportsController.cs index e05a1a19f1..f21755f863 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Reports/ReportsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Reports/ReportsController.cs @@ -18,7 +18,7 @@ [Authorize(Policy = CustomPolicies.UserCentreAdmin)] [SetDlsSubApplication(nameof(DlsSubApplication.TrackingSystem))] [SetSelectedTab(nameof(NavMenuTab.Centre))] - [Route("/TrackingSystem/Centre/Reports")] + [Route("/TrackingSystem/Centre/Reports/Courses")] public class ReportsController : Controller { private readonly IActivityService activityService; diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs new file mode 100644 index 0000000000..61920d8649 --- /dev/null +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs @@ -0,0 +1,45 @@ +namespace DigitalLearningSolutions.Web.Controllers.TrackingSystem.Centre.SelfAssessmentReports +{ + using DigitalLearningSolutions.Data.Services; + using DigitalLearningSolutions.Web.Helpers; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + using Microsoft.FeatureManagement.Mvc; + using DigitalLearningSolutions.Web.Attributes; + using DigitalLearningSolutions.Web.Models.Enums; + using DigitalLearningSolutions.Data.Enums; + using System; + [FeatureGate(FeatureFlags.RefactoredTrackingSystem)] + [Authorize(Policy = CustomPolicies.UserCentreAdmin)] + [SetDlsSubApplication(nameof(DlsSubApplication.TrackingSystem))] + [SetSelectedTab(nameof(NavMenuTab.Centre))] + [Route("/TrackingSystem/Centre/Reports/SelfAssessments")] + public class SelfAssessmentReportsController : Controller + { + private readonly ISelfAssessmentReportService selfAssessmentReportService; + + public SelfAssessmentReportsController( + ISelfAssessmentReportService selfAssessmentReportService + ) + { + this.selfAssessmentReportService = selfAssessmentReportService; + } + public IActionResult Index() + { + return View(); + } + [HttpGet] + [Route("Download")] + public IActionResult DownloadDigitalCapabilityToExcel() + { + var centreId = User.GetCentreId(); + var dataFile = selfAssessmentReportService.GetDigitalCapabilityExcelExportForCentre(centreId); + var fileName = $"DLS DCSA Report - Centre {centreId} - downloaded {DateTime.Today:yyyy-MM-dd}.xlsx"; + return File( + dataFile, + FileHelper.GetContentTypeFromFileName(fileName), + fileName + ); + } + } +} diff --git a/DigitalLearningSolutions.Web/Models/Enums/CentrePage.cs b/DigitalLearningSolutions.Web/Models/Enums/CentrePage.cs index 5c061e2ef0..73b207d0c5 100644 --- a/DigitalLearningSolutions.Web/Models/Enums/CentrePage.cs +++ b/DigitalLearningSolutions.Web/Models/Enums/CentrePage.cs @@ -5,6 +5,7 @@ public enum CentrePage Dashboard, Configuration, Administrators, - Reports + Reports, + SelfAssessmentReports } } diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index 86136cc835..ecd5ca0e1b 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -229,6 +229,7 @@ private static void RegisterServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void RegisterDataServices(IServiceCollection services) @@ -269,6 +270,7 @@ private static void RegisterDataServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void RegisterHelpers(IServiceCollection services) diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml new file mode 100644 index 0000000000..a4be3711d6 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml @@ -0,0 +1,22 @@ +@using DigitalLearningSolutions.Web.Models.Enums +@{ + ViewData["Title"] = "Self Assessment Reports"; +} +
+
+ +
+ +
+

+ + Beta + - + @ViewData["Title"] +

+

This page will be used to provide acces to comprehensive competency framework self assessment reports for your centre. For now, it provides access to temporary reports for specific self assessments.

+ +
+
diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/Shared/_CentreSideNavMenu.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/Shared/_CentreSideNavMenu.cshtml index 46087c87fb..bc0ab89744 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/Shared/_CentreSideNavMenu.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/Shared/_CentreSideNavMenu.cshtml @@ -5,26 +5,32 @@

Centre

    - - - + - @if (User.HasCentreManagerPermissions()) { + + @if (User.HasCentreManagerPermissions()) + { + - } + is-current-page="Model == CentrePage.Administrators" /> + } - + + +