Skip to content

Commit 0dcb7db

Browse files
committed
TD-2183- feature 'Refactor the Tracking System - Course delegates list to use SQL filtering and pagination' added.
1 parent 8674cf5 commit 0dcb7db

File tree

10 files changed

+419
-133
lines changed

10 files changed

+419
-133
lines changed

DigitalLearningSolutions.Data.Tests/TestHelpers/SearchSortFilterAndPaginateTestHelper.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,50 @@ public static void GivenACallToSearchSortFilterPaginateServiceReturnsResult<T>(
4242
}
4343
);
4444
}
45+
46+
public static void GivenACallToPaginateServiceReturnsResult<T>(
47+
IPaginateService paginateService,
48+
int searchresultCount,
49+
string searchString,
50+
string sortBy,
51+
string sortDirection
52+
) where T : BaseSearchableItem
53+
{
54+
A.CallTo(
55+
() => paginateService.Paginate(
56+
A<IEnumerable<T>>._,
57+
A<int>._,
58+
A<PaginationOptions>._,
59+
A<FilterOptions>._,
60+
A<string>._,
61+
A<string>._,
62+
A<string>._
63+
)
64+
).ReturnsLazily(
65+
x =>
66+
{
67+
var items = x.Arguments.Get<IEnumerable<T>>("items")?.ToList() ??
68+
new List<T>();
69+
var options =
70+
x.Arguments.Get<PaginationOptions>("paginationOptions");
71+
var filterOptions =
72+
x.Arguments.Get<FilterOptions>("filterOptions");
73+
return new SearchSortFilterPaginationResult<T>(
74+
new PaginationResult<T>(
75+
items,
76+
options!.PageNumber,
77+
1,
78+
options.ItemsPerPage,
79+
searchresultCount,
80+
false
81+
),
82+
searchString,
83+
sortBy,
84+
sortDirection,
85+
filterOptions!.FilterString
86+
);
87+
}
88+
);
89+
}
4590
}
4691
}

DigitalLearningSolutions.Data/DataServices/CourseDataService.cs

Lines changed: 152 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ int EnrolOnActivitySelfAssessment(int selfAssessmentId, int candidateId, int sup
105105

106106
public (IEnumerable<CourseStatistics>, int) GetCourseStatisticsAtCentre(string searchString, int offSet, int itemsPerPage, string sortBy, string sortDirection, int centreId, int? categoryId, bool allCentreCourses, bool? hideInLearnerPortal,
107107
string isActive, string categoryName, string courseTopic, string hasAdminFields);
108+
109+
public (IEnumerable<DelegateCourseInfo>, int) GetDelegateCourseInfosPerPageForCourse(string searchString, int offSet, int itemsPerPage, string sortBy, string sortDirection,
110+
int customisationId, int centreId, bool? isDelegateActive, bool? isProgressLocked, bool? removed, bool? hasCompleted, string? answer1, string? answer2, string? answer3);
108111
}
109112

110113
public class CourseDataService : ICourseDataService
@@ -149,18 +152,36 @@ AND RemovedDate IS NULL
149152

150153
private const string DelegateAllAttemptsQuery =
151154
@"(SELECT COUNT(aa.AssessAttemptID)
152-
FROM dbo.AssessAttempts AS aa
153-
INNER JOIN dbo.DelegateAccounts AS dacc ON dacc.ID = aa.CandidateID
155+
FROM dbo.AssessAttempts AS aa WITH (NOLOCK)
156+
INNER JOIN dbo.DelegateAccounts AS dacc WITH (NOLOCK) ON dacc.ID = aa.CandidateID
154157
WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] IS NOT NULL
155158
AND dacc.ID = da.ID) AS AllAttempts";
156159

157160
private const string DelegateAttemptsPassedQuery =
158161
@"(SELECT COUNT(aa.AssessAttemptID)
159-
FROM dbo.AssessAttempts AS aa
160-
INNER JOIN dbo.DelegateAccounts AS dacc ON dacc.ID = aa.CandidateID
162+
FROM dbo.AssessAttempts AS aa WITH (NOLOCK)
163+
INNER JOIN dbo.DelegateAccounts AS dacc WITH (NOLOCK) ON dacc.ID = aa.CandidateID
161164
WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] = 1
162165
AND dacc.ID = da.ID) AS AttemptsPassed";
163166

167+
private const string DelegatePassRateQuery =
168+
@"CASE
169+
WHEN (SELECT COUNT(aa.AssessAttemptID)
170+
FROM dbo.AssessAttempts AS aa WITH (NOLOCK)
171+
INNER JOIN dbo.DelegateAccounts AS dacc WITH (NOLOCK) ON dacc.ID = aa.CandidateID
172+
WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] IS NOT NULL
173+
AND dacc.ID = da.ID) = 0 THEN 0
174+
ELSE ROUND((100 * (CAST((SELECT COUNT(aa.AssessAttemptID)
175+
FROM dbo.AssessAttempts AS aa WITH (NOLOCK)
176+
INNER JOIN dbo.DelegateAccounts AS dacc WITH (NOLOCK) ON dacc.ID = aa.CandidateID
177+
WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] = 1
178+
AND dacc.ID = da.ID) AS FLOAT) / (SELECT COUNT(aa.AssessAttemptID)
179+
FROM dbo.AssessAttempts AS aa WITH (NOLOCK)
180+
INNER JOIN dbo.DelegateAccounts AS dacc WITH (NOLOCK) ON dacc.ID = aa.CandidateID
181+
WHERE aa.CustomisationID = cu.CustomisationID AND aa.[Status] IS NOT NULL
182+
AND dacc.ID = da.ID))),2,0)
183+
END AS PassRate";
184+
164185
private const string TutorialWithLearningCountQuery =
165186
@"SELECT COUNT(ct.TutorialID)
166187
FROM CustomisationTutorials AS ct
@@ -763,6 +784,133 @@ FROM CentreApplications cap
763784
);
764785
}
765786

787+
public (IEnumerable<DelegateCourseInfo>, int) GetDelegateCourseInfosPerPageForCourse(string searchString, int offSet, int itemsPerPage, string sortBy, string sortDirection,
788+
int customisationId, int centreId, bool? isDelegateActive, bool? isProgressLocked, bool? removed, bool? hasCompleted, string? answer1, string? answer2, string? answer3)
789+
{
790+
791+
var selectColumnQuery = $@"SELECT
792+
cu.CustomisationID AS CustomisationId,
793+
cu.CustomisationName,
794+
ap.ApplicationName,
795+
ap.CourseCategoryID,
796+
cu.IsAssessed,
797+
cu.CentreID AS CustomisationCentreId,
798+
cu.Active AS IsCourseActive,
799+
cu.AllCentres AS AllCentresCourse,
800+
pr.ProgressId,
801+
pr.PLLocked as IsProgressLocked,
802+
pr.SubmittedTime AS LastUpdated,
803+
pr.CompleteByDate AS CompleteBy,
804+
pr.RemovedDate,
805+
pr.Completed AS Completed,
806+
pr.Evaluated AS Evaluated,
807+
pr.LoginCount,
808+
pr.Duration AS LearningTime,
809+
pr.DiagnosticScore,
810+
LTRIM(RTRIM(pr.Answer1)) AS Answer1,
811+
LTRIM(RTRIM(pr.Answer2)) AS Answer2,
812+
LTRIM(RTRIM(pr.Answer3)) AS Answer3,
813+
{DelegateAllAttemptsQuery},
814+
{DelegateAttemptsPassedQuery},
815+
pr.FirstSubmittedTime AS Enrolled,
816+
pr.EnrollmentMethodID AS EnrolmentMethodId,
817+
uEnrolledBy.FirstName AS EnrolledByForename,
818+
uEnrolledBy.LastName AS EnrolledBySurname,
819+
aaEnrolledBy.Active AS EnrolledByAdminActive,
820+
aaSupervisor.ID AS SupervisorAdminId,
821+
uSupervisor.FirstName AS SupervisorForename,
822+
uSupervisor.LastName AS SupervisorSurname,
823+
aaSupervisor.Active AS SupervisorAdminActive,
824+
da.ID AS DelegateId,
825+
da.CandidateNumber AS CandidateNumber,
826+
u.FirstName AS DelegateFirstName,
827+
u.LastName AS DelegateLastName,
828+
COALESCE(ucd.Email, u.PrimaryEmail) AS DelegateEmail,
829+
da.Active AS IsDelegateActive,
830+
u.HasBeenPromptedForPrn,
831+
u.ProfessionalRegistrationNumber,
832+
da.CentreID AS DelegateCentreId,
833+
ap.ArchivedDate AS CourseArchivedDate,
834+
{DelegatePassRateQuery}";
835+
836+
var fromTableQuery = $@" FROM Customisations cu WITH (NOLOCK)
837+
INNER JOIN Applications AS ap WITH (NOLOCK) ON ap.ApplicationID = cu.ApplicationID
838+
INNER JOIN Progress AS pr WITH (NOLOCK) ON pr.CustomisationID = cu.CustomisationID
839+
LEFT OUTER JOIN AdminAccounts AS aaSupervisor WITH (NOLOCK) ON aaSupervisor.ID = pr.SupervisorAdminId
840+
LEFT OUTER JOIN Users AS uSupervisor WITH (NOLOCK) ON uSupervisor.ID = aaSupervisor.UserID
841+
LEFT OUTER JOIN AdminAccounts AS aaEnrolledBy WITH (NOLOCK) ON aaEnrolledBy.ID = pr.EnrolledByAdminID
842+
LEFT OUTER JOIN Users AS uEnrolledBy WITH (NOLOCK) ON uEnrolledBy.ID = aaEnrolledBy.UserID
843+
INNER JOIN DelegateAccounts AS da WITH (NOLOCK) ON da.ID = pr.CandidateID
844+
INNER JOIN Users AS u WITH (NOLOCK) ON u.ID = da.UserID
845+
LEFT JOIN UserCentreDetails AS ucd WITH (NOLOCK) ON ucd.UserID = da.UserID AND ucd.centreID = da.CentreID
846+
847+
WHERE (cu.CentreID = @centreId OR
848+
(cu.AllCentres = 1 AND
849+
EXISTS (SELECT CentreApplicationID
850+
FROM CentreApplications cap
851+
WHERE cap.ApplicationID = cu.ApplicationID AND
852+
cap.CentreID = @centreID AND
853+
cap.Active = 1)))
854+
AND da.CentreID = @centreId
855+
AND pr.CustomisationID = @customisationId
856+
AND ap.DefaultContentTypeID <> 4
857+
858+
AND ( u.FirstName + ' ' + u.LastName + ' ' + COALESCE(ucd.Email, u.PrimaryEmail) + ' ' + COALESCE(CandidateNumber, '') LIKE N'%' + @searchString + N'%')
859+
AND ((@isDelegateActive IS NULL) OR (@isDelegateActive = 1 AND (da.Active = 1)) OR (@isDelegateActive = 0 AND (da.Active = 0)))
860+
AND ((@isProgressLocked IS NULL) OR (@isProgressLocked = 1 AND (pr.PLLocked = 1)) OR (@isProgressLocked = 0 AND (pr.PLLocked = 0)))
861+
AND ((@removed IS NULL) OR (@removed = 1 AND (pr.RemovedDate IS NOT NULL)) OR (@removed = 0 AND (pr.RemovedDate IS NULL)))
862+
AND ((@hasCompleted IS NULL) OR (@hasCompleted = 1 AND pr.Completed IS NOT NULL) OR (@hasCompleted = 0 AND pr.Completed IS NULL))
863+
864+
AND ((@answer1 IS NULL) OR ((@answer1 = 'No option selected' OR @answer1 = 'FREETEXTBLANKVALUE') AND (pr.Answer1 IS NULL OR TRIM(pr.Answer1) = ''))
865+
OR (@answer1 = 'FREETEXTNOTBLANKVALUE' AND (pr.Answer1 IS NOT NULL OR pr.Answer1 = @answer1)))
866+
867+
AND ((@answer2 IS NULL) OR ((@answer2 = 'No option selected' OR @answer2 = 'FREETEXTBLANKVALUE') AND (pr.Answer2 IS NULL OR TRIM(pr.Answer2) = ''))
868+
OR (@answer2 = 'FREETEXTNOTBLANKVALUE' AND (pr.Answer2 IS NOT NULL OR pr.Answer2 = @answer2)))
869+
870+
AND ((@answer3 IS NULL) OR ((@answer3 = 'No option selected' OR @answer3 = 'FREETEXTBLANKVALUE') AND (pr.Answer3 IS NULL OR TRIM(pr.Answer3) = ''))
871+
OR (@answer3 = 'FREETEXTNOTBLANKVALUE' AND (pr.Answer3 IS NOT NULL OR pr.Answer3 = @answer3)))
872+
873+
AND COALESCE(ucd.Email, u.PrimaryEmail) LIKE '%_@_%.__%'";
874+
875+
string orderBy;
876+
string sortOrder;
877+
878+
if (sortDirection == "Ascending")
879+
sortOrder = " ASC ";
880+
else
881+
sortOrder = " DESC ";
882+
883+
if (sortBy == "SearchableName" || sortBy == "FullNameForSearchingSorting")
884+
orderBy = " ORDER BY LTRIM(u.LastName) " + sortOrder + ", LTRIM(u.FirstName) ";
885+
else
886+
orderBy = " ORDER BY "+ sortBy + sortOrder;
887+
888+
orderBy += " OFFSET " + offSet + " ROWS FETCH NEXT " + itemsPerPage + " ROWS ONLY ";
889+
890+
var mainSql = selectColumnQuery + fromTableQuery + orderBy;
891+
892+
IEnumerable<DelegateCourseInfo> delegateUserCard = connection.Query<DelegateCourseInfo>(
893+
mainSql,
894+
new
895+
{searchString, offSet, itemsPerPage, sortBy, sortDirection, customisationId,
896+
centreId, isDelegateActive, isProgressLocked, removed, hasCompleted, answer1, answer2, answer3
897+
},
898+
commandTimeout: 3000
899+
);
900+
901+
var delegateCountQuery = @$"SELECT COUNT(*) AS Matches " + fromTableQuery;
902+
903+
int ResultCount = connection.ExecuteScalar<int>(
904+
delegateCountQuery,
905+
new
906+
{searchString, offSet, itemsPerPage, sortBy, sortDirection, customisationId, centreId,
907+
isDelegateActive, isProgressLocked, removed, hasCompleted, answer1, answer2, answer3
908+
},
909+
commandTimeout: 3000
910+
);
911+
return (delegateUserCard, ResultCount);
912+
}
913+
766914
public CourseDetails? GetCourseDetailsFilteredByCategory(int customisationId, int centreId, int? categoryId)
767915
{
768916
return connection.Query<CourseDetails>(

0 commit comments

Comments
 (0)