diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs index 2f388ba5cc..68240fd974 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/CourseDataServiceTests.cs @@ -290,13 +290,13 @@ public void GetNumberOfActiveCoursesAtCentreFilteredByCategory_with_filtered_cat } [Test] - public void GetNumberOfActiveCoursesAtCentreFilteredByCategory_excludes_courses_from_archived_applications() + public void GetNumberOfActiveCoursesAtCentreFilteredByCategory_includes_courses_from_archived_applications() { // When var count = courseDataService.GetNumberOfActiveCoursesAtCentreFilteredByCategory(101, null); // Then - count.Should().Be(141); + count.Should().Be(144); } [Test] @@ -345,6 +345,40 @@ public void GetCourseStatisticsAtCentreFilteredByCategory_should_return_course_s LearningMinutes = "N/A", }; + result.Should().HaveCount(259); + result.First().Should().BeEquivalentTo(expectedFirstCourse); + } + + [Test] + public void GetNonArchivedCourseStatisticsAtCentreFilteredByCategory_should_return_course_statistics_correctly() + { + // Given + const int centreId = 101; + int? categoryId = null; + + // When + var result = courseDataService.GetNonArchivedCourseStatisticsAtCentreFilteredByCategory(centreId, categoryId).ToList(); + + // Then + var expectedFirstCourse = new CourseStatistics + { + CustomisationId = 100, + CentreId = 101, + Active = false, + AllCentres = false, + ApplicationId = 1, + ApplicationName = "Entry Level - Win XP, Office 2003/07 OLD", + CustomisationName = "Standard", + DelegateCount = 25, + AllAttempts = 49, + AttemptsPassed = 34, + CompletedCount = 5, + HideInLearnerPortal = false, + CategoryName = "Office 2007", + CourseTopic = "Microsoft Office", + LearningMinutes = "N/A", + }; + result.Should().HaveCount(256); result.First().Should().BeEquivalentTo(expectedFirstCourse); } @@ -463,6 +497,37 @@ public void GetCoursesAvailableToCentreByCategory_returns_expected_values() IsAssessed = false, }; + result.Should().HaveCount(259); + result.First().Should().BeEquivalentTo(expectedFirstCourse); + } + + [Test] + public void GetNonArchivedCoursesAvailableToCentreByCategory_returns_expected_values() + { + // Given + const int centreId = 101; + int? categoryId = null; + + // When + var result = courseDataService.GetNonArchivedCoursesAvailableToCentreByCategory(centreId, categoryId) + .ToList(); + + // Then + var expectedFirstCourse = new CourseAssessmentDetails + { + CustomisationId = 100, + CentreId = 101, + ApplicationId = 1, + ApplicationName = "Entry Level - Win XP, Office 2003/07 OLD", + CustomisationName = "Standard", + Active = false, + CategoryName = "Undefined", + CourseTopic = "Undefined", + HasDiagnostic = false, + HasLearning = true, + IsAssessed = false, + }; + result.Should().HaveCount(256); result.First().Should().BeEquivalentTo(expectedFirstCourse); } diff --git a/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs index 66a60c8d4c..b3f665907b 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/CourseServiceTests.cs @@ -550,14 +550,14 @@ public void GetCourseOptionAlphabeticalListForCentre_calls_data_service() const int categoryId = 1; const int centreId = 1; var courseOptions = new List(); - A.CallTo(() => courseDataService.GetCoursesAvailableToCentreByCategory(centreId, categoryId)) + A.CallTo(() => courseDataService.GetNonArchivedCoursesAvailableToCentreByCategory(centreId, categoryId)) .Returns(courseOptions); // When var result = courseService.GetCourseOptionsAlphabeticalListForCentre(centreId, categoryId); // Then - A.CallTo(() => courseDataService.GetCoursesAvailableToCentreByCategory(centreId, categoryId)) + A.CallTo(() => courseDataService.GetNonArchivedCoursesAvailableToCentreByCategory(centreId, categoryId)) .MustHaveHappened(); result.Should().BeEquivalentTo(courseOptions); } diff --git a/DigitalLearningSolutions.Data/DataServices/CourseAdminFieldsDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseAdminFieldsDataService.cs index 941317057e..807bf637bd 100644 --- a/DigitalLearningSolutions.Data/DataServices/CourseAdminFieldsDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CourseAdminFieldsDataService.cs @@ -68,8 +68,7 @@ LEFT JOIN CoursePrompts AS cp2 LEFT JOIN CoursePrompts AS cp3 ON cu.CourseField3PromptID = cp3.CoursePromptID INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = cu.ApplicationID - WHERE ap.ArchivedDate IS NULL - AND ap.DefaultContentTypeID <> 4 + WHERE ap.DefaultContentTypeID <> 4 AND cu.CustomisationID = @customisationId", new { customisationId } ).Single(); @@ -183,7 +182,6 @@ int customisationId FROM Customisations AS cu INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = cu.ApplicationID WHERE CustomisationID = @customisationId - AND ap.ArchivedDate IS NULL AND ap.DefaultContentTypeID <> 4", new { customisationId } ).Single(); diff --git a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs index 35e9b4d853..b5f9deeed8 100644 --- a/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/CourseDataService.cs @@ -28,6 +28,8 @@ public interface ICourseDataService IEnumerable GetCourseStatisticsAtCentreFilteredByCategory(int centreId, int? categoryId); + IEnumerable GetNonArchivedCourseStatisticsAtCentreFilteredByCategory(int centreId, int? categoryId); + IEnumerable GetDelegateCoursesInfo(int delegateId); DelegateCourseInfo? GetDelegateCourseInfoByProgressId(int progressId); @@ -38,6 +40,8 @@ public interface ICourseDataService IEnumerable GetCoursesAvailableToCentreByCategory(int centreId, int? categoryId); + IEnumerable GetNonArchivedCoursesAvailableToCentreByCategory(int centreId, int? categoryId); + IEnumerable GetApplicationsAvailableToCentreByCategory(int centreId, int? categoryId); IEnumerable GetApplicationsByBrandId(int brandId); @@ -141,9 +145,51 @@ FROM dbo.AssessAttempts AS aa WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] = 1 AND can.CandidateId = ca.CandidateId) AS AttemptsPassed"; + private const string TutorialWithLearningCountQuery = + @"SELECT COUNT(ct.TutorialID) + FROM CustomisationTutorials AS ct + INNER JOIN Tutorials AS t ON ct.TutorialID = t.TutorialID + WHERE ct.Status = 1 AND ct.CustomisationID = c.CustomisationID"; + + private const string TutorialWithDiagnosticCountQuery = + @"SELECT COUNT(ct.TutorialID) + FROM CustomisationTutorials AS ct + INNER JOIN Tutorials AS t ON ct.TutorialID = t.TutorialID + INNER JOIN Customisations AS c ON c.CustomisationID = ct.CustomisationID + INNER JOIN Applications AS a ON a.ApplicationID = c.ApplicationID + WHERE ct.DiagStatus = 1 AND a.DiagAssess = 1 AND ct.CustomisationID = c.CustomisationID + AND a.ArchivedDate IS NULL AND a.DefaultContentTypeID <> 4"; + private readonly IDbConnection connection; private readonly ILogger logger; + private readonly string CourseStatisticsQuery = @$"SELECT + cu.CustomisationID, + cu.CentreID, + cu.Active, + cu.AllCentres, + ap.ApplicationId, + ap.ApplicationName, + cu.CustomisationName, + {DelegateCountQuery}, + {CompletedCountQuery}, + {AllAttemptsQuery}, + {AttemptsPassedQuery}, + cu.HideInLearnerPortal, + cc.CategoryName, + ct.CourseTopic, + cu.LearningTimeMins AS LearningMinutes, + cu.IsAssessed + FROM dbo.Customisations AS cu + INNER JOIN dbo.CentreApplications AS ca ON ca.ApplicationID = cu.ApplicationID + INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = ca.ApplicationID + INNER JOIN dbo.CourseCategories AS cc ON cc.CourseCategoryID = ap.CourseCategoryID + INNER JOIN dbo.CourseTopics AS ct ON ct.CourseTopicID = ap.CourseTopicId + WHERE (ap.CourseCategoryID = @categoryId OR @categoryId IS NULL) + AND (cu.CentreID = @centreId OR (cu.AllCentres = 1 AND ca.Active = 1)) + AND ca.CentreID = @centreId + AND ap.DefaultContentTypeID <> 4"; + private readonly string selectDelegateCourseInfoQuery = @$"SELECT cu.CustomisationID AS CustomisationId, @@ -194,6 +240,27 @@ FROM Customisations cu LEFT OUTER JOIN AdminUsers auEnrolledBy ON auEnrolledBy.AdminID = pr.EnrolledByAdminID INNER JOIN Candidates AS ca ON ca.CandidateID = pr.CandidateID"; + private readonly string courseAssessmentDetailsQuery = $@"SELECT + c.CustomisationID, + c.CentreID, + c.ApplicationID, + ap.ApplicationName, + c.CustomisationName, + c.Active, + c.IsAssessed, + cc.CategoryName, + ct.CourseTopic, + CASE WHEN ({TutorialWithLearningCountQuery}) > 0 THEN 1 ELSE 0 END AS HasLearning, + CASE WHEN ({TutorialWithDiagnosticCountQuery}) > 0 THEN 1 ELSE 0 END AS HasDiagnostic + FROM Customisations AS c + INNER JOIN Applications AS ap ON ap.ApplicationID = c.ApplicationID + INNER JOIN CourseCategories AS cc ON ap.CourseCategoryId = cc.CourseCategoryId + INNER JOIN CourseTopics AS ct ON ap.CourseTopicId = ct.CourseTopicId + WHERE (c.CentreID = @centreId OR c.AllCentres = 1) + AND (ap.CourseCategoryID = @categoryId OR @categoryId IS NULL) + AND EXISTS (SELECT CentreApplicationID FROM CentreApplications WHERE (ApplicationID = c.ApplicationID) AND (CentreID = @centreID) AND (Active = 1)) + AND ap.DefaultContentTypeID <> 4"; + public CourseDataService(IDbConnection connection, ILogger logger) { this.connection = connection; @@ -309,7 +376,6 @@ FROM dbo.Customisations AS cu AND cu.Active = 1 AND cu.CentreID = @centreId AND ca.CentreID = @centreId - AND ap.ArchivedDate IS NULL AND ap.DefaultContentTypeID <> 4", new { centreId, adminCategoryId } ); @@ -321,33 +387,18 @@ public IEnumerable GetCourseStatisticsAtCentreFilteredByCatego ) { return connection.Query( - @$"SELECT - cu.CustomisationID, - cu.CentreID, - cu.Active, - cu.AllCentres, - ap.ApplicationId, - ap.ApplicationName, - cu.CustomisationName, - {DelegateCountQuery}, - {CompletedCountQuery}, - {AllAttemptsQuery}, - {AttemptsPassedQuery}, - cu.HideInLearnerPortal, - cc.CategoryName, - ct.CourseTopic, - cu.LearningTimeMins AS LearningMinutes, - cu.IsAssessed - FROM dbo.Customisations AS cu - INNER JOIN dbo.CentreApplications AS ca ON ca.ApplicationID = cu.ApplicationID - INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = ca.ApplicationID - INNER JOIN dbo.CourseCategories AS cc ON cc.CourseCategoryID = ap.CourseCategoryID - INNER JOIN dbo.CourseTopics AS ct ON ct.CourseTopicID = ap.CourseTopicId - WHERE (ap.CourseCategoryID = @categoryId OR @categoryId IS NULL) - AND (cu.CentreID = @centreId OR (cu.AllCentres = 1 AND ca.Active = 1)) - AND ca.CentreID = @centreId - AND ap.ArchivedDate IS NULL - AND ap.DefaultContentTypeID <> 4", + CourseStatisticsQuery, + new { centreId, categoryId } + ); + } + + public IEnumerable GetNonArchivedCourseStatisticsAtCentreFilteredByCategory( + int centreId, + int? categoryId + ) + { + return connection.Query( + @$"{CourseStatisticsQuery} AND ap.ArchivedDate IS NULL", new { centreId, categoryId } ); } @@ -357,7 +408,6 @@ public IEnumerable GetDelegateCoursesInfo(int delegateId) return connection.Query( $@"{selectDelegateCourseInfoQuery} WHERE pr.CandidateID = @delegateId - AND ap.ArchivedDate IS NULL AND pr.RemovedDate IS NULL AND ap.DefaultContentTypeID <> 4", new { delegateId } @@ -369,7 +419,6 @@ AND pr.RemovedDate IS NULL return connection.QuerySingleOrDefault( $@"{selectDelegateCourseInfoQuery} WHERE pr.ProgressID = @progressId - AND ap.ArchivedDate IS NULL AND ap.DefaultContentTypeID <> 4", new { progressId } ); @@ -464,43 +513,16 @@ FROM Customisations cu public IEnumerable GetCoursesAvailableToCentreByCategory(int centreId, int? categoryId) { - const string tutorialWithLearningCountQuery = - @"SELECT COUNT(ct.TutorialID) - FROM CustomisationTutorials AS ct - INNER JOIN Tutorials AS t ON ct.TutorialID = t.TutorialID - WHERE ct.Status = 1 AND ct.CustomisationID = c.CustomisationID"; - - const string tutorialWithDiagnosticCountQuery = - @"SELECT COUNT(ct.TutorialID) - FROM CustomisationTutorials AS ct - INNER JOIN Tutorials AS t ON ct.TutorialID = t.TutorialID - INNER JOIN Customisations AS c ON c.CustomisationID = ct.CustomisationID - INNER JOIN Applications AS a ON a.ApplicationID = c.ApplicationID - WHERE ct.DiagStatus = 1 AND a.DiagAssess = 1 AND ct.CustomisationID = c.CustomisationID - AND a.ArchivedDate IS NULL AND a.DefaultContentTypeID <> 4"; + return connection.Query( + courseAssessmentDetailsQuery, + new { centreId, categoryId } + ); + } + public IEnumerable GetNonArchivedCoursesAvailableToCentreByCategory(int centreId, int? categoryId) + { return connection.Query( - $@"SELECT - c.CustomisationID, - c.CentreID, - c.ApplicationID, - ap.ApplicationName, - c.CustomisationName, - c.Active, - c.IsAssessed, - cc.CategoryName, - ct.CourseTopic, - CASE WHEN ({tutorialWithLearningCountQuery}) > 0 THEN 1 ELSE 0 END AS HasLearning, - CASE WHEN ({tutorialWithDiagnosticCountQuery}) > 0 THEN 1 ELSE 0 END AS HasDiagnostic - FROM Customisations AS c - INNER JOIN Applications AS ap ON ap.ApplicationID = c.ApplicationID - INNER JOIN CourseCategories AS cc ON ap.CourseCategoryId = cc.CourseCategoryId - INNER JOIN CourseTopics AS ct ON ap.CourseTopicId = ct.CourseTopicId - WHERE ap.ArchivedDate IS NULL - AND (c.CentreID = @centreId OR c.AllCentres = 1) - AND (ap.CourseCategoryID = @categoryId OR @categoryId IS NULL) - AND EXISTS (SELECT CentreApplicationID FROM CentreApplications WHERE (ApplicationID = c.ApplicationID) AND (CentreID = @centreID) AND (Active = 1)) - AND ap.DefaultContentTypeID <> 4", + @$"{courseAssessmentDetailsQuery} AND ap.ArchivedDate IS NULL", new { centreId, categoryId } ); } @@ -567,7 +589,6 @@ FROM Candidates AS cn INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = c.ApplicationID WHERE cn.CentreID = @centreID AND (ap.CourseCategoryID = @categoryId OR @categoryId IS NULL) - AND ap.ArchivedDate IS NULL AND ap.DefaultContentTypeID <> 4", new { centreId, categoryId } ); diff --git a/DigitalLearningSolutions.Data/Services/CourseService.cs b/DigitalLearningSolutions.Data/Services/CourseService.cs index 4bbb7ede65..8f8fbd3a52 100644 --- a/DigitalLearningSolutions.Data/Services/CourseService.cs +++ b/DigitalLearningSolutions.Data/Services/CourseService.cs @@ -156,6 +156,22 @@ public IEnumerable ); } + private IEnumerable + GetNonArchivedCentreSpecificCourseStatisticsWithAdminFieldResponseCounts( + int centreId, + int? categoryId, + bool includeAllCentreCourses = false + ) + { + var allCourses = courseDataService.GetNonArchivedCourseStatisticsAtCentreFilteredByCategory(centreId, categoryId); + return allCourses.Where(c => c.CentreId == centreId || c.AllCentres && includeAllCentreCourses).Select( + c => new CourseStatisticsWithAdminFieldResponseCounts( + c, + courseAdminFieldsService.GetCourseAdminFieldsWithAnswerCountsForCourse(c.CustomisationId, centreId) + ) + ); + } + public IEnumerable GetAllCoursesInCategoryForDelegate( int delegateId, int centreId, @@ -241,7 +257,7 @@ public void UpdateLearningPathwayDefaultsForCourse( int? categoryId ) { - var activeCourses = courseDataService.GetCoursesAvailableToCentreByCategory(centreId, categoryId) + var activeCourses = courseDataService.GetNonArchivedCoursesAvailableToCentreByCategory(centreId, categoryId) .Where(c => c.Active = true); var orderedCourses = activeCourses.OrderBy(c => c.ApplicationName); return orderedCourses.Select(c => (c.CustomisationId, c.CourseName)); @@ -335,7 +351,7 @@ public IEnumerable GetTopicsForCentreAndCentrallyManagedCourses(int cent public CentreCourseDetails GetCentreCourseDetails(int centreId, int? categoryId) { var (courses, categories, topics) = ( - GetCentreSpecificCourseStatisticsWithAdminFieldResponseCounts(centreId, categoryId), + GetNonArchivedCentreSpecificCourseStatisticsWithAdminFieldResponseCounts(centreId, categoryId), courseCategoriesDataService.GetCategoriesForCentreAndCentrallyManagedCourses(centreId) .Select(c => c.CategoryName), courseTopicsDataService.GetCourseTopicsAvailableAtCentre(centreId).Select(c => c.CourseTopic));