diff --git a/DigitalLearningSolutions.Data.Migrations/202410071401_UpdateCandidateAssessmentSupervisorsTabl.cs b/DigitalLearningSolutions.Data.Migrations/202410071401_UpdateCandidateAssessmentSupervisorsTabl.cs new file mode 100644 index 0000000000..de95a04841 --- /dev/null +++ b/DigitalLearningSolutions.Data.Migrations/202410071401_UpdateCandidateAssessmentSupervisorsTabl.cs @@ -0,0 +1,21 @@ + +namespace DigitalLearningSolutions.Data.Migrations +{ + using FluentMigrator; + + [Migration(202410071401)] + public class UpdateCandidateAssessmentSupervisorsTabl : ForwardOnlyMigration + { + public override void Up() + { + Execute.Sql($@"UPDATE cas + SET SelfAssessmentSupervisorRoleID = (SELECT ID FROM SelfAssessmentSupervisorRoles + WHERE SelfAssessmentID = ssr.SelfAssessmentID and AllowDelegateNomination = 1) + FROM CandidateAssessmentSupervisors cas INNER JOIN + SelfAssessmentSupervisorRoles ssr ON cas.SelfAssessmentSupervisorRoleID = ssr.ID + AND cas.Removed IS NULL AND ssr.AllowDelegateNomination = 0 INNER JOIN + SupervisorDelegates sd ON cas.SupervisorDelegateId = sd.ID INNER JOIN + AdminAccounts aa ON sd.SupervisorAdminID = aa.ID WHERE aa.IsSupervisor = 0 AND aa.IsNominatedSupervisor = 1"); + } + } +} diff --git a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx index 677aa02999..c2c733076c 100644 --- a/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx +++ b/DigitalLearningSolutions.Data.Migrations/Properties/Resources.resx @@ -469,4 +469,13 @@ ..\Scripts\TD_4950_Alter_GetAssessmentResultsByDelegate_UP.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + ..\Resources\TD-4950-dboGetOtherCentresForSelfAssessmentCreateOrAlter.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + ..\Scripts\TD-4878-Alter_GetActivitiesForDelegateEnrolment_Down.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + + + ..\Scripts\TD-4878-Alter_GetActivitiesForDelegateEnrolment_Up.sql;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16 + \ No newline at end of file diff --git a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs index 9e3addf30f..fee4f1b696 100644 --- a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs @@ -139,7 +139,7 @@ int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int sup public IEnumerable GetDelegateAssessmentStatisticsAtCentre(string searchString, int centreId, string categoryName, string isActive, int? categoryId); bool IsSelfEnrollmentAllowed(int customisationId); - Customisation? GetCourse(int customisationId); + Customisation? GetCourse(int? customisationId); } public class CourseDataService : ICourseDataService @@ -537,16 +537,16 @@ LEFT OUTER JOIN UserCentreDetails AS UCD ON if (candidateAssessmentId > 1 && supervisorDelegateId == 0) { connection.Execute( - @"UPDATE CandidateAssessments SET RemovedDate = NULL, EnrolmentMethodId = @enrolmentMethodId, CompleteByDate = @completeByDateDynamic + @"UPDATE CandidateAssessments SET RemovedDate = NULL, EnrolmentMethodId = @enrolmentMethodId, CompleteByDate = @completeByDateDynamic, EnrolledByAdminId = @enrolledByAdminId WHERE ID = @candidateAssessmentId", - new { candidateAssessmentId, enrolmentMethodId, completeByDateDynamic } + new { candidateAssessmentId, enrolmentMethodId, completeByDateDynamic, enrolledByAdminId } ); } if (candidateAssessmentId > 1 && supervisorDelegateId != 0) { string sqlQuery = $@" BEGIN TRANSACTION - UPDATE CandidateAssessments SET RemovedDate = NULL, EnrolmentMethodId = @enrolmentMethodId, CompleteByDate = @completeByDateDynamic + UPDATE CandidateAssessments SET RemovedDate = NULL, EnrolmentMethodId = @enrolmentMethodId, CompleteByDate = @completeByDateDynamic, EnrolledByAdminId = @enrolledByAdminId WHERE ID = @candidateAssessmentId UPDATE CandidateAssessmentSupervisors SET Removed = NULL @@ -556,7 +556,7 @@ BEGIN TRANSACTION COMMIT TRANSACTION"; connection.Execute(sqlQuery - , new { candidateAssessmentId, selfAssessmentSupervisorRoleId, enrolmentMethodId, completeByDateDynamic, supervisorDelegateId }); + , new { candidateAssessmentId, selfAssessmentSupervisorRoleId, enrolmentMethodId, completeByDateDynamic, supervisorDelegateId, enrolledByAdminId }); } if (supervisorId > 0) @@ -2024,7 +2024,7 @@ public bool IsSelfEnrollmentAllowed(int customisationId) return selfRegister > 0; } - public Customisation? GetCourse(int customisationId) + public Customisation? GetCourse(int? customisationId) { return connection.Query( @"SELECT CustomisationID diff --git a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs index 06edcc938a..57405d8bc7 100644 --- a/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/FrameworkDataService.cs @@ -130,7 +130,7 @@ bool zeroBased int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId); - int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify); + int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify, int? centreID); void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); void UpdateFrameworkCustomFlag(int frameworkId, int id, string flagName, string flagGroup, string flagTagClass); @@ -299,7 +299,7 @@ FROM CourseTopics private const string FrameworkTables = @"Frameworks AS FW LEFT OUTER JOIN - FrameworkCollaborators AS fwc ON fwc.FrameworkID = FW.ID AND fwc.AdminID = @adminId + FrameworkCollaborators AS fwc ON fwc.FrameworkID = FW.ID AND fwc.AdminID = @adminId AND COALESCE(IsDeleted, 0) = 0 LEFT OUTER JOIN FrameworkReviews AS fwr ON fwc.ID = fwr.FrameworkCollaboratorID AND fwr.Archived IS NULL AND fwr.ReviewComplete IS NULL"; private const string AssessmentQuestionFields = @@ -751,7 +751,7 @@ FROM FrameworkCollaborators fc ); } - public int AddCollaboratorToFramework(int frameworkId, string? userEmail, bool canModify) + public int AddCollaboratorToFramework(int frameworkId, string? userEmail, bool canModify, int? centreID) { if (userEmail is null || userEmail.Length == 0) { @@ -774,8 +774,8 @@ FROM FrameworkCollaborators } var adminId = (int?)connection.ExecuteScalar( - @"SELECT AdminID FROM AdminUsers WHERE Email = @userEmail AND Active = 1", - new { userEmail } + @"SELECT AdminID FROM AdminUsers WHERE Email = @userEmail AND Active = 1 AND CentreID = @centreID", + new { userEmail, centreID } ); if (adminId is null) { @@ -1823,11 +1823,11 @@ FROM Competencies AS C INNER JOIN public int GetAdminUserRoleForFrameworkId(int adminId, int frameworkId) { - return (int)connection.ExecuteScalar( - @"SELECT CASE WHEN FW.OwnerAdminID = @adminId THEN 3 WHEN fwc.CanModify = 1 THEN 2 WHEN fwc.CanModify = 0 THEN 1 ELSE 0 END AS UserRole - FROM Frameworks AS FW LEFT OUTER JOIN - FrameworkCollaborators AS fwc ON fwc.FrameworkID = FW.ID AND fwc.AdminID = @adminId - WHERE FW.ID = @frameworkId", + return connection.QuerySingle( + @"SELECT CASE WHEN FW.OwnerAdminID = @adminId THEN 3 WHEN COALESCE (fwc.CanModify, 0) = 1 THEN 2 WHEN COALESCE (fwc.CanModify, 0) = 0 THEN 1 ELSE 0 END AS UserRole + FROM Frameworks AS FW LEFT OUTER JOIN + FrameworkCollaborators AS fwc ON fwc.FrameworkID = FW.ID AND fwc.AdminID = @adminId AND fwc.IsDeleted = 0 + WHERE (FW.ID = @frameworkId)", new { adminId, frameworkId } ); } diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs index 845a00b07c..76a1a199bd 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/CompetencyDataService.cs @@ -615,6 +615,41 @@ FROM SelfAssessmentResults ); } + public IEnumerable GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ) + { + return connection.Query( + @"SELECT + s.ID, + s.SelfAssessmentID, + s.CompetencyID, + s.AssessmentQuestionID, + s.Result, + s.DateTime, + s.SupportingComments, + s.DelegateUserId + FROM SelfAssessmentResults s inner join + SelfAssessmentResultSupervisorVerifications sv ON s.ID = sv.SelfAssessmentResultId AND sv.Superceded = 0 + WHERE s.CompetencyID = @competencyId + AND s.SelfAssessmentID = @selfAssessmentId + AND s.DelegateUserID = @delegateUserId", + new { selfAssessmentId, delegateUserId, competencyId } + ); + } + + public void RemoveReviewCandidateAssessmentOptionalCompetencies(int id) + { + + connection.Execute(@"UPDATE SelfAssessmentResults SET Result = NULL WHERE ID = @id", new { id}); + + connection.Execute( + @"delete from SelfAssessmentResultSupervisorVerifications WHERE SelfAssessmentResultId = @id", new { id }); + + } + private static string PrintResult( int competencyId, int selfAssessmentId, diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs index 0528c7f907..d4b87c6f78 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentDataService.cs @@ -173,6 +173,12 @@ int GetSelfAssessmentActivityDelegatesExportCount(string searchString, string so bool IsCentreSelfAssessment(int selfAssessmentId, int centreId); bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId); int GetSelfAssessmentCategoryId(int selfAssessmentId); + void RemoveReviewCandidateAssessmentOptionalCompetencies(int id); + public IEnumerable GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ); } public partial class SelfAssessmentDataService : ISelfAssessmentDataService { diff --git a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs index 7c3a5d118f..2a77222937 100644 --- a/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SelfAssessmentDataService/SelfAssessmentSupervisorDataService.cs @@ -78,7 +78,7 @@ int delegateUserId return connection.Query( @$"{SelectSelfAssessmentSupervisorQuery} WHERE (sd.Removed IS NULL) AND (cas.Removed IS NULL) AND (ca.DelegateUserID = @delegateUserId) - AND (ca.SelfAssessmentID = @selfAssessmentId) + AND (ca.SelfAssessmentID = @selfAssessmentId) AND (au.Supervisor = 1 or au.NominatedSupervisor = 1) AND (au.CategoryID = 0 OR au.CategoryID IN (select CategoryID from SelfAssessments where ID = @selfAssessmentId)) ORDER BY SupervisorName", new { selfAssessmentId, delegateUserId } @@ -95,7 +95,7 @@ int delegateUserId WHERE (sd.Removed IS NULL) AND (cas.Removed IS NULL) AND (sd.DelegateUserID = @delegateUserId) AND (ca.SelfAssessmentID = @selfAssessmentId) AND (sd.SupervisorAdminID IS NOT NULL) AND (coalesce(sasr.ResultsReview, 1) = 1) - AND au.Active = 1 + AND au.Active = 1 AND (au.Supervisor = 1 or au.NominatedSupervisor = 1) AND (au.CategoryID = 0 OR au.CategoryID IN (select CategoryID from SelfAssessments where ID = @selfAssessmentId)) ORDER BY SupervisorName", new { selfAssessmentId, delegateUserId } @@ -153,7 +153,7 @@ int delegateUserId WHERE (sd.Removed IS NULL) AND (cas.Removed IS NULL) AND (sd.DelegateUserID = @delegateUserId) AND (ca.SelfAssessmentID = @selfAssessmentId) AND (sd.SupervisorAdminID IS NOT NULL) AND (coalesce(sasr.SelfAssessmentReview, 1) = 1) AND (cas.ID NOT IN (SELECT CandidateAssessmentSupervisorID FROM CandidateAssessmentSupervisorVerifications WHERE Verified IS NULL)) - AND au.Active = 1 + AND au.Active = 1 AND (au.Supervisor = 1 or au.NominatedSupervisor = 1) AND (au.CategoryID = 0 OR au.CategoryID IN (select CategoryID from SelfAssessments where ID = @selfAssessmentId)) ORDER BY SupervisorName", new { selfAssessmentId, delegateUserId } diff --git a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs index 5c52d18b1e..0d8fab0715 100644 --- a/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/SupervisorDataService.cs @@ -126,13 +126,35 @@ public SupervisorDataService(IDbConnection connection, ILogger( - @" SELECT (SELECT COUNT(sd.ID) AS StaffCount - FROM SupervisorDelegates sd - LEFT OUTER JOIN users u - ON u.id = sd.DelegateUserID - AND u.Active = 1 - WHERE (sd.SupervisorAdminID = @adminId) - AND (sd.Removed IS NULL)) AS StaffCount, + @"SELECT (SELECT COUNT(sd.ID) AS StaffCount + FROM CustomPrompts AS cp6 + RIGHT OUTER JOIN CustomPrompts AS cp5 + RIGHT OUTER JOIN DelegateAccounts AS da + RIGHT OUTER JOIN SupervisorDelegates AS sd + INNER JOIN AdminUsers AS au + ON sd.SupervisorAdminID = au.AdminID + INNER JOIN Centres AS ct + ON au.CentreID = ct.CentreID + ON da.CentreID = ct.CentreID + AND da.UserID = sd.DelegateUserID + LEFT OUTER JOIN Users AS u + LEFT OUTER JOIN JobGroups AS jg + ON u.JobGroupID = jg.JobGroupID + ON da.UserID = u.ID + LEFT OUTER JOIN CustomPrompts AS cp1 + ON ct.CustomField1PromptID = cp1.CustomPromptID + LEFT OUTER JOIN CustomPrompts AS cp2 + ON ct.CustomField2PromptID = cp2.CustomPromptID + LEFT OUTER JOIN CustomPrompts AS cp3 + ON ct.CustomField3PromptID = cp3.CustomPromptID + LEFT OUTER JOIN CustomPrompts AS cp4 + ON ct.CustomField4PromptID = cp4.CustomPromptID + ON cp5.CustomPromptID = ct.CustomField5PromptID + ON cp6.CustomPromptID = ct.CustomField6PromptID + LEFT OUTER JOIN AdminAccounts AS au2 + ON da.UserID = au2.UserID AND da.CentreID = au2.CentreID + WHERE (sd.SupervisorAdminID = @adminId) AND (sd.Removed IS NULL) AND + (u.ID = da.UserID OR sd.DelegateUserID IS NULL)) AS StaffCount, (SELECT COUNT(ID) AS StaffCount FROM SupervisorDelegates AS SupervisorDelegates_1 WHERE (SupervisorAdminID = @adminId) diff --git a/DigitalLearningSolutions.Data/Helpers/FilteringHelper.cs b/DigitalLearningSolutions.Data/Helpers/FilteringHelper.cs index 4ea7ae9ebe..cdd42ca825 100644 --- a/DigitalLearningSolutions.Data/Helpers/FilteringHelper.cs +++ b/DigitalLearningSolutions.Data/Helpers/FilteringHelper.cs @@ -36,8 +36,8 @@ public static string BuildFilterValueString(string group, string propertyName, s { return existingFilterString; } - - return existingFilterString + FilterSeparator + newFilterToAdd; + var filterString = existingFilterString + FilterSeparator + newFilterToAdd; + return RemoveDuplicateFilters(filterString, newFilterToAdd); } public static string? GetFilterString( @@ -46,7 +46,8 @@ public static string BuildFilterValueString(string group, string propertyName, s bool clearFilters, HttpRequest request, string cookieName, - string? defaultFilterValue = null + string? defaultFilterValue = null, + IEnumerable? availableFilters = null ) { var cookieHasBeenSet = request.Cookies.ContainsKey(cookieName); @@ -59,18 +60,19 @@ public static string BuildFilterValueString(string group, string propertyName, s if (cookieHasBeenSet && noFiltersInQueryParams) { - return request.Cookies[cookieName] == EmptyFiltersCookieValue ? null : request.Cookies[cookieName]; + return request.Cookies[cookieName] == EmptyFiltersCookieValue ? null : GetValidFilters(request.Cookies[cookieName], availableFilters); } - return noFiltersInQueryParams - ? defaultFilterValue - : AddNewFilterToFilterString(existingFilterString, newFilterToAdd); - } + var filterString = noFiltersInQueryParams + ? defaultFilterValue + : AddNewFilterToFilterString(existingFilterString, newFilterToAdd); + return GetValidFilters(filterString, availableFilters); + } public static string? GetCategoryAndTopicFilterString( - string? categoryFilterString, - string? topicFilterString - ) + string? categoryFilterString, + string? topicFilterString + ) { if (categoryFilterString == null && topicFilterString == null) { @@ -169,6 +171,57 @@ public static string GetFilterValueForRegistrationPrompt(int promptNumber, strin return BuildFilterValueString(group, group.Split('(')[0], propertyValue); } + public static string? GetValidFilters(string? existingFilterString, IEnumerable? availableFilters) + { + if (string.IsNullOrEmpty(existingFilterString) || availableFilters == null) + { + return null; + } + var existingFilters = existingFilterString.Split(FilterSeparator); + var validFilterValues = availableFilters + .SelectMany(filter => filter.FilterOptions) + .Select(option => option.FilterValue) + .ToHashSet(); + + var filteredResults = existingFilters + .Where(entry => IsFilterInvalid(entry, validFilterValues)) + .ToList(); + var newFilterString = string.Join(FilterSeparator, filteredResults); + + return string.IsNullOrEmpty(newFilterString) ? null : newFilterString; + } + + private static bool IsFilterInvalid(string filterEntry, HashSet validFilterValues) + { + if (validFilterValues.Contains(filterEntry)) return true; + return false; + } + public static string RemoveDuplicateFilters(string? existingFilterString, string newFilterToAdd) + { + if (string.IsNullOrEmpty(existingFilterString)) + { + return existingFilterString ?? string.Empty; + } + var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); + if (!string.IsNullOrEmpty(newFilterToAdd)) + { + var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[0]; + var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); + if (dupfilters.Count() > 1) + { + foreach (var filter in selectedFilters) + { + if (filter.Contains(filterHeader)) + { + selectedFilters.Remove(filter); + existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); + break; + } + } + } + } + return existingFilterString; + } private static IEnumerable GetFilterOptionsForPromptWithOptions(Prompt prompt) { var group = GetFilterGroupForPrompt(prompt); diff --git a/DigitalLearningSolutions.Data/Models/MultiPageFormData/AddNewCentreCourse/AddNewCentreCourseTempData.cs b/DigitalLearningSolutions.Data/Models/MultiPageFormData/AddNewCentreCourse/AddNewCentreCourseTempData.cs index 717d1d0f3e..4fd6350d2b 100644 --- a/DigitalLearningSolutions.Data/Models/MultiPageFormData/AddNewCentreCourse/AddNewCentreCourseTempData.cs +++ b/DigitalLearningSolutions.Data/Models/MultiPageFormData/AddNewCentreCourse/AddNewCentreCourseTempData.cs @@ -17,7 +17,7 @@ public AddNewCentreCourseTempData() public CourseOptionsTempData? CourseOptionsData { get; set; } public CourseContentTempData? CourseContentData { get; set; } public List? SectionContentData { get; set; } - + public bool EditCourseContent { get; set; } public void SetApplicationAndResetModels(ApplicationDetails application) { if (Application == application) diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/LearningPortal/CurrentTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/LearningPortal/CurrentTests.cs index 46c6f0685e..245c727128 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/LearningPortal/CurrentTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/LearningPortal/CurrentTests.cs @@ -44,7 +44,7 @@ bool apiIsAccessible var bannerText = "bannerText"; A.CallTo(() => courseService.GetCurrentCourses(CandidateId)).Returns(currentCourses); - A.CallTo(() => selfAssessmentService.GetSelfAssessmentsForCandidate(DelegateUserId, A._)).Returns(selfAssessments); + A.CallTo(() => selfAssessmentService.GetSelfAssessmentsForCandidate(DelegateUserId, A._, A._)).Returns(selfAssessments); A.CallTo(() => actionPlanService.GetIncompleteActionPlanResources(DelegateUserId)) .Returns((actionPlanResources, apiIsAccessible)); A.CallTo(() => centresService.GetBannerText(CentreId)).Returns(bannerText); @@ -426,7 +426,7 @@ public void MarkActionPlanResourceAsComplete_does_not_call_service_with_invalid_ private void GivenCurrentActivitiesAreEmptyLists() { A.CallTo(() => courseService.GetCurrentCourses(A._)).Returns(new List()); - A.CallTo(() => selfAssessmentService.GetSelfAssessmentsForCandidate(A._, A._)) + A.CallTo(() => selfAssessmentService.GetSelfAssessmentsForCandidate(A._, A._, A._)) .Returns(new List()); A.CallTo(() => actionPlanService.GetIncompleteActionPlanResources(A._)) .Returns((new List(), false)); diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs index 0581361535..7330042c4e 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/SupervisorController/SupervisorControllerTests.cs @@ -48,7 +48,7 @@ public class SupervisorControllerTests private ICandidateAssessmentDownloadFileService candidateAssessmentDownloadFileService = null!; private IPdfService pdfService = null!; private SupervisorController controller = null!; - private ICourseCategoriesService courseCategoriesService = null!; + private ICourseCategoriesService courseCategoriesService = null!; [SetUp] public void Setup() diff --git a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/CourseSetupControllerTests.cs b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/CourseSetupControllerTests.cs index f24d243c53..d047e1a744 100644 --- a/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/CourseSetupControllerTests.cs +++ b/DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/CourseSetup/CourseSetupControllerTests.cs @@ -416,7 +416,7 @@ public void SetCourseContent_get_redirects_to_summary_if_application_has_no_sect ).Returns(new List
()); // When - var result = controller.SetCourseContent(); + var result = controller.SetCourseContent(false); // Then result.Should().BeRedirectToActionResult().WithActionName("Summary"); diff --git a/DigitalLearningSolutions.Web/Controllers/FindYourCentreController.cs b/DigitalLearningSolutions.Web/Controllers/FindYourCentreController.cs index 6ae7adab72..51cdb69978 100644 --- a/DigitalLearningSolutions.Web/Controllers/FindYourCentreController.cs +++ b/DigitalLearningSolutions.Web/Controllers/FindYourCentreController.cs @@ -57,25 +57,27 @@ public async Task Index( return View("Index", model); } - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - FindCentreFilterCookieName - ); - var centreSummaries = centresService.GetAllCentreSummariesForFindCentre(); var regions = regionService.GetRegionsAlphabetical(); var availableFilters = FindYourCentreViewModelFilterOptions .GetFindCentreFilterModels(regions).ToList(); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + FindCentreFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( new SearchOptions(searchString, searchMatchCutoff: 90), null, new FilterOptions( - existingFilterString, + filterString, availableFilters ), new PaginationOptions(page, itemsPerPage) diff --git a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs index bdaa3d4e20..7133838e07 100644 --- a/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs +++ b/DigitalLearningSolutions.Web/Controllers/FrameworksController/Frameworks.cs @@ -166,7 +166,7 @@ public IActionResult CreateNewFramework(string actionname, int frameworkId = 0) var sessionNewFramework = multiPageFormService.GetMultiPageFormData( MultiPageFormDataFeature.AddNewFramework, TempData - ).GetAwaiter().GetResult(); + ).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData(sessionNewFramework, MultiPageFormDataFeature.AddNewFramework, TempData); detailFramework = sessionNewFramework.DetailFramework; } @@ -252,7 +252,7 @@ public IActionResult SaveNewFramework(string frameworkname, string actionname) { return StatusCode(500); } - var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); + var sessionNewFramework = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewFramework, TempData).GetAwaiter().GetResult(); multiPageFormService.SetMultiPageFormData( sessionNewFramework, MultiPageFormDataFeature.AddNewFramework, @@ -641,7 +641,8 @@ public IActionResult AddCollaborators(string actionname, int frameworkId, bool e [Route("/Frameworks/Collaborators/{actionname}/{frameworkId}/")] public IActionResult AddCollaborator(string actionname, string userEmail, bool canModify, int frameworkId) { - var collaboratorId = frameworkService.AddCollaboratorToFramework(frameworkId, userEmail, canModify); + int? centreID = GetCentreId(); + var collaboratorId = frameworkService.AddCollaboratorToFramework(frameworkId, userEmail, canModify, centreID); if (collaboratorId > 0) { frameworkNotificationService.SendFrameworkCollaboratorInvite(collaboratorId, GetAdminId()); diff --git a/DigitalLearningSolutions.Web/Controllers/LearningContentController.cs b/DigitalLearningSolutions.Web/Controllers/LearningContentController.cs index 4ac6dd3e8b..cbbaf62da4 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningContentController.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningContentController.cs @@ -53,14 +53,6 @@ public IActionResult Index( } sortBy ??= DefaultSortByOptions.Name.PropertyName; - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - BrandCoursesFilterCookieName - ); - var tutorials = tutorialService.GetPublicTutorialSummariesForBrand(brandId); var applications = courseService.GetApplicationsThatHaveSectionsByBrandId(brandId).ToList(); @@ -69,11 +61,21 @@ public IActionResult Index( var availableFilters = LearningContentViewModelFilterOptions .GetFilterOptions(categories, topics).ToList(); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + BrandCoursesFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( null, new SortOptions(sortBy, sortDirection), new FilterOptions( - existingFilterString, + filterString, availableFilters ), new PaginationOptions(page) diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs index d77ae9f556..ed30c74566 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/Current.cs @@ -44,7 +44,7 @@ public async Task Current( var centreId = User.GetCentreIdKnownNotNull(); var selfAssessments = - selfAssessmentService.GetSelfAssessmentsForCandidate(delegateUserId, centreId); + selfAssessmentService.GetSelfAssessmentsForCandidate(delegateUserId, centreId, 0); var (learningResources, apiIsAccessible) = await GetIncompleteActionPlanResourcesIfSignpostingEnabled(delegateUserId); @@ -81,7 +81,7 @@ public async Task AllCurrentItems() var centreId = User.GetCentreIdKnownNotNull(); var selfAssessment = - selfAssessmentService.GetSelfAssessmentsForCandidate(delegateUserId, centreId); + selfAssessmentService.GetSelfAssessmentsForCandidate(delegateUserId, centreId, 0); var (learningResources, _) = await GetIncompleteActionPlanResourcesIfSignpostingEnabled(delegateUserId); var model = new AllCurrentItemsPageViewModel(currentCourses, selfAssessment, learningResources); diff --git a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs index 3b39ac4361..2234f7d9eb 100644 --- a/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs +++ b/DigitalLearningSolutions.Web/Controllers/LearningPortalController/SelfAssessment.cs @@ -1532,6 +1532,22 @@ ManageOptionalCompetenciesViewModel model ); } } + var optionalCompetency = + (selfAssessmentService.GetCandidateAssessmentOptionalCompetencies(selfAssessmentId, delegateUserId)).Where(x => !x.IncludedInSelfAssessment); + if (optionalCompetency.Any()) + { + foreach (var optinal in optionalCompetency) + { + var selfAssessmentResults = selfAssessmentService.GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency(delegateUserId, selfAssessmentId, optinal.Id); + if (selfAssessmentResults.Any()) + { + foreach (var item in selfAssessmentResults) + { + selfAssessmentService.RemoveReviewCandidateAssessmentOptionalCompetencies(item.Id); + } + } + } + } if (model.GroupOptionalCompetenciesChecked != null) { var optionalCompetencies = @@ -1549,7 +1565,7 @@ ManageOptionalCompetenciesViewModel model } } - + return RedirectToAction("SelfAssessmentOverview", new { selfAssessmentId, vocabulary }); } @@ -1558,6 +1574,12 @@ ManageOptionalCompetenciesViewModel model public IActionResult RequestSignOff(int selfAssessmentId) { var delegateUserId = User.GetUserIdKnownNotNull(); + var delegateId = User.GetCandidateIdKnownNotNull(); + var recentResults = selfAssessmentService.GetMostRecentResults(selfAssessmentId, delegateId).ToList(); + var competencySummaries = CertificateHelper.CompetencySummation(recentResults); + + if (competencySummaries.QuestionsCount != competencySummaries.VerifiedCount) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + var assessment = selfAssessmentService.GetSelfAssessmentForCandidateById(delegateUserId, selfAssessmentId); var supervisors = selfAssessmentService.GetSignOffSupervisorsForSelfAssessmentId(selfAssessmentId, delegateUserId); @@ -1568,6 +1590,8 @@ public IActionResult RequestSignOff(int selfAssessmentId) Supervisors = supervisors, NumberOfSelfAssessedOptionalCompetencies = optionalCompetencies.Count(x => x.IncludedInSelfAssessment) }; + if (model.NumberOfSelfAssessedOptionalCompetencies < model.SelfAssessment.MinimumOptionalCompetencies) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + return View("SelfAssessments/RequestSignOff", model); } diff --git a/DigitalLearningSolutions.Web/Controllers/Register/ClaimAccountController.cs b/DigitalLearningSolutions.Web/Controllers/Register/ClaimAccountController.cs index 2f25c02be0..bda98a8ad8 100644 --- a/DigitalLearningSolutions.Web/Controllers/Register/ClaimAccountController.cs +++ b/DigitalLearningSolutions.Web/Controllers/Register/ClaimAccountController.cs @@ -297,22 +297,18 @@ public IActionResult AdminAccountAlreadyExists(string email, string centreName) return RedirectToAction("AccessDenied", "LearningSolutions"); } - var adminAccounts = userService.GetUserById(loggedInUserId)!.AdminAccounts; + var userEntity = userService.GetUserById(loggedInUserId); - if (adminAccounts.Any()) + if (userEntity.DelegateAccounts!.Any(account => account.CentreId == model.CentreId)) { - return RedirectToAction( - "AdminAccountAlreadyExists", + return RedirectToAction("AccountAlreadyExists", new { email = model.Email, centreName = model.CentreName } ); } - var delegateAccounts = userService.GetUserById(loggedInUserId)!.DelegateAccounts; - - if (delegateAccounts.Any(account => account.CentreId == model.CentreId)) + if (userEntity.AdminAccounts!.Any(account => account.CentreId == model.CentreId)) { - return RedirectToAction( - "AccountAlreadyExists", + return RedirectToAction("AdminAccountAlreadyExists", new { email = model.Email, centreName = model.CentreName } ); } diff --git a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs index 32bc6889f6..22eb2ddf41 100644 --- a/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs +++ b/DigitalLearningSolutions.Web/Controllers/SupervisorController/Supervisor.cs @@ -60,10 +60,6 @@ public IActionResult MyStaffList( var loggedInAdminUser = userService.GetAdminUserById(adminId); var centreRegistrationPrompts = centreRegistrationPromptsService.GetCentreRegistrationPromptsByCentreId(centreId); var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId, loggedInAdminUser.CategoryId); - if (!supervisorDelegateDetails.Any()) - { - supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminIdWithoutRemovedClause(adminId); - } var isSupervisor = User.GetCustomClaimAsBool(CustomClaimTypes.IsSupervisor) ?? false; var allSupervisorDelegateDetailViewModels = supervisorDelegateDetails.Select( supervisor => @@ -322,7 +318,7 @@ public IActionResult RemoveSupervisorDelegateConfirmed(SupervisorDelegateViewMod public IActionResult DelegateProfileAssessments(int supervisorDelegateId, int delegateUserId = 0) { var adminId = GetAdminId(); - var superviseDelegate = supervisorService.GetSupervisorDelegateDetailsByIdWithoutRemoveClause(supervisorDelegateId, adminId, delegateUserId); + var superviseDelegate = supervisorService.GetSupervisorDelegateDetailsById(supervisorDelegateId, adminId, delegateUserId); var loggedInUserId = User.GetAdminId(); var loggedInAdminUser = userService.GetAdminUserById(loggedInUserId!.GetValueOrDefault()); var delegateSelfAssessments = supervisorService.GetSelfAssessmentsForSupervisorDelegateId(supervisorDelegateId, loggedInAdminUser.CategoryId); @@ -339,9 +335,9 @@ public IActionResult DelegateProfileAssessments(int supervisorDelegateId, int de public IActionResult AllStaffList() { var adminId = GetAdminId(); + var loggedInAdminUser = userService.GetAdminUserById(adminId); var centreId = GetCentreId(); var loggedInUserId = User.GetUserId(); - var loggedInAdminUser = userService.GetAdminUserById(adminId); var centreCustomPrompts = centreRegistrationPromptsService.GetCentreRegistrationPromptsByCentreId(centreId); var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId, loggedInAdminUser.CategoryId) .Select(supervisor => @@ -1215,7 +1211,7 @@ public IActionResult SignOffProfileAssessment( SignOffProfileAssessmentViewModel model ) { - if ((!ModelState.IsValid) && (model.NumberOfSelfAssessedOptionalCompetencies > 0) && (!model.OptionalCompetenciesChecked)) + if ((!ModelState.IsValid) && (model.NumberOfSelfAssessedOptionalCompetencies > 0) && (!model.OptionalCompetenciesChecked) && model.SignedOff) { SelfAssessmentResultSummary? selfAssessmentSummary = supervisorService.GetSelfAssessmentResultSummary(candidateAssessmentId, supervisorDelegateId); @@ -1231,7 +1227,10 @@ SignOffProfileAssessmentViewModel model CandidateAssessmentSupervisorVerificationId = selfAssessmentSummary.CandidateAssessmentSupervisorVerificationId, CandidateAssessmentSupervisorVerificationSummaries = verificationsSummary, - NumberOfSelfAssessedOptionalCompetencies = optionalCompetencies.Count(x => x.IncludedInSelfAssessment) + NumberOfSelfAssessedOptionalCompetencies = optionalCompetencies.Count(x => x.IncludedInSelfAssessment), + SupervisorComments = model.SupervisorComments, + SignedOff = model.SignedOff, + IsSignOffverified = model.SignedOff }; return View("SignOffProfileAssessment", newModel); } @@ -1402,15 +1401,15 @@ public IActionResult ExportCandidateAssessment(int candidateAssessmentId, string [Route("/Supervisor/Staff/{supervisorDelegateId:int}/ProfileAssessment/{candidateAssessmentId:int}/Certificate")] public IActionResult CompetencySelfAssessmentCertificatesupervisor(int candidateAssessmentId, int supervisorDelegateId) { - var adminId = User.GetAdminId(); + var adminId = GetAdminId(); + var loggedInAdminUser = userService.GetAdminUserById(adminId); User.GetUserIdKnownNotNull(); - var loggedInAdminUser = userService.GetAdminUserById(adminId.Value); var competencymaindata = selfAssessmentService.GetCompetencySelfAssessmentCertificate(candidateAssessmentId); if ((competencymaindata == null) || (candidateAssessmentId == 0)) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } - var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId.Value, loggedInAdminUser.CategoryId); + var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId, loggedInAdminUser.CategoryId); var checkSupervisorDelegate = supervisorDelegateDetails.Where(x => x.DelegateUserID == competencymaindata.LearnerId).FirstOrDefault(); if ((checkSupervisorDelegate == null)) { @@ -1440,14 +1439,14 @@ public async Task DownloadCertificate(int candidateAssessmentId) { PdfReportStatusResponse pdfReportStatusResponse = new PdfReportStatusResponse(); var delegateId = User.GetCandidateIdKnownNotNull(); - var adminId = User.GetAdminId(); - var loggedInAdminUser = userService.GetAdminUserById(adminId.Value); + var adminId = GetAdminId(); + var loggedInAdminUser = userService.GetAdminUserById(adminId); var competencymaindata = selfAssessmentService.GetCompetencySelfAssessmentCertificate(candidateAssessmentId); if (competencymaindata == null || candidateAssessmentId == 0 || adminId == 0) { return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); } - var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId.Value, loggedInAdminUser.CategoryId); + var supervisorDelegateDetails = supervisorService.GetSupervisorDelegateDetailsForAdminId(adminId, loggedInAdminUser.CategoryId); var checkSupervisorDelegate = supervisorDelegateDetails.Where(x => x.DelegateUserID == competencymaindata.LearnerId).FirstOrDefault(); if (checkSupervisorDelegate == null) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); var delegateUserId = competencymaindata.LearnerId; diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs index 77ce1e4971..317eec01ad 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/Administrator/AdministratorController.cs @@ -61,13 +61,6 @@ public IActionResult Index( int? itemsPerPage = null ) { - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - AdminFilterCookieName - ); searchString = searchString == null ? null : searchString.Trim(); var centreId = User.GetCentreIdKnownNotNull(); var adminsAtCentre = userService.GetAdminsByCentreId(centreId); @@ -77,10 +70,20 @@ public IActionResult Index( var availableFilters = AdministratorsViewModelFilterOptions.GetAllAdministratorsFilterModels(categories); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + AdminFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( new SearchOptions(searchString), new SortOptions(GenericSortingHelper.DefaultSortOption, GenericSortingHelper.Ascending), - new FilterOptions(existingFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), new PaginationOptions(page, itemsPerPage) ); diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs index dccb1d0301..7af1d2f4ca 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs @@ -15,6 +15,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using DigitalLearningSolutions.Data.Extensions; + using DigitalLearningSolutions.Web.Services; [FeatureGate(FeatureFlags.RefactoredTrackingSystem)] [Authorize(Policy = CustomPolicies.UserCentreAdmin)] @@ -30,11 +31,13 @@ public class SelfAssessmentReportsController : Controller private readonly string tableauSiteName; private readonly string workbookName; private readonly string viewName; + private readonly ISelfAssessmentService selfAssessmentService; public SelfAssessmentReportsController( ISelfAssessmentReportService selfAssessmentReportService, ITableauConnectionHelperService tableauConnectionHelper, IClockUtility clockUtility, - IConfiguration config + IConfiguration config, + ISelfAssessmentService selfAssessmentService ) { this.selfAssessmentReportService = selfAssessmentReportService; @@ -44,12 +47,14 @@ IConfiguration config tableauSiteName = config.GetTableauSiteName(); workbookName = config.GetTableauWorkbookName(); viewName = config.GetTableauViewName(); + this.selfAssessmentService = selfAssessmentService; } public IActionResult Index() { var centreId = User.GetCentreId(); - var categoryId = User.GetAdminCategoryId(); - var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, categoryId)); + var adminCategoryId = User.GetAdminCategoryId(); + var categoryId = this.selfAssessmentService.GetSelfAssessmentCategoryId(1); + var model = new SelfAssessmentReportsViewModel(selfAssessmentReportService.GetSelfAssessmentsForReportList((int)centreId, adminCategoryId), adminCategoryId, categoryId); return View(model); } [HttpGet] diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/CourseSetup/CourseSetupController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/CourseSetup/CourseSetupController.cs index 5eb4f688d5..775e52ecdb 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/CourseSetup/CourseSetupController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/CourseSetup/CourseSetupController.cs @@ -92,47 +92,33 @@ public IActionResult Index( sortBy ??= DefaultSortByOptions.Name.PropertyName; sortDirection ??= GenericSortingHelper.Ascending; - existingFilterString = FilteringHelper.GetFilterString( + var centreId = User.GetCentreIdKnownNotNull(); + var categoryId = User.GetAdminCategoryId(); + var courseCategoryName = this.activityService.GetCourseCategoryNameForActivityFilter(categoryId); + var Categories = courseCategoriesService.GetCategoriesForCentreAndCentrallyManagedCourses(centreId).Select(c => c.CategoryName); + var Topics = courseTopicsService.GetCourseTopicsAvailableAtCentre(centreId).Select(c => c.CourseTopic); + + var availableFilters = CourseStatisticsViewModelFilterOptions + .GetFilterOptions(categoryId.HasValue ? new string[] { } : Categories, Topics).ToList(); + + var filterString = FilteringHelper.GetFilterString( existingFilterString, newFilterToAdd, clearFilters, Request, CourseFilterCookieName, - CourseStatusFilterOptions.IsActive.FilterValue + CourseStatusFilterOptions.IsActive.FilterValue, + availableFilters ); - var centreId = User.GetCentreIdKnownNotNull(); - var categoryId = User.GetAdminCategoryId(); - var courseCategoryName = this.activityService.GetCourseCategoryNameForActivityFilter(categoryId); - var Categories = courseCategoriesService.GetCategoriesForCentreAndCentrallyManagedCourses(centreId).Select(c => c.CategoryName); - var Topics = courseTopicsService.GetCourseTopicsAvailableAtCentre(centreId).Select(c => c.CourseTopic); - int offSet = ((page - 1) * itemsPerPage) ?? 0; string isActive, categoryName, courseTopic, hasAdminFields; isActive = categoryName = courseTopic = hasAdminFields = "Any"; bool? hideInLearnerPortal = null; - if (!string.IsNullOrEmpty(existingFilterString)) + if (!string.IsNullOrEmpty(filterString)) { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - - if (!string.IsNullOrEmpty(newFilterToAdd)) - { - var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[0]; - var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); - if (dupfilters.Count() > 1) - { - foreach (var filter in selectedFilters) - { - if (filter.Contains(filterHeader)) - { - selectedFilters.Remove(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - break; - } - } - } - } + var selectedFilters = filterString.Split(FilteringHelper.FilterSeparator).ToList(); if (selectedFilters.Count > 0) { @@ -173,14 +159,11 @@ public IActionResult Index( isActive, categoryName, courseTopic, hasAdminFields); } - var availableFilters = CourseStatisticsViewModelFilterOptions - .GetFilterOptions(categoryId.HasValue ? new string[] { } : Categories, Topics).ToList(); - var result = paginateService.Paginate( courses, resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), searchString, sortBy, sortDirection @@ -382,7 +365,7 @@ public IActionResult SetCourseOptions(EditCourseOptionsFormData model) data!.CourseOptionsData = model.ToCourseOptionsTempData(); multiPageFormService.SetMultiPageFormData(data, MultiPageFormDataFeature.AddNewCourse, TempData); - return RedirectToAction("SetCourseContent"); + return RedirectToAction("SetCourseContent", false); } [HttpGet("AddCourse/SetCourseContent")] @@ -390,7 +373,7 @@ public IActionResult SetCourseOptions(EditCourseOptionsFormData model) typeof(RedirectMissingMultiPageFormData), Arguments = new object[] { nameof(MultiPageFormDataFeature.AddNewCourse) } )] - public IActionResult SetCourseContent() + public IActionResult SetCourseContent(bool editCourseContent) { var data = multiPageFormService.GetMultiPageFormData(MultiPageFormDataFeature.AddNewCourse, TempData).GetAwaiter().GetResult(); @@ -398,6 +381,8 @@ public IActionResult SetCourseContent() { return RedirectToAction("Summary"); } + data.EditCourseContent = editCourseContent; + multiPageFormService.SetMultiPageFormData(data, MultiPageFormDataFeature.AddNewCourse, TempData); var model = data!.CourseContentData != null ? new SetCourseContentViewModel(data.CourseContentData) @@ -429,7 +414,10 @@ public IActionResult SetCourseContent(SetCourseContentViewModel model) } else { - data!.SectionContentData = null; + if (data!.SectionContentData == null) + { + data!.SectionContentData = null; + } } if (!ModelState.IsValid) @@ -471,9 +459,18 @@ public IActionResult SetSectionContent(int sectionIndex) } var showDiagnostic = data.Application!.DiagAssess; - var model = new SetSectionContentViewModel(section, sectionIndex, showDiagnostic, tutorials); + if (data.EditCourseContent) + { + var tutorial = GetTutorialsFromSectionContentData(data.SectionContentData, tutorials); + var model = new SetSectionContentViewModel(section, sectionIndex, showDiagnostic, tutorial); + return View("AddNewCentreCourse/SetSectionContent", model); + } + else + { + var model = new SetSectionContentViewModel(section, sectionIndex, showDiagnostic, tutorials); + return View("AddNewCentreCourse/SetSectionContent", model); + } - return View("AddNewCentreCourse/SetSectionContent", model); } [HttpPost("AddCourse/SetSectionContent")] @@ -654,14 +651,19 @@ private IActionResult SaveSectionAndRedirect(SetSectionContentViewModel model) { data.SectionContentData = new List(); } - + if (data.EditCourseContent) + { + return RedirectToNextSectionOrSummary( + model.Index, + new SetCourseContentViewModel(data.CourseContentData!)); + } data!.SectionContentData!.Add( - new SectionContentTempData( - model.Tutorials != null - ? model.Tutorials.Select(GetCourseTutorialData) - : new List() - ) - ); + new SectionContentTempData( + model.Tutorials != null + ? model.Tutorials.Select(GetCourseTutorialData) + : new List() + ) + ); multiPageFormService.SetMultiPageFormData(data, MultiPageFormDataFeature.AddNewCourse, TempData); return RedirectToNextSectionOrSummary( @@ -727,6 +729,25 @@ private static IEnumerable UpdateC return updatedCourses; } + private IEnumerable GetTutorialsFromSectionContentData(List sectionContentData, List sectionTutorial) + { + if (sectionContentData == null || sectionTutorial == null) return new List(); + var updatedRecords = sectionContentData + .SelectMany(data => data.Tutorials) + .Join(sectionTutorial, + tempData => new { tempData.TutorialId, tempData.TutorialName }, // Match on both TutorialId and TutorialName + index => new { index.TutorialId, index.TutorialName }, + (tempData, index) => new Tutorial + { + TutorialId = index.TutorialId, + TutorialName = index.TutorialName, + Status = tempData.LearningEnabled, // Updated from sectionContentData + DiagStatus = tempData.DiagnosticEnabled // Updated from sectionContentData + }) + .ToList(); + + return updatedRecords; + } } } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs index bfa04cd271..f806ab1991 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ActivityDelegatesController.cs @@ -96,34 +96,30 @@ public IActionResult Index( sortBy ??= DefaultSortByOptions.Name.PropertyName; sortDirection ??= GenericSortingHelper.Ascending; - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - filterCookieName, - CourseDelegateAccountStatusFilterOptions.Active.FilterValue - ); + var availableFilters = isCourseDelegate + ? CourseDelegateViewModelFilterOptions.GetAllCourseDelegatesFilterViewModels(courseAdminFieldsService.GetCourseAdminFieldsForCourse(customisationId.Value).AdminFields) + : SelfAssessmentDelegateViewModelFilterOptions.GetAllSelfAssessmentDelegatesFilterViewModels(); - if (isCourseDelegate) - { - if (TempData["actDelCustomisationId"] != null && TempData["actDelCustomisationId"].ToString() != customisationId.ToString() - && existingFilterString != null && existingFilterString.Contains("Answer")) - { - var availableCourseFilters = CourseDelegateViewModelFilterOptions.GetAllCourseDelegatesFilterViewModels(courseAdminFieldsService.GetCourseAdminFieldsForCourse(customisationId.Value).AdminFields); - existingFilterString = FilterHelper.RemoveNonExistingPromptFilters(availableCourseFilters, existingFilterString); - } - } - else + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + filterCookieName, + CourseDelegateAccountStatusFilterOptions.Active.FilterValue, + availableFilters + ); + + if (!isCourseDelegate) { isUnsupervisedSelfAssessment = selfAssessmentService.IsUnsupervisedSelfAssessment((int)selfAssessmentId); - if (existingFilterString != null) + if (filterString != null) { var existingfilterList = isUnsupervisedSelfAssessment ? - existingFilterString!.Split(FilteringHelper.FilterSeparator).Where(filter => !filter.Contains("SignedOff")).ToList() : - existingFilterString!.Split(FilteringHelper.FilterSeparator).Where(filter => !filter.Contains("SubmittedDate")).ToList(); + filterString!.Split(FilteringHelper.FilterSeparator).Where(filter => !filter.Contains("SignedOff")).ToList() : + filterString!.Split(FilteringHelper.FilterSeparator).Where(filter => !filter.Contains("SubmittedDate")).ToList(); - existingFilterString = existingfilterList.Any() ? string.Join(FilteringHelper.FilterSeparator, existingfilterList) : null; + filterString = existingfilterList.Any() ? string.Join(FilteringHelper.FilterSeparator, existingfilterList) : null; } } @@ -139,27 +135,9 @@ public IActionResult Index( string? answer1, answer2, answer3; answer1 = answer2 = answer3 = null; - if (!string.IsNullOrEmpty(existingFilterString)) + if (!string.IsNullOrEmpty(filterString)) { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - - if (!string.IsNullOrEmpty(newFilterToAdd)) - { - var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[0]; - var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); - if (dupfilters.Count() > 1) - { - foreach (var filter in selectedFilters) - { - if (filter.Contains(filterHeader)) - { - selectedFilters.Remove(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - break; - } - } - } - } + var selectedFilters = filterString.Split(FilteringHelper.FilterSeparator).ToList(); if (selectedFilters.Count > 0) { @@ -246,7 +224,7 @@ public IActionResult Index( page = 1; offSet = 0; (selfAssessmentDelegatesData, resultCount) = selfAssessmentService.GetSelfAssessmentDelegatesPerPage(searchString ?? string.Empty, offSet, itemsPerPage ?? 0, sortBy, sortDirection, selfAssessmentId, centreId, isDelegateActive, removed, submitted, signedOff, adminCategoryId); - } + } var adminId = User.GetCustomClaimAsRequiredInt(CustomClaimTypes.UserAdminId); @@ -269,10 +247,6 @@ public IActionResult Index( } } - var availableFilters = isCourseDelegate - ? CourseDelegateViewModelFilterOptions.GetAllCourseDelegatesFilterViewModels(courseDelegatesData.CourseAdminFields) - : SelfAssessmentDelegateViewModelFilterOptions.GetAllSelfAssessmentDelegatesFilterViewModels(); - var activityName = isCourseDelegate ? courseService.GetCourseNameAndApplication((int)customisationId).CourseName : selfAssessmentService.GetSelfAssessmentNameById((int)selfAssessmentId); @@ -283,7 +257,7 @@ public IActionResult Index( courseDelegatesData.Delegates, (int)resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters, CourseDelegateAccountStatusFilterOptions.Active.FilterValue), + new FilterOptions(filterString, availableFilters, CourseDelegateAccountStatusFilterOptions.Active.FilterValue), searchString, sortBy, sortDirection); @@ -301,7 +275,7 @@ public IActionResult Index( selfAssessmentDelegatesData.Delegates, (int)resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters, CourseDelegateAccountStatusFilterOptions.Active.FilterValue), + new FilterOptions(filterString, availableFilters, CourseDelegateAccountStatusFilterOptions.Active.FilterValue), searchString, sortBy, sortDirection); @@ -391,7 +365,9 @@ public IActionResult DownloadCurrent( fileName ); } + [Route("DownloadActivityDelegates/{selfAssessmentId:int}")] + [ServiceFilter(typeof(VerifyAdminUserCanAccessSelfAssessment))] public IActionResult DownloadActivityDelegates( int selfAssessmentId, string? searchString = null, @@ -404,7 +380,6 @@ public IActionResult DownloadActivityDelegates( sortBy ??= DefaultSortByOptions.Name.PropertyName; sortDirection ??= GenericSortingHelper.Ascending; - bool? isDelegateActive, isProgressLocked, removed, hasCompleted, submitted, signedOff; isDelegateActive = isProgressLocked = removed = hasCompleted = submitted = signedOff = null; @@ -472,6 +447,7 @@ public IActionResult DownloadActivityDelegates( fileName ); } + [Route("TrackingSystem/Delegates/ActivityDelegates/{candidateAssessmentsId}/Remove")] [HttpGet] public IActionResult RemoveDelegateSelfAssessment(int candidateAssessmentsId) @@ -482,12 +458,21 @@ public IActionResult RemoveDelegateSelfAssessment(int candidateAssessmentsId) return RedirectToAction("StatusCode", "LearningSolutions", new { code = 410 }); } var selfAssessmentDelegate = selfAssessmentService.GetDelegateSelfAssessmentByCandidateAssessmentsId(candidateAssessmentsId); - if (selfAssessmentDelegate == null) + if (selfAssessmentDelegate != null) + { + var adminCategoryId = User.GetAdminCategoryId(); + var selfAssessmentCategoryId = selfAssessmentService.GetSelfAssessmentCategoryId(selfAssessmentDelegate.SelfAssessmentID); + if (adminCategoryId > 0 && adminCategoryId != selfAssessmentCategoryId) + { + return RedirectToAction("StatusCode", "LearningSolutions", new { code = 403 }); + } + var model = new DelegateSelfAssessmenteViewModel(selfAssessmentDelegate); + return View(model); + } + else { return new NotFoundResult(); } - var model = new DelegateSelfAssessmenteViewModel(selfAssessmentDelegate); - return View(model); } [Route("TrackingSystem/Delegates/ActivityDelegates/{candidateAssessmentsId}/Remove")] @@ -518,6 +503,7 @@ public IActionResult RemoveDelegateSelfAssessment(DelegateSelfAssessmenteViewMod [HttpGet] [ServiceFilter(typeof(IsCentreAuthorizedSelfAssessment))] + [ServiceFilter(typeof(VerifyAdminUserCanAccessSelfAssessment))] [Route("{selfAssessmentId:int}/EditCompleteByDate")] public IActionResult EditCompleteByDate( int delegateUserId, @@ -591,6 +577,7 @@ DelegateAccessRoute accessedVia return RedirectToAction("Index", "ActivityDelegates", routeData, returnPageQuery.Value.ItemIdToReturnTo); } } + } } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/AllDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/AllDelegatesController.cs index 9bdedeb4c4..77a1170c99 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/AllDelegatesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/AllDelegatesController.cs @@ -80,14 +80,6 @@ public IActionResult Index( sortDirection ??= GenericSortingHelper.Ascending; DelegateFilterCookieName += env.EnvironmentName; - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - DelegateFilterCookieName, - DelegateActiveStatusFilterOptions.IsActive.FilterValue - ); int offSet = ((page - 1) * itemsPerPage) ?? 0; @@ -99,41 +91,24 @@ public IActionResult Index( var promptsWithOptions = customPrompts.Where(customPrompt => customPrompt.Options.Count > 0); var availableFilters = AllDelegatesViewModelFilterOptions.GetAllDelegatesFilterViewModels(jobGroups, promptsWithOptions, groups); - if (existingFilterString != null && TempData["allDelegatesCentreId"] != null && - TempData["allDelegatesCentreId"].ToString() != User.GetCentreId().ToString()) - { - if (existingFilterString.Contains("Answer")) - existingFilterString = FilterHelper.RemoveNonExistingPromptFilters(availableFilters, existingFilterString); - if (existingFilterString != null && existingFilterString.Contains("DelegateGroup")) - existingFilterString = FilterHelper.RemoveNonExistingFilterOptions(availableFilters, existingFilterString); - } + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + DelegateFilterCookieName, + DelegateActiveStatusFilterOptions.IsActive.FilterValue, + availableFilters + ); string isActive, isPasswordSet, isAdmin, isUnclaimed, isEmailVerified, registrationType, answer1, answer2, answer3, answer4, answer5, answer6; isActive = isPasswordSet = isAdmin = isUnclaimed = isEmailVerified = registrationType = answer1 = answer2 = answer3 = answer4 = answer5 = answer6 = "Any"; int jobGroupId = 0; int? groupId = null; - if (!string.IsNullOrEmpty(existingFilterString)) + if (!string.IsNullOrEmpty(filterString)) { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - - if (!string.IsNullOrEmpty(newFilterToAdd)) - { - var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[1]; - var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); - if (dupfilters.Count() > 1) - { - foreach (var filter in selectedFilters) - { - if (filter.Contains(filterHeader)) - { - selectedFilters.Remove(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - break; - } - } - } - } + var selectedFilters = filterString.Split(FilteringHelper.FilterSeparator).ToList(); if (selectedFilters.Count > 0) { @@ -167,11 +142,6 @@ public IActionResult Index( if (filter.Contains("DelegateGroupId")) { groupId = Convert.ToInt32(filterValue); - if (!(groups.Any(g => g.Item1 == groupId))) - { - groupId = null; - existingFilterString = FilterHelper.RemoveNonExistingFilterOptions(availableFilters, existingFilterString); - } } if (filter.Contains("Answer1")) @@ -211,7 +181,7 @@ public IActionResult Index( delegates, resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), + new FilterOptions(filterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), searchString, sortBy, sortDirection @@ -229,9 +199,8 @@ public IActionResult Index( model.TotalPages = (int)(resultCount / itemsPerPage) + ((resultCount % itemsPerPage) > 0 ? 1 : 0); model.MatchingSearchResults = resultCount; - Response.UpdateFilterCookie(DelegateFilterCookieName, existingFilterString); + Response.UpdateFilterCookie(DelegateFilterCookieName, filterString); TempData.Remove("delegateRegistered"); - TempData["allDelegatesCentreId"] = User.GetCentreId().ToString(); return View(model); } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateCoursesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateCoursesController.cs index 70040ebb9c..eda47bdb8a 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateCoursesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateCoursesController.cs @@ -4,14 +4,12 @@ using DigitalLearningSolutions.Data.Helpers; using DigitalLearningSolutions.Data.Models.Courses; using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate; - using DigitalLearningSolutions.Data.Models.SelfAssessments; using DigitalLearningSolutions.Web.Attributes; using DigitalLearningSolutions.Web.Helpers; using DigitalLearningSolutions.Web.Helpers.FilterOptions; using DigitalLearningSolutions.Web.Models.Enums; using DigitalLearningSolutions.Web.Services; using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates.DelegateCourses; - using DocumentFormat.OpenXml.Wordprocessing; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.FeatureManagement.Mvc; @@ -67,15 +65,6 @@ public IActionResult Index( sortBy ??= DefaultSortByOptions.Name.PropertyName; sortDirection ??= GenericSortingHelper.Ascending; - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - CourseFilterCookieName, - CourseStatusFilterOptions.IsActive.FilterValue - ); - var centreId = User.GetCentreIdKnownNotNull(); var categoryId = User.GetAdminCategoryId(); var courseCategoryName = this.activityService.GetCourseCategoryNameForActivityFilter(categoryId); @@ -89,33 +78,19 @@ public IActionResult Index( var availableFilters = DelegateCourseStatisticsViewModelFilterOptions .GetFilterOptions(categoryId.HasValue ? new string[] { } : Categories, Topics).ToList(); - if (TempData["DelegateActivitiesCentreId"] != null && TempData["DelegateActivitiesCentreId"].ToString() != User.GetCentreId().ToString() - && existingFilterString != null) - { - existingFilterString = FilterHelper.RemoveNonExistingFilterOptions(availableFilters, existingFilterString); - } + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + CourseFilterCookieName, + CourseStatusFilterOptions.IsActive.FilterValue, + availableFilters + ); - if (!string.IsNullOrEmpty(existingFilterString)) + if (!string.IsNullOrEmpty(filterString)) { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - - if (!string.IsNullOrEmpty(newFilterToAdd)) - { - var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[0]; - var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); - if (dupfilters.Count() > 1) - { - foreach (var filter in selectedFilters) - { - if (filter.Contains(filterHeader)) - { - selectedFilters.Remove(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - break; - } - } - } - } + var selectedFilters = filterString.Split(FilteringHelper.FilterSeparator).ToList(); if (selectedFilters.Count > 0) { @@ -177,7 +152,7 @@ public IActionResult Index( allItems, resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), + new FilterOptions(filterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), searchString, sortBy, sortDirection @@ -204,7 +179,6 @@ public IActionResult Index( model.TotalPages = (int)(resultCount / itemsPerPage) + ((resultCount % itemsPerPage) > 0 ? 1 : 0); model.MatchingSearchResults = resultCount; Response.UpdateFilterCookie(CourseFilterCookieName, result.FilterString); - TempData["DelegateActivitiesCentreId"] = centreId; return View(model); } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs index 84680c9ba7..12f327cbc7 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/DelegateGroupsController.cs @@ -69,15 +69,6 @@ public IActionResult Index( sortDirection = GenericSortingHelper.Ascending; } - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - DelegateGroupsFilterCookieName, - null - ); - var centreId = User.GetCentreIdKnownNotNull(); var addedByAdmins = groupsService.GetAdminsForCentreGroups(centreId); @@ -95,37 +86,23 @@ public IActionResult Index( var availableFilters = DelegateGroupsViewModelFilterOptions .GetDelegateGroupFilterModels(addedByAdmins, registrationPrompts).ToList(); - if (TempData["DelegateGroupCentreId"] != null && TempData["DelegateGroupCentreId"].ToString() != User.GetCentreId().ToString() - && existingFilterString != null) - { - existingFilterString = FilterHelper.RemoveNonExistingFilterOptions(availableFilters, existingFilterString); - } + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + DelegateGroupsFilterCookieName, + null, + availableFilters + ); int offSet = ((page - 1) * itemsPerPage) ?? 0; string filterAddedBy = ""; string filterLinkedField = ""; - if (!string.IsNullOrEmpty(existingFilterString)) + if (!string.IsNullOrEmpty(filterString)) { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - - if (!string.IsNullOrEmpty(newFilterToAdd)) - { - var filterHeader = newFilterToAdd.Split(FilteringHelper.Separator)[0]; - var dupfilters = selectedFilters.Where(x => x.Contains(filterHeader)); - if (dupfilters.Count() > 1) - { - foreach (var filter in selectedFilters) - { - if (filter.Contains(filterHeader)) - { - selectedFilters.Remove(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - break; - } - } - } - } + var selectedFilters = filterString.Split(FilteringHelper.FilterSeparator).ToList(); if (selectedFilters.Count > 0) { @@ -174,7 +151,7 @@ public IActionResult Index( groups, resultCount, new PaginationOptions(page, itemsPerPage), - new FilterOptions(existingFilterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), + new FilterOptions(filterString, availableFilters, DelegateActiveStatusFilterOptions.IsActive.FilterValue), searchString, sortBy, sortDirection @@ -191,7 +168,6 @@ public IActionResult Index( model.TotalPages = (int)(resultCount / itemsPerPage) + ((resultCount % itemsPerPage) > 0 ? 1 : 0); model.MatchingSearchResults = resultCount; Response.UpdateFilterCookie(DelegateGroupsFilterCookieName, result.FilterString); - TempData["DelegateGroupCentreId"] = centreId; return View(model); } diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/EmailDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/EmailDelegatesController.cs index a297f7b7d2..74617c2ba4 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/EmailDelegatesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/EmailDelegatesController.cs @@ -62,13 +62,6 @@ public IActionResult Index( bool selectAll = false ) { - var newFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - EmailDelegateFilterCookieName - ); var jobGroups = jobGroupsService.GetJobGroupsAlphabetical(); var customPrompts = promptsService.GetCentreRegistrationPrompts(User.GetCentreIdKnownNotNull()); var delegateUsers = GetDelegateUserCards(); @@ -79,10 +72,20 @@ public IActionResult Index( promptsWithOptions ); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + EmailDelegateFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( null, null, - new FilterOptions(newFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), null ); @@ -115,13 +118,6 @@ public IActionResult Index( if (!ModelState.IsValid) { - var newFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - EmailDelegateFilterCookieName - ); var jobGroups = jobGroupsService.GetJobGroupsAlphabetical(); var customPrompts = promptsService.GetCentreRegistrationPrompts(User.GetCentreIdKnownNotNull()); @@ -131,10 +127,20 @@ public IActionResult Index( promptsWithOptions ); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + EmailDelegateFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( null, null, - new FilterOptions(newFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), null ); diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupCoursesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupCoursesController.cs index a1222156db..313731b5a2 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupCoursesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupCoursesController.cs @@ -114,14 +114,6 @@ public IActionResult AddCourseToGroupSelectCourse( int page = 1 ) { - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - GroupAddCourseFilterCookieName - ); - var centreId = User.GetCentreIdKnownNotNull(); var adminCategoryFilter = User.GetAdminCategoryId(); @@ -136,10 +128,20 @@ public IActionResult AddCourseToGroupSelectCourse( ? AddCourseToGroupViewModelFilterOptions.GetAllCategoriesFilters(categories, topics) : AddCourseToGroupViewModelFilterOptions.GetSingleCategoryFilters(courses)).ToList(); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + GroupAddCourseFilterCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( new SearchOptions(searchString), new SortOptions(nameof(CourseAssessmentDetails.CourseName), GenericSortingHelper.Ascending), - new FilterOptions(existingFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), new PaginationOptions(page) ); var result = searchSortFilterPaginateService.SearchFilterSortAndPaginate( diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupDelegatesController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupDelegatesController.cs index 6e7b2b4ce4..206c2492c7 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupDelegatesController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/GroupDelegatesController.cs @@ -78,14 +78,6 @@ public IActionResult SelectDelegate( int page = 1 ) { - existingFilterString = FilteringHelper.GetFilterString( - existingFilterString, - newFilterToAdd, - clearFilters, - Request, - AddGroupDelegateCookieName - ); - var centreId = User.GetCentreIdKnownNotNull(); var jobGroups = jobGroupsService.GetJobGroupsAlphabetical().ToList(); var customPrompts = promptsService.GetCentreRegistrationPrompts(centreId).ToList(); @@ -97,13 +89,23 @@ public IActionResult SelectDelegate( promptsWithOptions ); + var filterString = FilteringHelper.GetFilterString( + existingFilterString, + newFilterToAdd, + clearFilters, + Request, + AddGroupDelegateCookieName, + null, + availableFilters + ); + var searchSortPaginationOptions = new SearchSortFilterAndPaginateOptions( new SearchOptions(searchString), new SortOptions( DefaultSortByOptions.Name.PropertyName, GenericSortingHelper.Ascending ), - new FilterOptions(existingFilterString, availableFilters), + new FilterOptions(filterString, availableFilters), new PaginationOptions(page) ); var result = searchSortFilterPaginateService.SearchFilterSortAndPaginate( diff --git a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs index 22ca66bb84..e0c157e3f2 100644 --- a/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs +++ b/DigitalLearningSolutions.Web/Controllers/TrackingSystem/Delegates/ViewDelegateController.cs @@ -81,6 +81,7 @@ public IActionResult Index(int delegateId, string? callType) course.LastUpdated = DateHelper.GetLocalDateTime(course.LastUpdated); course.Completed = course.Completed?.TimeOfDay == TimeSpan.Zero ? course.Completed : DateHelper.GetLocalDateTime(course.Completed); } + var selfAssessments = selfAssessmentService.GetSelfAssessmentsForCandidate(delegateEntity.UserAccount.Id, centreId, categoryIdFilter); diff --git a/DigitalLearningSolutions.Web/Helpers/CertificateHelper.cs b/DigitalLearningSolutions.Web/Helpers/CertificateHelper.cs index 7d107df12f..912354764a 100644 --- a/DigitalLearningSolutions.Web/Helpers/CertificateHelper.cs +++ b/DigitalLearningSolutions.Web/Helpers/CertificateHelper.cs @@ -46,5 +46,25 @@ public static CompetencySummary CanViewCertificate(List reviewedComp }; return model; } + public static CompetencySummary CompetencySummation(List reviewedCompetencies) + { + var CompetencyGroups = reviewedCompetencies.GroupBy(competency => competency.CompetencyGroup); + var competencySummaries = CompetencyGroups.Select(g => + { + var questions = g.SelectMany(c => c.AssessmentQuestions).Where(q => q.Required); + var verifiedCount = questions.Count(q => !((q.Result == null || q.Verified == null || q.SignedOff != true) && q.Required)); + return new + { + QuestionsCount = questions.Count(), + VerifiedCount = verifiedCount + }; + }); + var model = new CompetencySummary() + { + VerifiedCount = competencySummaries.Sum(item => item.VerifiedCount), + QuestionsCount = competencySummaries.Sum(item => item.QuestionsCount), + }; + return model; + } } } diff --git a/DigitalLearningSolutions.Web/Helpers/FilterHelper.cs b/DigitalLearningSolutions.Web/Helpers/FilterHelper.cs deleted file mode 100644 index 7a472f4e51..0000000000 --- a/DigitalLearningSolutions.Web/Helpers/FilterHelper.cs +++ /dev/null @@ -1,115 +0,0 @@ -using DigitalLearningSolutions.Data.Helpers; -using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate; -using System.Collections.Generic; -using System.Linq; - -namespace DigitalLearningSolutions.Web.Helpers -{ - public static class FilterHelper - { - public static string? RemoveNonExistingPromptFilters(List availableFilters, string existingFilterString) - { - if (existingFilterString != null && existingFilterString.Contains("Answer")) - { - if (availableFilters.Where(x => x.FilterGroupKey == "prompts").ToList().Any()) - { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - var existingPromptFilters = existingFilterString!.Split(FilteringHelper.FilterSeparator).Where(filter => filter.Contains("Answer")).ToList(); - - foreach (var existingPromptFilter in existingPromptFilters) - { - bool isFound = false; - var splitFilter = existingPromptFilter.Split(FilteringHelper.Separator); - var filterHeaderArr = splitFilter[0].SkipWhile(c => c != '(').Skip(1).TakeWhile(c => c != ')').ToArray(); - var filterHeader = string.Join("", filterHeaderArr); //prompt header - var filterOptionText = splitFilter[2] == FilteringHelper.EmptyValue ? - "No option selected" : splitFilter[2]; //prompt option text - var promptDbText = splitFilter[1]; //filter db text eg. Answer1 - - var availableFilterOptions = availableFilters.Where(x => x.FilterGroupKey == "prompts" && x.FilterName == filterHeader) - .Select(o => o.FilterOptions).ToList(); - - foreach (var filterOption in availableFilterOptions) - { - if (filterOption.Any(x => x.DisplayText.Contains(filterOptionText))) - { - var filter = filterOption.Where(x => x.DisplayText.Contains(filterOptionText)).ToList().Select(x => x.FilterValue).FirstOrDefault(); - if (!filter.Contains(promptDbText)) - { //when prompt filter header and selected option match but db coulum (eg. Answer1) does not match - //remove from existing filter and add from available filter - selectedFilters.Remove(existingPromptFilter); - selectedFilters.Add(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - } - isFound = true; break; - } - } - if (!isFound) - { - selectedFilters.Remove(existingPromptFilter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - if (existingFilterString == "") existingFilterString = null; - } - } - } - else - { - var filtersExceptPrompts = existingFilterString!.Split(FilteringHelper.FilterSeparator).Where(filter => !filter.Contains("Answer")).ToList(); - existingFilterString = filtersExceptPrompts.Any() ? string.Join(FilteringHelper.FilterSeparator, filtersExceptPrompts) : null; - } - } - return existingFilterString; - } - - public static string? RemoveNonExistingFilterOptions(List availableFilters, string existingFilterString) - { - var selectedFilters = existingFilterString.Split(FilteringHelper.FilterSeparator).ToList(); - string[] filterGroups = { "LinkedToField", "AddedByAdminId", "CourseTopic", "CategoryName", "DelegateGroup" }; - foreach (var filterGroup in filterGroups) - { - var existingFilters = existingFilterString!.Split(FilteringHelper.FilterSeparator).Where(filter => filter.Contains(filterGroup)).ToList(); - - foreach (var existingFilter in existingFilters) - { - bool isFound = false; - var splitFilter = existingFilter.Split(FilteringHelper.Separator); - var filterHeader = splitFilter[1]; - var filterOptionText = splitFilter[2]; - var availableFilterOptions = availableFilters.Where(x => x.FilterProperty == filterGroup).Select(o => o.FilterOptions).ToList(); - foreach (var availableFilterOption in availableFilterOptions) - { - if (filterGroup == "LinkedToField") - { - if (availableFilterOption.Any(x => x.FilterValue.Contains(filterHeader))) - { - var filter = availableFilterOption.Where(x => x.FilterValue.Contains(filterHeader)).ToList().Select(x => x.FilterValue).FirstOrDefault(); - if (!filter.Contains(filterOptionText)) - { - selectedFilters.Remove(existingFilter); - selectedFilters.Add(filter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - } - isFound = true; break; - } - } - else - { - if (availableFilterOption.Any(x => x.FilterValue.Contains(filterOptionText))) - { - isFound = true; break; - } - } - } - - if (!isFound) - { - selectedFilters.Remove(existingFilter); - existingFilterString = string.Join(FilteringHelper.FilterSeparator, selectedFilters); - } - } - } - if (existingFilterString == "") existingFilterString = null; - return existingFilterString; - } - } -} diff --git a/DigitalLearningSolutions.Web/ServiceFilter/VerifyAdminUserCanAccessSelfAssessment.cs b/DigitalLearningSolutions.Web/ServiceFilter/VerifyAdminUserCanAccessSelfAssessment.cs new file mode 100644 index 0000000000..684f5974fa --- /dev/null +++ b/DigitalLearningSolutions.Web/ServiceFilter/VerifyAdminUserCanAccessSelfAssessment.cs @@ -0,0 +1,39 @@ +namespace DigitalLearningSolutions.Web.ServiceFilter +{ + using Microsoft.AspNetCore.Mvc; + using Microsoft.AspNetCore.Mvc.Filters; + using DigitalLearningSolutions.Web.Helpers; + using DigitalLearningSolutions.Web.Services; + using Microsoft.Extensions.Logging; + + public class VerifyAdminUserCanAccessSelfAssessment : IActionFilter + { + private readonly ISelfAssessmentService selfAssessmentService; + private readonly ILogger logger; + + public VerifyAdminUserCanAccessSelfAssessment(ISelfAssessmentService selfAssessmentService, + ILogger logger) + { + this.selfAssessmentService = selfAssessmentService; + this.logger = logger; + } + + public void OnActionExecuted(ActionExecutedContext context) { } + + public void OnActionExecuting(ActionExecutingContext context) + { + if (!(context.Controller is Controller controller)) + { + return; + } + var adminCategoryId = controller.User.GetAdminCategoryId(); + var selfAssessmentId = int.Parse(context.ActionArguments["selfAssessmentId"].ToString()!); + var selfAssessmentCategoryId = selfAssessmentService.GetSelfAssessmentCategoryId((selfAssessmentId)); + if (adminCategoryId > 0 && adminCategoryId != selfAssessmentCategoryId) + { + logger.LogWarning($"Attempt to access restricted self assessment {selfAssessmentId} by user {controller.User.GetUserIdKnownNotNull()}"); + context.Result = new RedirectToActionResult("StatusCode", "LearningSolutions", new { code = 403 }); + } + } + } +} diff --git a/DigitalLearningSolutions.Web/Services/CourseService.cs b/DigitalLearningSolutions.Web/Services/CourseService.cs index 9c255baf2e..530d34c22c 100644 --- a/DigitalLearningSolutions.Web/Services/CourseService.cs +++ b/DigitalLearningSolutions.Web/Services/CourseService.cs @@ -134,7 +134,7 @@ int diagCompletionThreshold int GetNumberOfActiveCoursesAtCentreFilteredByCategory(int centreId, int? categoryId); public IEnumerable GetApplicationsAvailableToCentre(int centreId); bool IsSelfEnrollmentAllowed(int customisationId); - Customisation? GetCourse(int customisationId); + Customisation? GetCourse(int? customisationId); } public class CourseService : ICourseService @@ -632,7 +632,7 @@ public bool IsSelfEnrollmentAllowed(int customisationId) return courseDataService.IsSelfEnrollmentAllowed(customisationId); } - public Customisation? GetCourse(int customisationId) + public Customisation? GetCourse(int? customisationId) { return courseDataService.GetCourse(customisationId); } diff --git a/DigitalLearningSolutions.Web/Services/FrameworkService.cs b/DigitalLearningSolutions.Web/Services/FrameworkService.cs index 7ddb9b6169..9aa59127dd 100644 --- a/DigitalLearningSolutions.Web/Services/FrameworkService.cs +++ b/DigitalLearningSolutions.Web/Services/FrameworkService.cs @@ -124,7 +124,7 @@ bool zeroBased int InsertFrameworkCompetency(int competencyId, int? frameworkCompetencyGroupID, int adminId, int frameworkId); - int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify); + int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify, int? centreID); void AddCustomFlagToFramework(int frameworkId, string flagName, string flagGroup, string flagTagClass); void UpdateFrameworkCustomFlag(int frameworkId, int id, string flagName, string flagGroup, string flagTagClass); @@ -264,9 +264,9 @@ public FrameworkService(IFrameworkDataService frameworkDataService) this.frameworkDataService = frameworkDataService; } - public int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify) + public int AddCollaboratorToFramework(int frameworkId, string userEmail, bool canModify, int? centreID) { - return frameworkDataService.AddCollaboratorToFramework(frameworkId, userEmail, canModify); + return frameworkDataService.AddCollaboratorToFramework(frameworkId, userEmail, canModify, centreID); } public void AddCompetencyAssessmentQuestion(int frameworkCompetencyId, int assessmentQuestionId, int adminId) diff --git a/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs b/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs index 37b44e8e34..ce7afea305 100644 --- a/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs +++ b/DigitalLearningSolutions.Web/Services/SelfAssessmentService.cs @@ -150,7 +150,17 @@ public int GetSelfAssessmentActivityDelegatesExportCount(string searchString, st bool IsCentreSelfAssessment(int selfAssessmentId, int centreId); bool HasMinimumOptionalCompetencies(int selfAssessmentId, int delegateUserId); public int GetSelfAssessmentCategoryId(int selfAssessmentId); - + IEnumerable GetSelfAssessmentResultsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ); + public IEnumerable GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ); + void RemoveReviewCandidateAssessmentOptionalCompetencies(int id); } public class SelfAssessmentService : ISelfAssessmentService @@ -575,5 +585,25 @@ public int GetSelfAssessmentCategoryId(int selfAssessmentId) { return selfAssessmentDataService.GetSelfAssessmentCategoryId(selfAssessmentId); } + public IEnumerable GetSelfAssessmentResultsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ) + { + return selfAssessmentDataService.GetSelfAssessmentResultsForDelegateSelfAssessmentCompetency(delegateUserId, selfAssessmentId, competencyId); + } + public IEnumerable GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency( + int delegateUserId, + int selfAssessmentId, + int competencyId + ) + { + return selfAssessmentDataService.GetSelfAssessmentResultswithSupervisorVerificationsForDelegateSelfAssessmentCompetency(delegateUserId, selfAssessmentId, competencyId); + } + public void RemoveReviewCandidateAssessmentOptionalCompetencies(int id) + { + selfAssessmentDataService.RemoveReviewCandidateAssessmentOptionalCompetencies(id); + } } } diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index 193bdd1d2f..bdc34b4d79 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -51,7 +51,6 @@ namespace DigitalLearningSolutions.Web using static DigitalLearningSolutions.Web.Services.ICentreApplicationsService; using static DigitalLearningSolutions.Web.Services.ICentreSelfAssessmentsService; using System; - using IsolationLevel = System.Transactions.IsolationLevel; using Serilog; public class Startup @@ -581,6 +580,7 @@ private static void RegisterWebServiceFilters(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app, IMigrationRunner migrationRunner, IFeatureManager featureManager) diff --git a/DigitalLearningSolutions.Web/ViewModels/Supervisor/SignOffProfileAssessmentViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/Supervisor/SignOffProfileAssessmentViewModel.cs index c44a39a4c7..c6cd41ec5d 100644 --- a/DigitalLearningSolutions.Web/ViewModels/Supervisor/SignOffProfileAssessmentViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/Supervisor/SignOffProfileAssessmentViewModel.cs @@ -21,5 +21,6 @@ public class SignOffProfileAssessmentViewModel [Range(1, 1, ErrorMessage = "Please tick to confirm that you have reviewed the optional competencies included in this self assessment and they are appropriate to the learner’s role.")] public bool OptionalCompetenciesChecked { get; set; } public int NumberOfSelfAssessedOptionalCompetencies { get; set; } + public bool? IsSignOffverified { get; set; } } } diff --git a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs index 3bcbadf03b..6947aad14b 100644 --- a/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs +++ b/DigitalLearningSolutions.Web/ViewModels/TrackingSystem/Centre/Reports/SelfAssessmentReportsViewModel.cs @@ -6,11 +6,15 @@ public class SelfAssessmentReportsViewModel { public SelfAssessmentReportsViewModel( - IEnumerable selfAssessmentSelects + IEnumerable selfAssessmentSelects, int? adminCategoryId, int categoryId ) { SelfAssessmentSelects = selfAssessmentSelects; + AdminCategoryId = adminCategoryId; + CategoryId = categoryId; } public IEnumerable SelfAssessmentSelects { get; set; } + public int? AdminCategoryId { get; set; } + public int CategoryId { get; set; } } } diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentOverview.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentOverview.cshtml index a021e1fdbf..e9e97c5bbf 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentOverview.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/SelfAssessments/SelfAssessmentOverview.cshtml @@ -14,6 +14,9 @@ .Select(q => q.ResultDateTime) .DefaultIfEmpty(DateTime.MinValue) .Max(); + bool signedOff = (from record in Model.SupervisorSignOffs + orderby record.ID descending + select record.SignedOff).FirstOrDefault(); var competencySummaries = from g in Model.CompetencyGroups let questions = g.SelectMany(c => c.AssessmentQuestions).Where(q => q.Required) let selfAssessedCount = questions.Count(q => q.Result.HasValue) @@ -41,7 +44,7 @@ } @section mobilebacklink - { +{

@@ -197,7 +200,7 @@ } @if (!Model.SupervisorSignOffs.Where(x => x.Verified == null).Any() - && latestResult > latestSignoff + && (latestResult > latestSignoff || !signedOff) && (Model.NumberOfSelfAssessedOptionalCompetencies >= Model.SelfAssessment.MinimumOptionalCompetencies) ) { diff --git a/DigitalLearningSolutions.Web/Views/LearningPortal/Shared/_Dates.cshtml b/DigitalLearningSolutions.Web/Views/LearningPortal/Shared/_Dates.cshtml index 935177900a..d99eb8994a 100644 --- a/DigitalLearningSolutions.Web/Views/LearningPortal/Shared/_Dates.cshtml +++ b/DigitalLearningSolutions.Web/Views/LearningPortal/Shared/_Dates.cshtml @@ -6,7 +6,7 @@

- Enrolled + First enrolled
@Model.StartedDate.ToShortDateString() diff --git a/DigitalLearningSolutions.Web/Views/SuperAdmin/Centres/ManageCentre.cshtml b/DigitalLearningSolutions.Web/Views/SuperAdmin/Centres/ManageCentre.cshtml index 1e39fe25ce..e4e6e1ea0b 100644 --- a/DigitalLearningSolutions.Web/Views/SuperAdmin/Centres/ManageCentre.cshtml +++ b/DigitalLearningSolutions.Web/Views/SuperAdmin/Centres/ManageCentre.cshtml @@ -8,7 +8,7 @@
  1. Centres
  2. -
  3. Manage centre
  4. +
  5. Manage centre

Roles:

- @if (centreRow.IsActiveAdmin) + @if (Model.UserEntity.AdminAccounts.Any()) { foreach (var admin in Model.UserEntity.AdminAccounts) { - if (centreRow.CentreId==admin.CentreId) + if (centreRow.CentreId == admin.CentreId) { - Admin + bool IsDelegateActive = false; + foreach (var delegates in Model.UserEntity.DelegateAccounts) + { + if (centreRow.CentreId == delegates.CentreId) + { + IsDelegateActive = delegates.Active; + } + } + Admin@(!admin.Active && IsDelegateActive ? "(Inactive)" : "") } } } diff --git a/DigitalLearningSolutions.Web/Views/Supervisor/ReviewCompetencySelfAsessment.cshtml b/DigitalLearningSolutions.Web/Views/Supervisor/ReviewCompetencySelfAsessment.cshtml index 8a8df8157d..c812e43f9a 100644 --- a/DigitalLearningSolutions.Web/Views/Supervisor/ReviewCompetencySelfAsessment.cshtml +++ b/DigitalLearningSolutions.Web/Views/Supervisor/ReviewCompetencySelfAsessment.cshtml @@ -19,14 +19,14 @@
  • My Staff
  • @Model.SupervisorDelegate.FirstName @Model.SupervisorDelegate.LastName + asp-action="DelegateProfileAssessments" + asp-route-supervisorDelegateId="@ViewContext.RouteData.Values["supervisorDelegateId"]">@Model.SupervisorDelegate.FirstName @Model.SupervisorDelegate.LastName
  • + asp-action="ReviewDelegateSelfAssessment" + asp-route-supervisorDelegateId="@ViewContext.RouteData.Values["supervisorDelegateId"]" + asp-route-candidateAssessmentId="@Model.DelegateSelfAssessment.ID"> @(Model.DelegateSelfAssessment.RoleName.Length > 35 ? Model.DelegateSelfAssessment.RoleName.Substring(0, 32) + "..." : Model.DelegateSelfAssessment.RoleName)
  • @@ -37,9 +37,9 @@

    + asp-action="ReviewDelegateSelfAssessment" + asp-route-supervisorDelegateId="@ViewContext.RouteData.Values["supervisorDelegateId"]" + asp-route-candidateAssessmentId="@Model.DelegateSelfAssessment.ID"> Back to @(Model.DelegateSelfAssessment.RoleName.Length > 35 ? Model.DelegateSelfAssessment.RoleName.Substring(0, 32) + "..." : Model.DelegateSelfAssessment.RoleName)

    @@ -122,13 +122,11 @@ } @if (Model.Competency.AssessmentQuestions.First().IncludeComments) { + var commentsLabel = string.IsNullOrEmpty(Model.Competency.AssessmentQuestions.First()?.CommentsPrompt) ? + Model.DelegateSelfAssessment.ReviewerCommentsLabel : Model.Competency.AssessmentQuestions.First()?.CommentsPrompt;
    - @(((Model.Competency.AssessmentQuestions.First().SignedOff == true && Model.Competency.AssessmentQuestions.First().Verified.HasValue) - || (Model.Competency.AssessmentQuestions.First().ResultId != null && Model.Competency.AssessmentQuestions.First().Verified == null && Model.Competency.AssessmentQuestions.First().Requested != null && Model.Competency.AssessmentQuestions.First().UserIsVerifier == false) - || (Model.Competency.AssessmentQuestions.First().Verified == null && Model.Competency.AssessmentQuestions.First().Requested != null) - && (!String.IsNullOrEmpty(Model.DelegateSelfAssessment.ReviewerCommentsLabel))) - ? Model.DelegateSelfAssessment.ReviewerCommentsLabel : "Comments") + @(!String.IsNullOrEmpty(commentsLabel) ? commentsLabel : "Comments")
    @Html.Raw(Model.Competency.AssessmentQuestions.First().SupportingComments) diff --git a/DigitalLearningSolutions.Web/Views/Supervisor/SignOffProfileAssessment.cshtml b/DigitalLearningSolutions.Web/Views/Supervisor/SignOffProfileAssessment.cshtml index b1387a5d6c..f47fa23a6e 100644 --- a/DigitalLearningSolutions.Web/Views/Supervisor/SignOffProfileAssessment.cshtml +++ b/DigitalLearningSolutions.Web/Views/Supervisor/SignOffProfileAssessment.cshtml @@ -2,209 +2,209 @@ @using DigitalLearningSolutions.Web.ViewModels.Supervisor @model SignOffProfileAssessmentViewModel; @{ - var errorHasOccurred = !ViewData.ModelState.IsValid; - ViewData["Title"] = "Sign-off Self Assessment"; - ViewData["Application"] = "Supervisor"; - ViewData["HeaderPathName"] = "Supervisor"; + var errorHasOccurred = !ViewData.ModelState.IsValid; + ViewData["Title"] = "Sign-off Self Assessment"; + ViewData["Application"] = "Supervisor"; + ViewData["HeaderPathName"] = "Supervisor"; } @section NavMenuItems { - + } - @section NavBreadcrumbs { - +@section NavBreadcrumbs { + } @if (errorHasOccurred) { } -
    +
    -

    - @Model.SupervisorDelegate.FirstName @Model.SupervisorDelegate.LastName -

    +

    + @Model.SupervisorDelegate.FirstName @Model.SupervisorDelegate.LastName +

    - +
    -
    -

    @Model.SelfAssessmentResultSummary.RoleName

    -
    +
    +

    @Model.SelfAssessmentResultSummary.RoleName

    +
    -
    - Assessment questions in profile -
    -
    - @Model.SelfAssessmentResultSummary.CompetencyAssessmentQuestionCount -
    +
    + Assessment questions in profile +
    +
    + @Model.SelfAssessmentResultSummary.CompetencyAssessmentQuestionCount +
    -
    - Assessment question responses -
    -
    - @Model.SelfAssessmentResultSummary.ResultCount -
    +
    + Assessment question responses +
    +
    + @Model.SelfAssessmentResultSummary.ResultCount +
    -
    - Responses verified -
    -
    - @Model.SelfAssessmentResultSummary.VerifiedCount -
    +
    + Responses verified +
    +
    + @Model.SelfAssessmentResultSummary.VerifiedCount +
    @if (Model.CandidateAssessmentSupervisorVerificationSummaries.Any()) - { -
    -
    - Response confirmers -
    -
    - @foreach (var candidateAssessmentSupervisorVerificationSummary in Model.CandidateAssessmentSupervisorVerificationSummaries) - { - - @DisplayStringHelper.GetPotentiallyInactiveAdminName( - candidateAssessmentSupervisorVerificationSummary.Forename, - candidateAssessmentSupervisorVerificationSummary.Surname, - candidateAssessmentSupervisorVerificationSummary.AdminActive) (@candidateAssessmentSupervisorVerificationSummary.Email) - @candidateAssessmentSupervisorVerificationSummary.VerifiedCount - - -
    - } -
    - -
    - } - - @if (Model.SelfAssessmentResultSummary.UngradedCount < Model.SelfAssessmentResultSummary.ResultCount) - { -
    -
    - Responses meeting role requirements -
    -
    - @Model.SelfAssessmentResultSummary.MeetingCount -
    - -
    -
    -
    - Responses partially meeting role requirements -
    -
    - @Model.SelfAssessmentResultSummary.PartiallyMeetingCount -
    - -
    -
    -
    - Responses not meeting role requirements -
    -
    - @Model.SelfAssessmentResultSummary.NotMeetingCount -
    - -
    -
    -
    - Responses with no role requirements set -
    -
    - @Model.SelfAssessmentResultSummary.UngradedCount -
    -
    - } + { +
    +
    + Response confirmers +
    +
    + @foreach (var candidateAssessmentSupervisorVerificationSummary in Model.CandidateAssessmentSupervisorVerificationSummaries) + { + + @DisplayStringHelper.GetPotentiallyInactiveAdminName( + candidateAssessmentSupervisorVerificationSummary.Forename, + candidateAssessmentSupervisorVerificationSummary.Surname, + candidateAssessmentSupervisorVerificationSummary.AdminActive) (@candidateAssessmentSupervisorVerificationSummary.Email) - @candidateAssessmentSupervisorVerificationSummary.VerifiedCount + + +
    + } +
    + +
    + } + + @if (Model.SelfAssessmentResultSummary.UngradedCount < Model.SelfAssessmentResultSummary.ResultCount) + { +
    +
    + Responses meeting role requirements +
    +
    + @Model.SelfAssessmentResultSummary.MeetingCount +
    + +
    +
    +
    + Responses partially meeting role requirements +
    +
    + @Model.SelfAssessmentResultSummary.PartiallyMeetingCount +
    + +
    +
    +
    + Responses not meeting role requirements +
    +
    + @Model.SelfAssessmentResultSummary.NotMeetingCount +
    + +
    +
    +
    + Responses with no role requirements set +
    +
    + @Model.SelfAssessmentResultSummary.UngradedCount +
    +
    + }
    -
    -
    -
    - -

    - Sign-off Self Assessment -

    -
    - +
    + +
    + +

    + Sign-off Self Assessment +

    +
    + - - - - - -
    -
    - - -
    - @Html.Raw(Model.SelfAssessmentResultSummary.SignOffSupervisorStatement == null ? - "I confirm that this profile self-assessment accurately captures" + - $" {Model.SupervisorDelegate.FirstName} {Model.SupervisorDelegate.LastName}'s " + - "current capability in the areas assessed." : - Model.SelfAssessmentResultSummary.SignOffSupervisorStatement) -
    -
    -
    - - -
    - You must provide sign-off comments indicating the reason for rejecting sign-off. -
    -
    - -
    -
    + + + + + +
    +
    + + +
    + @Html.Raw(Model.SelfAssessmentResultSummary.SignOffSupervisorStatement == null ? + "I confirm that this profile self-assessment accurately captures" + + $" {Model.SupervisorDelegate.FirstName} {Model.SupervisorDelegate.LastName}'s " + + "current capability in the areas assessed." : + Model.SelfAssessmentResultSummary.SignOffSupervisorStatement) +
    +
    +
    + + +
    + You must provide sign-off comments indicating the reason for rejecting sign-off. +
    +
    + +
    +
    @if (Model.NumberOfSelfAssessedOptionalCompetencies > 0) { @@ -217,25 +217,25 @@ }
    - - -
    + + +
    @section scripts { - + } diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml index d61717e107..d68daf4064 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml @@ -28,10 +28,13 @@

    Excel learner activity reports

    Download Excel competency self assessments activity reports for your centre.

      + @if((Model.AdminCategoryId == null) || (Model.AdminCategoryId == Model.CategoryId)) + {
    • Digital Skills Assessment Tool - Download report
    • + } @if (Model.SelfAssessmentSelects.Any()) { @foreach (var report in Model.SelfAssessmentSelects) diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/CourseSetup/AddNewCentreCourse/_CourseContentSummary.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/CourseSetup/AddNewCentreCourse/_CourseContentSummary.cshtml index 31031f3363..ab5298108e 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/CourseSetup/AddNewCentreCourse/_CourseContentSummary.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/CourseSetup/AddNewCentreCourse/_CourseContentSummary.cshtml @@ -26,7 +26,8 @@
      @if (!Model.NoContent) { - + Change course content } diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateCourseProgressDetailsWithStatusTags.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateCourseProgressDetailsWithStatusTags.cshtml index 8faa2883b9..d11de651f1 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateCourseProgressDetailsWithStatusTags.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateCourseProgressDetailsWithStatusTags.cshtml @@ -36,7 +36,7 @@
    - Enrolled + First enrolled
    @Model.Enrolled diff --git a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateSelfAssessmentProgressDetails.cshtml b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateSelfAssessmentProgressDetails.cshtml index 81b836ab24..2b5caa35c2 100644 --- a/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateSelfAssessmentProgressDetails.cshtml +++ b/DigitalLearningSolutions.Web/Views/TrackingSystem/Delegates/Shared/_DelegateSelfAssessmentProgressDetails.cshtml @@ -5,7 +5,7 @@
    - Enrolled + First enrolled
    @Model.StartedDate.ToString(DateHelper.StandardDateAndTimeFormat)