diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs index 5dae4a9465..a3c3586bb9 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs @@ -272,5 +272,48 @@ public void GetCourseDetailsByIdAtCentreForCategoryId_should_return_course_detai // Then result.Should().BeEquivalentTo(expectedCourseDetails); } + + [Test] + public void GetDelegateCoursesInfo_should_return_delegate_course_info_correctly() + { + // When + var results = courseDataService.GetDelegateCoursesInfo(20).ToList(); + + // Then + var enrollmentDate = new DateTime(2019, 04, 11, 14, 33, 37).AddMilliseconds(140); + var expected = new DelegateCourseInfo( + 27915, + "LinkedIn", + "Cohort Testing", + "Kevin", + "Whittaker (Developer)", + enrollmentDate, + enrollmentDate, + null, + null, + null, + 3, + 0, + 0, + null, + true, + null, + null, + null + ); + results.Should().HaveCount(4); + results[3].Should().BeEquivalentTo(expected); + } + + [Test] + public void GetDelegateCoursesAttemptStats_should_return_delegate_course_info_correctly() + { + // When + var (totalAttempts, attemptsPassed) = courseDataService.GetDelegateCourseAttemptStats(11, 100); + + // Then + totalAttempts.Should().Be(23); + attemptsPassed.Should().Be(11); + } } } diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/CustomPromptsDataServiceTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/CustomPromptsDataServiceTests.cs index cdafa75308..d5681761b8 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/CustomPromptsDataServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/CustomPromptsDataServiceTests.cs @@ -117,11 +117,12 @@ public void GetCourseCustomPrompts_returns_populated_CourseCustomPromptsResult() "Yes\nNo\nNot sure", true, null, - "Yes\nNo\nNot sure" + "Yes\nNo\nNot sure", + courseCategoryId: 2 ); // When - var returnedCourseCustomPromptsResult = customPromptsDataService.GetCourseCustomPrompts(1379, 101, 0); + var returnedCourseCustomPromptsResult = customPromptsDataService.GetCourseCustomPrompts(1379, 101); // Then returnedCourseCustomPromptsResult.Should().BeEquivalentTo(expectedCourseCustomPromptsResult); diff --git a/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs index 35335cd275..7c6aeb0e71 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs @@ -15,6 +15,7 @@ public class CourseServiceTests private const int AdminCategoryId = 0; private ICourseDataService courseDataService = null!; private CourseService courseService = null!; + private ICustomPromptsService customPromptsService = null!; [SetUp] public void Setup() @@ -22,8 +23,8 @@ public void Setup() courseDataService = A.Fake(); A.CallTo(() => courseDataService.GetCourseStatisticsAtCentreForCategoryId(CentreId, AdminCategoryId)) .Returns(GetSampleCourses()); - - courseService = new CourseService(courseDataService); + customPromptsService = A.Fake(); + courseService = new CourseService(courseDataService, customPromptsService); } [Test] @@ -84,5 +85,69 @@ private IEnumerable GetSampleCourses() } }; } + + [Test] + public void GetAllCoursesForDelegate_should_call_correct_data_service_and_helper_methods() + { + // Given + const int delegateId = 20; + const int customisationId = 111; + var attemptStats = (7, 4); + var info = new DelegateCourseInfo + { CustomisationId = customisationId, IsAssessed = true }; + A.CallTo(() => courseDataService.GetDelegateCoursesInfo(delegateId)) + .Returns(new List { info }); + A.CallTo(() => courseDataService.GetDelegateCourseAttemptStats(delegateId, customisationId)) + .Returns(attemptStats); + + // When + var results = courseService.GetAllCoursesForDelegate(delegateId, CentreId).ToList(); + + // Then + A.CallTo(() => courseDataService.GetDelegateCoursesInfo(delegateId)).MustHaveHappened(1, Times.Exactly); + A.CallTo( + () => customPromptsService.GetCustomPromptsWithAnswersForCourse( + info, + customisationId, + CentreId, + 0 + ) + ).MustHaveHappened(1, Times.Exactly); + A.CallTo(() => courseDataService.GetDelegateCourseAttemptStats(delegateId, customisationId)) + .MustHaveHappened(1, Times.Exactly); + results.Should().HaveCount(1); + results[0].DelegateCourseInfo.Should().BeEquivalentTo(info); + results[0].AttemptStats.Should().Be(attemptStats); + } + + [Test] + public void GetAllCoursesForDelegate_should_not_fetch_attempt_stats_if_course_not_assessed() + { + // Given + const int delegateId = 20; + const int customisationId = 111; + var info = new DelegateCourseInfo + { CustomisationId = customisationId, IsAssessed = false }; + A.CallTo(() => courseDataService.GetDelegateCoursesInfo(delegateId)) + .Returns(new List { info }); + + // When + var results = courseService.GetAllCoursesForDelegate(delegateId, CentreId).ToList(); + + // Then + A.CallTo(() => courseDataService.GetDelegateCoursesInfo(delegateId)).MustHaveHappened(1, Times.Exactly); + A.CallTo( + () => customPromptsService.GetCustomPromptsWithAnswersForCourse( + info, + customisationId, + CentreId, + 0 + ) + ).MustHaveHappened(1, Times.Exactly); + A.CallTo(() => courseDataService.GetDelegateCourseAttemptStats(A._, A._)).MustNotHaveHappened(); + results.Should().HaveCount(1); + results[0].DelegateCourseInfo.Should().BeEquivalentTo(info); + results[0].AttemptStats.Should().Be((0, 0)); + } } } diff --git a/DigitalLearningSolutions.Data.Tests/Services/CustomPromptsServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/CustomPromptsServiceTests.cs index fdae4ac76a..d1a6816507 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/CustomPromptsServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/CustomPromptsServiceTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Data.Models.CustomPrompts; using DigitalLearningSolutions.Data.Services; using DigitalLearningSolutions.Data.Tests.TestHelpers; @@ -306,18 +307,48 @@ public void RemoveCustomPromptFromCentre_calls_data_service_with_correct_values( public void GetCustomPromptsForCourse_Returns_Populated_CourseCustomPrompts() { // Given - var expectedPrompt1 = CustomPromptsTestHelper.GetDefaultCustomPrompt(1, "System Access Granted", "Yes\r\nNo"); + var expectedPrompt1 = + CustomPromptsTestHelper.GetDefaultCustomPrompt(1, "System Access Granted", "Yes\r\nNo"); var expectedPrompt2 = CustomPromptsTestHelper.GetDefaultCustomPrompt(2, "Access Permissions"); var customPrompts = new List { expectedPrompt1, expectedPrompt2 }; var expectedCoursePrompts = CustomPromptsTestHelper.GetDefaultCourseCustomPrompts(customPrompts); - A.CallTo(() => customPromptsDataService.GetCourseCustomPrompts(27920, 101, 0)) + A.CallTo(() => customPromptsDataService.GetCourseCustomPrompts(27920, 101)) .Returns(CustomPromptsTestHelper.GetDefaultCourseCustomPromptsResult()); // When - var result = customPromptsService.GetCustomPromptsForCourse(27920, 101, 0); + var result = customPromptsService.GetCustomPromptsForCourse(27920, 101); // Then result.Should().BeEquivalentTo(expectedCoursePrompts); } + + [Test] + public void GetCustomPromptsWithAnswersForCourse_Returns_Populated_List_of_CustomPromptWithAnswer() + { + // Given + const string answer1 = "ans1"; + const string answer2 = "ans2"; + var expected1 = CustomPromptsTestHelper.GetDefaultCustomPromptWithAnswer( + 1, + "System Access Granted", + "Yes\r\nNo", + answer: answer1 + ); + var expected2 = CustomPromptsTestHelper.GetDefaultCustomPromptWithAnswer( + 2, + "Access Permissions", + answer: answer2 + ); + var expected = new List { expected1, expected2 }; + A.CallTo(() => customPromptsDataService.GetCourseCustomPrompts(27920, 101)) + .Returns(CustomPromptsTestHelper.GetDefaultCourseCustomPromptsResult()); + var delegateCourseInfo = new DelegateCourseInfo { Answer1 = answer1, Answer2 = answer2 }; + + // When + var result = customPromptsService.GetCustomPromptsWithAnswersForCourse(delegateCourseInfo, 27920, 101); + + // Then + result.Should().BeEquivalentTo(expected); + } } } diff --git a/DigitalLearningSolutions.Data.Tests/TestHelpers/CustomPromptsTestHelper.cs b/DigitalLearningSolutions.Data.Tests/TestHelpers/CustomPromptsTestHelper.cs index 4799cc85f1..1ac7a869ae 100644 --- a/DigitalLearningSolutions.Data.Tests/TestHelpers/CustomPromptsTestHelper.cs +++ b/DigitalLearningSolutions.Data.Tests/TestHelpers/CustomPromptsTestHelper.cs @@ -1,12 +1,12 @@ namespace DigitalLearningSolutions.Data.Tests.TestHelpers { + using System; using System.Collections.Generic; using DigitalLearningSolutions.Data.Models.CustomPrompts; public static class CustomPromptsTestHelper { - public static CentreCustomPrompts GetDefaultCentreCustomPrompts - ( + public static CentreCustomPrompts GetDefaultCentreCustomPrompts( List customPrompts, int centreId = 29 ) @@ -14,8 +14,7 @@ public static CentreCustomPrompts GetDefaultCentreCustomPrompts return new CentreCustomPrompts(centreId, customPrompts); } - public static CourseCustomPrompts GetDefaultCourseCustomPrompts - ( + public static CourseCustomPrompts GetDefaultCourseCustomPrompts( List customPrompts, int customisationId = 27920, int centreId = 101 @@ -24,8 +23,7 @@ public static CourseCustomPrompts GetDefaultCourseCustomPrompts return new CourseCustomPrompts(customisationId, centreId, customPrompts); } - public static CustomPrompt GetDefaultCustomPrompt - ( + public static CustomPrompt GetDefaultCustomPrompt( int promptNumber, string text = "Custom Prompt", string? options = "", @@ -35,8 +33,7 @@ public static CustomPrompt GetDefaultCustomPrompt return new CustomPrompt(promptNumber, text, options, mandatory); } - public static CentreCustomPromptsWithAnswers GetDefaultCentreCustomPromptsWithAnswers - ( + public static CentreCustomPromptsWithAnswers GetDefaultCentreCustomPromptsWithAnswers( List customPrompts, int centreId = 29 ) @@ -44,8 +41,7 @@ public static CentreCustomPromptsWithAnswers GetDefaultCentreCustomPromptsWithAn return new CentreCustomPromptsWithAnswers(centreId, customPrompts); } - public static CustomPromptWithAnswer GetDefaultCustomPromptWithAnswer - ( + public static CustomPromptWithAnswer GetDefaultCustomPromptWithAnswer( int promptNumber, string text = "Custom Prompt", string? options = "", @@ -56,8 +52,7 @@ public static CustomPromptWithAnswer GetDefaultCustomPromptWithAnswer return new CustomPromptWithAnswer(promptNumber, text, options, mandatory, answer); } - public static CentreCustomPromptsResult GetDefaultCentreCustomPromptsResult - ( + public static CentreCustomPromptsResult GetDefaultCentreCustomPromptsResult( int centreId = 29, string? customField1Prompt = "Group", string? customField1Options = "Clinical\r\nNon-Clinical", @@ -112,7 +107,8 @@ public static CourseCustomPromptsResult GetDefaultCourseCustomPromptsResult( bool customField2Mandatory = false, string? customField3Prompt = null, string? customField3Options = "", - bool customField3Mandatory = false + bool customField3Mandatory = false, + int courseCategoryId = 0 ) { return new CourseCustomPromptsResult @@ -125,7 +121,8 @@ public static CourseCustomPromptsResult GetDefaultCourseCustomPromptsResult( CustomField2Mandatory = customField2Mandatory, CustomField3Prompt = customField3Prompt, CustomField3Options = customField3Options, - CustomField3Mandatory = customField3Mandatory + CustomField3Mandatory = customField3Mandatory, + CourseCategoryId = courseCategoryId }; } } diff --git a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs index 0cca125666..d41936a569 100644 --- a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs @@ -1,4 +1,4 @@ -namespace DigitalLearningSolutions.Data.DataServices +namespace DigitalLearningSolutions.Data.DataServices { using System; using System.Collections.Generic; @@ -18,6 +18,8 @@ public interface ICourseDataService void EnrolOnSelfAssessment(int selfAssessmentId, int candidateId); int GetNumberOfActiveCoursesAtCentreForCategory(int centreId, int categoryId); IEnumerable GetCourseStatisticsAtCentreForCategoryId(int centreId, int categoryId); + IEnumerable GetDelegateCoursesInfo(int delegateId); + (int totalAttempts, int attemptsPassed) GetDelegateCourseAttemptStats(int delegateId, int customisationId); CourseDetails? GetCourseDetails(int customisationId, int centreId, int categoryId); } @@ -198,6 +200,56 @@ FROM dbo.Customisations AS cu ); } + public IEnumerable GetDelegateCoursesInfo(int delegateId) + { + return connection.Query( + @"SELECT + cu.CustomisationID AS CustomisationId, + ap.ApplicationName, + cu.CustomisationName, + au.Forename AS SupervisorForename, + au.Surname AS SupervisorSurname, + pr.FirstSubmittedTime AS Enrolled, + pr.SubmittedTime AS LastUpdated, + pr.CompleteByDate AS CompleteBy, + pr.Completed AS Completed, + pr.Evaluated AS Evaluated, + pr.EnrollmentMethodID AS EnrolmentMethodId, + pr.LoginCount, + pr.Duration AS LearningTime, + pr.DiagnosticScore, + cu.IsAssessed, + pr.Answer1, + pr.Answer2, + pr.Answer3 + FROM Customisations cu + INNER JOIN Applications ap ON ap.ApplicationID = cu.ApplicationID + INNER JOIN Progress pr ON pr.CustomisationID = cu.CustomisationID + LEFT OUTER JOIN AdminUsers au ON au.AdminID = pr.SupervisorAdminId + WHERE pr.CandidateID = @delegateId + AND ap.ArchivedDate IS NULL + AND pr.RemovedDate IS NULL", + new { delegateId } + ); + } + + public (int totalAttempts, int attemptsPassed) GetDelegateCourseAttemptStats( + int delegateId, + int customisationId + ) + { + return connection.QueryFirstOrDefault<(int, int)>( + @"SELECT COUNT(aa.Status) AS TotalAttempts, + COUNT(CASE WHEN aa.Status=1 THEN 1 END) AS AttemptsPassed + FROM AssessAttempts aa + INNER JOIN Progress AS pr ON pr.ProgressID = aa.ProgressID + WHERE pr.CustomisationID = @customisationId + AND pr.CandidateID = @delegateId + AND pr.RemovedDate IS NULL", + new { delegateId, customisationId } + ); + } + public CourseDetails? GetCourseDetails(int customisationId, int centreId, int categoryId) { return connection.Query( diff --git a/DigitalLearningSolutions.Data/DataServices/CustomPromptsDataService.cs b/DigitalLearningSolutions.Data/DataServices/CustomPromptsDataService.cs index 15d57bff1a..41dab834a0 100644 --- a/DigitalLearningSolutions.Data/DataServices/CustomPromptsDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CustomPromptsDataService.cs @@ -22,7 +22,7 @@ public void UpdateCustomPromptForCentre( ); public string GetPromptNameForCentreAndPromptNumber(int centreId, int promptNumber); - public CourseCustomPromptsResult? GetCourseCustomPrompts(int customisationId, int centreId, int categoryId); + public CourseCustomPromptsResult? GetCourseCustomPrompts(int customisationId, int centreId); } public class CustomPromptsDataService : ICustomPromptsDataService @@ -134,7 +134,7 @@ LEFT JOIN CustomPrompts cp ).Single(); } - public CourseCustomPromptsResult? GetCourseCustomPrompts(int customisationId, int centreId, int categoryId) + public CourseCustomPromptsResult? GetCourseCustomPrompts(int customisationId, int centreId) { var result = connection.Query( @"SELECT @@ -146,7 +146,8 @@ LEFT JOIN CustomPrompts cp cu.Q2Mandatory AS CustomField2Mandatory, cp3.CoursePrompt AS CustomField3Prompt, cu.Q3Options AS CustomField3Options, - cu.Q3Mandatory AS CustomField3Mandatory + cu.Q3Mandatory AS CustomField3Mandatory, + ap.CourseCategoryID FROM Customisations AS cu LEFT JOIN CoursePrompts AS cp1 @@ -156,11 +157,10 @@ LEFT JOIN CoursePrompts AS cp2 LEFT JOIN CoursePrompts AS cp3 ON cu.CourseField3PromptID = cp3.CoursePromptID INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = cu.ApplicationID - WHERE (ap.CourseCategoryID = @categoryId OR @categoryId = 0) - AND cu.CentreID = @centreId + WHERE cu.CentreID = @centreId AND ap.ArchivedDate IS NULL AND cu.CustomisationID = @customisationId", - new { customisationId, centreId, categoryId } + new { customisationId, centreId } ).SingleOrDefault(); return result; diff --git a/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseDetails.cs b/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseDetails.cs new file mode 100644 index 0000000000..835838cf5d --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseDetails.cs @@ -0,0 +1,23 @@ +namespace DigitalLearningSolutions.Data.Models.Courses +{ + using System.Collections.Generic; + using DigitalLearningSolutions.Data.Models.CustomPrompts; + + public class DelegateCourseDetails + { + public DelegateCourseDetails( + DelegateCourseInfo delegateCourseInfo, + List customPrompts, + (int totalAttempts, int attemptsPassed) attemptStats + ) + { + DelegateCourseInfo = delegateCourseInfo; + CustomPrompts = customPrompts; + AttemptStats = attemptStats; + } + + public DelegateCourseInfo DelegateCourseInfo { get; set; } + public List CustomPrompts { get; set; } + public (int totalAttempts, int attemptsPassed) AttemptStats { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseInfo.cs b/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseInfo.cs new file mode 100644 index 0000000000..2f2dd7ffdc --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/Courses/DelegateCourseInfo.cs @@ -0,0 +1,69 @@ +namespace DigitalLearningSolutions.Data.Models.Courses +{ + using System; + + public class DelegateCourseInfo + { + public DelegateCourseInfo() { } + + public DelegateCourseInfo( + int customisationId, + string applicationName, + string customisationName, + string? supervisorForename, + string? supervisorSurname, + DateTime enrolled, + DateTime lastUpdated, + DateTime? completeBy, + DateTime? completed, + DateTime? evaluated, + int enrolmentMethodId, + int loginCount, + int learningTime, + int? diagnosticScore, + bool isAssessed, + string? answer1, + string? answer2, + string? answer3 + ) + { + CustomisationId = customisationId; + ApplicationName = applicationName; + CustomisationName = customisationName; + SupervisorForename = supervisorForename; + SupervisorSurname = supervisorSurname; + Enrolled = enrolled; + LastUpdated = lastUpdated; + CompleteBy = completeBy; + Completed = completed; + Evaluated = evaluated; + EnrolmentMethodId = enrolmentMethodId; + LoginCount = loginCount; + LearningTime = learningTime; + DiagnosticScore = diagnosticScore; + IsAssessed = isAssessed; + Answer1 = answer1; + Answer2 = answer2; + Answer3 = answer3; + } + + public int CustomisationId { get; set; } + public string ApplicationName { get; set; } + public string CustomisationName { get; set; } + public string? SupervisorForename { get; set; } + public string? SupervisorSurname { get; set; } + public DateTime Enrolled { get; set; } + public DateTime LastUpdated { get; set; } + public DateTime? CompleteBy { get; set; } + public DateTime? Completed { get; set; } + public DateTime? Evaluated { get; set; } + public int EnrolmentMethodId { get; set; } + public int LoginCount { get; set; } + public int LearningTime { get; set; } + public int? DiagnosticScore { get; set; } + public bool IsAssessed { get; set; } + public string? Answer1 { get; set; } + public string? Answer2 { get; set; } + public string? Answer3 { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/CustomPrompts/CourseCustomPromptsResult.cs b/DigitalLearningSolutions.Data/Models/CustomPrompts/CourseCustomPromptsResult.cs index 9d3bdc9a6f..0be025d3a2 100644 --- a/DigitalLearningSolutions.Data/Models/CustomPrompts/CourseCustomPromptsResult.cs +++ b/DigitalLearningSolutions.Data/Models/CustomPrompts/CourseCustomPromptsResult.cs @@ -1,5 +1,7 @@ namespace DigitalLearningSolutions.Data.Models.CustomPrompts { + using System; + public class CourseCustomPromptsResult { public string? CustomField1Prompt { get; set; } @@ -13,5 +15,7 @@ public class CourseCustomPromptsResult public string? CustomField3Prompt { get; set; } public string? CustomField3Options { get; set; } public bool CustomField3Mandatory { get; set; } + + public int CourseCategoryId { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Services/CourseService.cs b/DigitalLearningSolutions.Data/Services/CourseService.cs index 9871984680..1618de5b22 100644 --- a/DigitalLearningSolutions.Data/Services/CourseService.cs +++ b/DigitalLearningSolutions.Data/Services/CourseService.cs @@ -9,15 +9,18 @@ public interface ICourseService { public IEnumerable GetTopCourseStatistics(int centreId, int categoryId); public IEnumerable GetCentreSpecificCourseStatistics(int centreId, int categoryId); + public IEnumerable GetAllCoursesForDelegate(int delegateId, int centreId); } public class CourseService : ICourseService { private readonly ICourseDataService courseDataService; + private readonly ICustomPromptsService customPromptsService; - public CourseService(ICourseDataService courseDataService) + public CourseService(ICourseDataService courseDataService, ICustomPromptsService customPromptsService) { this.courseDataService = courseDataService; + this.customPromptsService = customPromptsService; } public IEnumerable GetTopCourseStatistics(int centreId, int categoryId) @@ -31,5 +34,23 @@ public IEnumerable GetCentreSpecificCourseStatistics(int centr var allCourses = courseDataService.GetCourseStatisticsAtCentreForCategoryId(centreId, categoryId); return allCourses.Where(c => c.CentreId == centreId); } + + public IEnumerable GetAllCoursesForDelegate(int delegateId, int centreId) + { + return courseDataService.GetDelegateCoursesInfo(delegateId).Select( + info => + { + var customPrompts = customPromptsService.GetCustomPromptsWithAnswersForCourse( + info, + info.CustomisationId, + centreId + ); + var attemptStats = info.IsAssessed + ? courseDataService.GetDelegateCourseAttemptStats(delegateId, info.CustomisationId) + : (0, 0); + return new DelegateCourseDetails(info, customPrompts, attemptStats); + } + ); + } } } diff --git a/DigitalLearningSolutions.Data/Services/CustomPromptsService.cs b/DigitalLearningSolutions.Data/Services/CustomPromptsService.cs index cf251874e7..6da948fe4c 100644 --- a/DigitalLearningSolutions.Data/Services/CustomPromptsService.cs +++ b/DigitalLearningSolutions.Data/Services/CustomPromptsService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Transactions; using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Data.Models.CustomPrompts; using DigitalLearningSolutions.Data.Models.User; using Microsoft.Extensions.Logging; @@ -32,7 +33,19 @@ IEnumerable delegateUsers public void RemoveCustomPromptFromCentre(int centreId, int promptNumber); public string GetPromptNameForCentreAndPromptNumber(int centreId, int promptNumber); - public CourseCustomPrompts? GetCustomPromptsForCourse(int customisationId, int centreId, int categoryId); + + public CourseCustomPrompts? GetCustomPromptsForCourse( + int customisationId, + int centreId, + int categoryId = 0 + ); + + public List GetCustomPromptsWithAnswersForCourse( + DelegateCourseInfo delegateCourseInfo, + int customisationId, + int centreId, + int categoryId = 0 + ); } public class CustomPromptsService : ICustomPromptsService @@ -157,16 +170,50 @@ public string GetPromptNameForCentreAndPromptNumber(int centreId, int promptNumb return customPromptsDataService.GetPromptNameForCentreAndPromptNumber(centreId, promptNumber); } - public CourseCustomPrompts? GetCustomPromptsForCourse(int customisationId, int centreId, int categoryId) + public CourseCustomPrompts? GetCustomPromptsForCourse( + int customisationId, + int centreId, + int categoryId = 0 + ) { - var result = customPromptsDataService.GetCourseCustomPrompts(customisationId, centreId, categoryId); - return result == null - ? null - : new CourseCustomPrompts( - customisationId, - centreId, - PopulateCustomPromptListFromCourseCustomPromptsResult(result) - ); + var result = GetCourseCustomPromptsResultForCourse(customisationId, centreId, categoryId); + if (result == null) + { + return null; + } + + return new CourseCustomPrompts( + customisationId, + centreId, + PopulateCustomPromptListFromCourseCustomPromptsResult(result) + ); + } + + public List GetCustomPromptsWithAnswersForCourse( + DelegateCourseInfo delegateCourseInfo, + int customisationId, + int centreId, + int categoryId = 0 + ) + { + var result = GetCourseCustomPromptsResultForCourse(customisationId, centreId, categoryId); + + return PopulateCustomPromptWithAnswerListFromCourseCustomPromptsResult(result, delegateCourseInfo); + } + + private CourseCustomPromptsResult? GetCourseCustomPromptsResultForCourse( + int customisationId, + int centreId, + int categoryId + ) + { + var result = customPromptsDataService.GetCourseCustomPrompts(customisationId, centreId); + if (result == null || categoryId != 0 && result.CourseCategoryId != categoryId) + { + return null; + } + + return result; } private static List PopulateCustomPromptListFromCentreCustomPromptsResult( @@ -403,5 +450,56 @@ private static List PopulateCustomPromptListFromCourseCustomPrompt return list; } + + private static List PopulateCustomPromptWithAnswerListFromCourseCustomPromptsResult( + CourseCustomPromptsResult? result, + DelegateCourseInfo delegateCourseInfo + ) + { + var list = new List(); + + if (result == null) + { + return list; + } + + var prompt1 = PopulateCustomPromptWithAnswer( + 1, + result.CustomField1Prompt, + result.CustomField1Options, + result.CustomField1Mandatory, + delegateCourseInfo.Answer1 + ); + if (prompt1 != null) + { + list.Add(prompt1); + } + + var prompt2 = PopulateCustomPromptWithAnswer( + 2, + result.CustomField2Prompt, + result.CustomField2Options, + result.CustomField2Mandatory, + delegateCourseInfo.Answer2 + ); + if (prompt2 != null) + { + list.Add(prompt2); + } + + var prompt3 = PopulateCustomPromptWithAnswer( + 3, + result.CustomField3Prompt, + result.CustomField3Options, + result.CustomField3Mandatory, + delegateCourseInfo.Answer3 + ); + if (prompt3 != null) + { + list.Add(prompt3); + } + + return list; + } } } diff --git a/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs b/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs index 5157138ace..b75048940b 100644 --- a/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs +++ b/DigitalLearningSolutions.Web.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs @@ -48,7 +48,7 @@ public void Page_has_no_accessibility_errors(string url, string pageTitle) [InlineData("/TrackingSystem/CourseSetup/10716/AdminFields", "Manage course admin fields")] [InlineData("/TrackingSystem/CourseSetup/10716/Manage", "Level 1 - Microsoft Excel 2010 - Inductions")] [InlineData("/TrackingSystem/Delegates/All", "Delegates")] - [InlineData("/TrackingSystem/Delegates/View/1", "xxxxx xxxxxxxxx")] + [InlineData("/TrackingSystem/Delegates/View/3", "xxxx xxxxxx")] [InlineData("/TrackingSystem/Delegates/Approve", "Approve delegate registrations")] [InlineData("/TrackingSystem/Delegates/BulkUpload", "Bulk upload/update delegates")] [InlineData("/NotificationPreferences", "Notification preferences")] diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/AdminFieldsControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/AdminFieldsControllerTests.cs index a04329b64d..889166b0a4 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/AdminFieldsControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/AdminFieldsControllerTests.cs @@ -28,7 +28,8 @@ public void Setup() public void AdminFields_returns_NotFound_when_no_appropriate_course_found() { // Given - A.CallTo(() => customPromptsService.GetCustomPromptsForCourse(A._, A._, A._)).Returns(null); + A.CallTo(() => customPromptsService.GetCustomPromptsForCourse(A._, A._, A._)) + .Returns(null); // When var result = controller.AdminFields(1); diff --git a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/DelegateCourseInfoViewModelTests.cs b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/DelegateCourseInfoViewModelTests.cs new file mode 100644 index 0000000000..26689e7a69 --- /dev/null +++ b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/DelegateCourseInfoViewModelTests.cs @@ -0,0 +1,185 @@ +namespace DigitalLearningSolutions.Web.Tests.ViewModels.TrackingSystem.Delegates.AllDelegates +{ + using System; + using System.Collections.Generic; + using DigitalLearningSolutions.Data.Models.Courses; + using DigitalLearningSolutions.Data.Models.CustomPrompts; + using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates; + using FluentAssertions; + using NUnit.Framework; + + public class DelegateCourseInfoViewModelTests + { + private readonly (int totalAttempts, int attemptsPassed) attemptStats = (0, 0); + private readonly List customPromptsWithAnswers = new List(); + + private DelegateCourseDetails GetDetailsFromInfo(DelegateCourseInfo info) + { + return new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + } + + [Test] + public void DelegateCourseInfoViewModel_sets_date_strings_correctly() + { + // Given + var enrolledDate = new DateTime(2021, 05, 1); + var updatedDate = new DateTime(2021, 05, 2); + var completeByDate = new DateTime(2021, 05, 3); + var completedDate = new DateTime(2021, 05, 4); + var evaluatedDate = new DateTime(2021, 05, 5); + var info = new DelegateCourseInfo + { + Enrolled = enrolledDate, + LastUpdated = updatedDate, + CompleteBy = completeByDate, + Completed = completedDate, + Evaluated = evaluatedDate + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.Enrolled.Should().Be("01/05/2021"); + model.LastUpdated.Should().Be("02/05/2021"); + model.CompleteBy.Should().Be("03/05/2021"); + model.Completed.Should().Be("04/05/2021"); + model.Evaluated.Should().Be("05/05/2021"); + } + + [TestCase(1, "Self enrolled")] + [TestCase(2, "Administrator")] + [TestCase(3, "Group")] + [TestCase(4, "System")] + public void DelegateCourseInfoViewModel_sets_enrollment_method_correctly( + int enrollmentMethodId, + string enrollmentMethod + ) + { + // Given + var info = new DelegateCourseInfo { EnrolmentMethodId = enrollmentMethodId }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.EnrolmentMethod.Should().Be(enrollmentMethod); + } + + [TestCase(0, 0, null)] + [TestCase(0, 1, null)] + [TestCase(2, 1, "50%")] + [TestCase(3, 1, "33%")] + [TestCase(4, 1, "25%")] + [TestCase(100, 1, "1%")] + public void DelegateCourseInfoViewModel_sets_pass_rate_correctly( + int totalAttempts, + int attemptsPassed, + string? passRate + ) + { + // Given + var details = new DelegateCourseDetails( + new DelegateCourseInfo(), + customPromptsWithAnswers, + (totalAttempts, attemptsPassed) + ); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.PassRate.Should().Be(passRate); + } + + [Test] + public void DelegateCourseInfoViewModel_without_customisation_name_sets_course_name_correctly() + { + // Given + var info = new DelegateCourseInfo + { + ApplicationName = "my application", CustomisationName = "" + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.CourseName.Should().Be("my application"); + } + + [Test] + public void DelegateCourseInfoViewModel_with_customisation_name_sets_course_name_correctly() + { + // Given + var info = new DelegateCourseInfo + { + ApplicationName = "my application", + CustomisationName = "my customisation" + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.CourseName.Should().Be("my application - my customisation"); + } + + [Test] + public void DelegateCourseInfoViewModel_without_supervisor_surname_sets_supervisor_correctly() + { + // Given + var info = new DelegateCourseInfo + { + SupervisorSurname = null + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.Supervisor.Should().BeNull(); + } + + [Test] + public void DelegateCourseInfoViewModel_without_supervisor_forename_sets_supervisor_correctly() + { + // Given + var info = new DelegateCourseInfo + { + SupervisorForename = "", + SupervisorSurname = "surname" + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.Supervisor.Should().Be("surname"); + } + + [Test] + public void DelegateCourseInfoViewModel_with_supervisor_forename_sets_supervisor_correctly() + { + // Given + var info = new DelegateCourseInfo + { + SupervisorForename = "firstname", + SupervisorSurname = "surname" + }; + var details = new DelegateCourseDetails(info, customPromptsWithAnswers, attemptStats); + + // When + var model = new DelegateCourseInfoViewModel(details); + + // Then + model.Supervisor.Should().Be("firstname surname"); + } + } +} diff --git a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/ViewDelegateViewModelTests.cs b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/ViewDelegateViewModelTests.cs index 3889a9ab71..fcae50a8f7 100644 --- a/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/ViewDelegateViewModelTests.cs +++ b/DigitalLearningSolutions.Web.Tests/ViewModels/TrackingSystem/Delegates/AllDelegates/ViewDelegateViewModelTests.cs @@ -11,6 +11,9 @@ public class ViewDelegateViewModelTests { private readonly List customFields = new List(); + private readonly List + delegateCourses = new List(); + [Test] public void ViewDelegateViewModel_sets_active_tag_name_correctly() { @@ -19,8 +22,14 @@ public void ViewDelegateViewModel_sets_active_tag_name_correctly() var inactiveUser = new DelegateUserCard { Active = false }; // When - var activeModel = new ViewDelegateViewModel(new DelegateInfoViewModel(activeUser, customFields)); - var inactiveModel = new ViewDelegateViewModel(new DelegateInfoViewModel(inactiveUser, customFields)); + var activeModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(activeUser, customFields), + delegateCourses + ); + var inactiveModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(inactiveUser, customFields), + delegateCourses + ); // Then activeModel.ActiveTagName.Should().Be("Active"); @@ -35,8 +44,14 @@ public void ViewDelegateViewModel_sets_password_tag_name_correctly() var pwNotSetUser = new DelegateUserCard { Password = null }; // When - var pwSetModel = new ViewDelegateViewModel(new DelegateInfoViewModel(pwSetUser, customFields)); - var pwNotSetModel = new ViewDelegateViewModel(new DelegateInfoViewModel(pwNotSetUser, customFields)); + var pwSetModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(pwSetUser, customFields), + delegateCourses + ); + var pwNotSetModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(pwNotSetUser, customFields), + delegateCourses + ); // Then pwSetModel.PasswordTagName.Should().Be("Password set"); @@ -52,11 +67,16 @@ public void ViewDelegateViewModel_sets_regstatus_tag_name_correctly() var centreRegUser = new DelegateUserCard { SelfReg = false }; // When - var selfRegModel = new ViewDelegateViewModel(new DelegateInfoViewModel(selfRegUser, customFields)); - var selfRegExternalModel = - new ViewDelegateViewModel(new DelegateInfoViewModel(selfRegExternalUser, customFields)); + var selfRegModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(selfRegUser, customFields), + delegateCourses + ); + var selfRegExternalModel = new ViewDelegateViewModel( + new DelegateInfoViewModel(selfRegExternalUser, customFields), + delegateCourses + ); var centreRegModel = - new ViewDelegateViewModel(new DelegateInfoViewModel(centreRegUser, customFields)); + new ViewDelegateViewModel(new DelegateInfoViewModel(centreRegUser, customFields), delegateCourses); // Then selfRegModel.RegStatusTagName.Should().Be("Self registered"); diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs index 2c09d67c58..aca8b6ea61 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs @@ -1,6 +1,8 @@ namespace DigitalLearningSolutions.Web.Controllers.TrackingSystem.Delegates { + using System.Linq; using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Services; using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates; using Microsoft.AspNetCore.Authorization; @@ -12,29 +14,38 @@ [Route("TrackingSystem/Delegates/View/{delegateId:int}")] public class ViewDelegateController : Controller { + private readonly ICourseService courseService; private readonly CustomPromptHelper customPromptHelper; private readonly IUserDataService userDataService; - public ViewDelegateController(IUserDataService userDataService, CustomPromptHelper customPromptHelper) + public ViewDelegateController( + IUserDataService userDataService, + CustomPromptHelper customPromptHelper, + ICourseService courseService + ) { this.userDataService = userDataService; this.customPromptHelper = customPromptHelper; + this.courseService = courseService; } public IActionResult Index(int delegateId) { var centreId = User.GetCentreId(); - var delegateUser = userDataService.GetDelegateUserCardById(delegateId); + var delegateUser = userDataService.GetDelegateUserCardById(delegateId); if (delegateUser == null || delegateUser.CentreId != centreId) { return new NotFoundResult(); } var customFields = customPromptHelper.GetCustomFieldViewModelsForCentre(centreId, delegateUser); - var delegateInfo = new DelegateInfoViewModel(delegateUser, customFields); - var model = new ViewDelegateViewModel(delegateInfo); + var delegateInfoViewModel = new DelegateInfoViewModel(delegateUser, customFields); + + var courseInfoViewModels = courseService.GetAllCoursesForDelegate(delegateId, centreId) + .Select(x => new DelegateCourseInfoViewModel(x)); + var model = new ViewDelegateViewModel(delegateInfoViewModel, courseInfoViewModels); return View(model); } } diff --git a/DigitalLearningSolutions.Web/Styles/trackingSystem/viewDelegate.scss b/DigitalLearningSolutions.Web/Styles/trackingSystem/viewDelegate.scss index d926f51ff8..a008ca2f76 100644 --- a/DigitalLearningSolutions.Web/Styles/trackingSystem/viewDelegate.scss +++ b/DigitalLearningSolutions.Web/Styles/trackingSystem/viewDelegate.scss @@ -23,3 +23,13 @@ width: 100%; } } + +/* Give some of the 'value' space to 'actions' when displayed side-by-side */ +@include mq($from: tablet) { + .nhsuk-summary-list__value { + width: 40%; + } + .nhsuk-summary-list__actions { + width: 30%; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateCourseInfoViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateCourseInfoViewModel.cs new file mode 100644 index 0000000000..220a2dc632 --- /dev/null +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/DelegateCourseInfoViewModel.cs @@ -0,0 +1,83 @@ +namespace DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates +{ + using System; + using System.Collections.Generic; + using DigitalLearningSolutions.Data.Models.Courses; + using DigitalLearningSolutions.Data.Models.CustomPrompts; + + public class DelegateCourseInfoViewModel + { + private const string DateFormat = "dd/MM/yyyy"; + + public DelegateCourseInfoViewModel(DelegateCourseDetails details) + { + var info = details.DelegateCourseInfo; + CustomisationId = info.CustomisationId; + ApplicationName = info.ApplicationName; + CustomisationName = info.CustomisationName; + SupervisorForename = info.SupervisorForename; + SupervisorSurname = info.SupervisorSurname; + Enrolled = info.Enrolled.ToString(DateFormat); + LastUpdated = info.LastUpdated.ToString(DateFormat); + CompleteBy = info.CompleteBy?.ToString(DateFormat); + Completed = info.Completed?.ToString(DateFormat); + Evaluated = info.Evaluated?.ToString(DateFormat); + EnrolmentMethod = info.EnrolmentMethodId switch + { + 1 => "Self enrolled", + 2 => "Administrator", + 3 => "Group", + 4 => "System", + _ => "" + }; + LoginCount = info.LoginCount; + LearningTime = info.LearningTime + " mins"; + DiagnosticScore = info.DiagnosticScore; + IsAssessed = info.IsAssessed; + + CourseCustomPromptsWithAnswers = details.CustomPrompts; + TotalAttempts = details.AttemptStats.totalAttempts; + AttemptsPassed = details.AttemptStats.attemptsPassed; + } + + public int CustomisationId { get; set; } + public string ApplicationName { get; set; } + public string CustomisationName { get; set; } + public string? SupervisorForename { get; set; } + public string? SupervisorSurname { get; set; } + public string Enrolled { get; set; } + public string LastUpdated { get; set; } + public string? CompleteBy { get; set; } + public string? Completed { get; set; } + public string? Evaluated { get; set; } + public string EnrolmentMethod { get; set; } + public int LoginCount { get; set; } + public string LearningTime { get; set; } + public int? DiagnosticScore { get; set; } + public bool IsAssessed { get; set; } + + public List CourseCustomPromptsWithAnswers { get; set; } + public int TotalAttempts { get; set; } + public int AttemptsPassed { get; set; } + + public string? Supervisor + { + get + { + // SupervisorSurname is not nullable in db; will only be null if no supervisor + if (SupervisorSurname == null) + { + return null; + } + + return (string.IsNullOrEmpty(SupervisorForename) ? "" : $"{SupervisorForename} ") + SupervisorSurname; + } + } + + public string CourseName => + ApplicationName + (string.IsNullOrEmpty(CustomisationName) ? "" : $" - {CustomisationName}"); + + public string? PassRate => + TotalAttempts != 0 ? Math.Round(100 * AttemptsPassed / (double)TotalAttempts) + "%" : null; + } +} diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/ViewDelegateViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/ViewDelegateViewModel.cs index 815c6b5067..6c312d8f22 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/ViewDelegateViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Delegates/ViewDelegateViewModel.cs @@ -1,14 +1,23 @@ namespace DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates { + using System.Collections.Generic; + using System.Linq; + public class ViewDelegateViewModel { - public ViewDelegateViewModel(DelegateInfoViewModel delegateInfoViewModel) + public ViewDelegateViewModel( + DelegateInfoViewModel delegateInfoViewModel, + IEnumerable courseInfoViewModels + ) { DelegateInfo = delegateInfoViewModel; + DelegateCourses = courseInfoViewModels.ToList(); } public DelegateInfoViewModel DelegateInfo { get; set; } + public List DelegateCourses { get; set; } + public string RegStatusTagName => DelegateInfo.IsSelfReg ? "Self registered" + (DelegateInfo.IsExternalReg ? " (External)" : "") diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/Index.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/Index.cshtml index 2b95a56776..669649a39d 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/Index.cshtml @@ -35,7 +35,7 @@ } -
+
@(Model.ActiveTagName) @(Model.RegStatusTagName) @@ -120,4 +120,17 @@ }
+ +
+

Courses

+ @if (!Model.DelegateCourses.Any()) { +

+ Not currently registered for any courses. +

+ } else { + @foreach (var delegateCourseInfoViewModel in Model.DelegateCourses) { + + } + } +
diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/_DelegateCourseInfoCard.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/_DelegateCourseInfoCard.cshtml new file mode 100644 index 0000000000..80f8b37d19 --- /dev/null +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/ViewDelegate/_DelegateCourseInfoCard.cshtml @@ -0,0 +1,143 @@ +@using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates +@model DelegateCourseInfoViewModel + +
+
+ + + @Model.CourseName + + + +
+
+
+
+ Enrolled +
+ +
+
+ +
+
+ Supervisor +
+ +
+ Edit supervisor +
+
+ +
+
+ Complete by +
+ +
+ Edit complete by date +
+
+ +
+
+ Last updated +
+ +
+
+ +
+
+ Completed +
+ +
+ @if (Model.Completed == null) { + Set completed date + } +
+
+ +
+
+ Evaluated +
+ +
+
+ +
+
+ Enrolment method +
+ +
+
+ +
+
+ Logins +
+ +
+
+ +
+
+ Learning time +
+ +
+
+ +
+
+ Diagnostic score +
+ +
+
+ + @if (Model.IsAssessed) { +
+
+ Assessments passed +
+ +
+
+ +
+
+ Assessment attempts +
+ +
+
+ +
+
+ Pass rate +
+ +
+
+ } + + @foreach (var courseCustomPrompt in Model.CourseCustomPromptsWithAnswers) { +
+
+ @courseCustomPrompt.CustomPromptText +
+ +
+
+ } +
+ + See learning log + Remove +
+
+