Skip to content

Commit 8772faf

Browse files
authored
Merge pull request #2159 from TechnologyEnhancedLearning/Develop/Features/TD-2181_DelegateGroupsListSQLFilteringPaginationRebase
TD-2181 Delegate groups list refactored sql search,sort and pagination
2 parents 9cc8974 + e5e88d7 commit 8772faf

File tree

11 files changed

+620
-433
lines changed

11 files changed

+620
-433
lines changed

DigitalLearningSolutions.Data.Tests/DataServices/GroupsDataServiceTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public void GetGroupsForCentre_returns_expected_groups()
5555
};
5656

5757
// When
58-
var result = groupsDataService.GetGroupsForCentre(101).ToList();
58+
var result = groupsDataService.GetGroupsForCentre(centreId: 101).ToList();
5959

6060
// Then
6161
using (new AssertionScope())

DigitalLearningSolutions.Data/DataServices/GroupsDataService.cs

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
namespace DigitalLearningSolutions.Data.DataServices
22
{
3+
using Dapper;
4+
using DigitalLearningSolutions.Data.Helpers;
5+
using DigitalLearningSolutions.Data.Models.DelegateGroups;
36
using System;
47
using System.Collections.Generic;
58
using System.Data;
69
using System.Linq;
7-
using Dapper;
8-
using DigitalLearningSolutions.Data.Models.Centres;
9-
using DigitalLearningSolutions.Data.Models.Courses;
10-
using DigitalLearningSolutions.Data.Models.DelegateGroups;
1110

1211
public interface IGroupsDataService
1312
{
1413
IEnumerable<Group> GetGroupsForCentre(int centreId);
1514

15+
(IEnumerable<Group>, int) GetGroupsForCentre(
16+
string? search,
17+
int? offset,
18+
int? rows,
19+
string? sortBy,
20+
string? sortDirection,
21+
int? centreId,
22+
string? filterAddedBy,
23+
string? filterLinkedField
24+
);
25+
1626
IEnumerable<GroupDelegate> GetGroupDelegates(int groupId);
1727

1828
IEnumerable<GroupCourse> GetGroupCoursesVisibleToCentre(int centreId);
@@ -84,10 +94,13 @@ void AddDelegatesWithMatchingAnswersToGroup(
8494
public class GroupsDataService : IGroupsDataService
8595
{
8696
private const string CourseCountSql = @"SELECT COUNT(*)
87-
FROM GroupCustomisations AS gc
88-
JOIN Customisations AS c ON c.CustomisationID = gc.CustomisationID
89-
INNER JOIN dbo.CentreApplications AS ca ON ca.ApplicationID = c.ApplicationID
90-
INNER JOIN dbo.Applications AS ap ON ap.ApplicationID = ca.ApplicationID
97+
FROM GroupCustomisations AS gc WITH (NOLOCK)
98+
JOIN Customisations AS c WITH (NOLOCK)
99+
ON c.CustomisationID = gc.CustomisationID
100+
INNER JOIN dbo.CentreApplications AS ca WITH (NOLOCK)
101+
ON ca.ApplicationID = c.ApplicationID
102+
INNER JOIN dbo.Applications AS ap WITH (NOLOCK)
103+
ON ap.ApplicationID = ca.ApplicationID
91104
WHERE gc.GroupID = g.GroupID
92105
AND ca.CentreId = @centreId
93106
AND gc.InactivatedDate IS NULL
@@ -172,9 +185,11 @@ AND NOT EXISTS (SELECT * FROM GroupCustomisations AS GCInner
172185
END AS LinkedToFieldName,
173186
AddNewRegistrants,
174187
SyncFieldChanges
175-
FROM Groups AS g
176-
JOIN AdminUsers AS au ON au.AdminID = g.CreatedByAdminUserID
177-
JOIN Centres AS c ON c.CentreID = g.CentreID
188+
FROM Groups AS g WITH (NOLOCK)
189+
JOIN AdminUsers AS au WITH (NOLOCK)
190+
ON au.AdminID = g.CreatedByAdminUserID
191+
JOIN Centres AS c WITH (NOLOCK)
192+
ON c.CentreID = g.CentreID
178193
WHERE RemovedDate IS NULL";
179194

180195
public GroupsDataService(IDbConnection connection)
@@ -190,6 +205,72 @@ public IEnumerable<Group> GetGroupsForCentre(int centreId)
190205
);
191206
}
192207

208+
public (IEnumerable<Group>, int) GetGroupsForCentre(
209+
string? search = "",
210+
int? offset = 0,
211+
int? rows = 10,
212+
string? sortBy = "",
213+
string? sortDirection = "",
214+
int? centreId = 0,
215+
string? filterAddedBy = "",
216+
string? filterLinkedField = "")
217+
{
218+
if (!string.IsNullOrEmpty(search))
219+
{
220+
search = search.Trim();
221+
}
222+
223+
var rootSqlQuery = @$"{groupsSql} AND g.CentreId = @centreId";
224+
225+
var filtersClause = "";
226+
if (!string.IsNullOrEmpty(filterAddedBy))
227+
{
228+
filtersClause += @$"AND (g.CreatedByAdminUserID = " + filterAddedBy + ") ";
229+
}
230+
if (!string.IsNullOrEmpty(filterLinkedField))
231+
{
232+
filtersClause += @$"AND (LinkedToField = " + filterLinkedField + ") ";
233+
}
234+
235+
var searchClause = "AND(COALESCE(GroupLabel, '') LIKE N'%" + search + "%' OR COALESCE(GroupDescription, '') LIKE N'%" + search + "%')";
236+
237+
var sortOrder = sortDirection == "Ascending" ? " ASC " : " DESC ";
238+
239+
if (string.IsNullOrEmpty(sortBy) || sortBy == DefaultSortByOptions.Name.PropertyName)
240+
{
241+
sortBy = "GroupLabel";
242+
}
243+
var orderByClause = " ORDER BY " + sortBy + " " + sortOrder;
244+
245+
var paginationClause = " OFFSET " + offset.ToString() + " ROWS FETCH NEXT " + rows + " ROWS ONLY ";
246+
247+
var groupsForCentreQuery = rootSqlQuery + " " + searchClause + " " + filtersClause + " " + orderByClause + " " + paginationClause;
248+
249+
IEnumerable<Group> groups = connection.Query<Group>(
250+
groupsForCentreQuery,
251+
new { centreId },
252+
commandTimeout: 3000
253+
);
254+
255+
int resultCount = connection.ExecuteScalar<int>(
256+
@$"SELECT COUNT(g.GroupID) AS Matches
257+
FROM Groups AS g WITH (NOLOCK)
258+
JOIN AdminUsers AS au WITH (NOLOCK)
259+
ON au.AdminID = g.CreatedByAdminUserID
260+
JOIN Centres AS c WITH (NOLOCK)
261+
ON c.CentreID = g.CentreID
262+
WHERE RemovedDate IS NULL
263+
AND g.CentreId = @centreId
264+
AND (COALESCE(GroupLabel, '') LIKE N'%' + @search + N'%'
265+
OR COALESCE(GroupDescription, '') LIKE N'%' + @search + N'%')"
266+
+ filtersClause,
267+
new { centreId, search },
268+
commandTimeout: 3000
269+
);
270+
271+
return (groups, resultCount);
272+
}
273+
193274
public IEnumerable<GroupDelegate> GetGroupDelegates(int groupId)
194275
{
195276
return connection.Query<GroupDelegate>(
@@ -420,25 +501,9 @@ int centreId
420501
{
421502
return connection.QuerySingle<int>(
422503
@"GroupCustomisation_Add_V2",
423-
new { groupId, customisationId, centreId, completeWithinMonths, adminUserID = addedByAdminUserId, cohortLearners, supervisorAdminId=supervisorAdminId ?? 0 },
504+
new { groupId, customisationId, centreId, completeWithinMonths, adminUserID = addedByAdminUserId, cohortLearners, supervisorAdminId = supervisorAdminId ?? 0 },
424505
commandType: CommandType.StoredProcedure
425506
);
426-
//return connection.QuerySingle<int>(
427-
// @"INSERT INTO GroupCustomisations
428-
// (GroupID, CustomisationID, CompleteWithinMonths, AddedByAdminUserID, CohortLearners, SupervisorAdminID)
429-
// OUTPUT Inserted.GroupCustomisationId
430-
// VALUES
431-
// (@groupId, @customisationId, @completeWithinMonths, @addedByAdminUserId, @cohortLearners, @supervisorAdminID)",
432-
// new
433-
// {
434-
// groupId,
435-
// customisationId,
436-
// completeWithinMonths,
437-
// addedByAdminUserId,
438-
// cohortLearners,
439-
// supervisorAdminId,
440-
// }
441-
//);
442507
}
443508

444509
public void AddDelegatesWithMatchingAnswersToGroup(

DigitalLearningSolutions.Web.Tests/Controllers/TrackingSystem/Delegates/DelegateGroupsControllerTests.cs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
namespace DigitalLearningSolutions.Web.Tests.Controllers.TrackingSystem.Delegates
1+
using System.Collections.Generic;
2+
using DigitalLearningSolutions.Data.Enums;
3+
using DigitalLearningSolutions.Data.Models.CustomPrompts;
4+
using DigitalLearningSolutions.Data.Models.DelegateGroups;
5+
using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate;
6+
using DigitalLearningSolutions.Data.Tests.TestHelpers;
7+
using DigitalLearningSolutions.Web.Controllers.TrackingSystem.Delegates;
8+
using DigitalLearningSolutions.Web.Helpers;
9+
using DigitalLearningSolutions.Web.Services;
10+
using DigitalLearningSolutions.Web.Tests.ControllerHelpers;
11+
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates.DelegateGroups;
12+
using FakeItEasy;
13+
using FluentAssertions;
14+
using FluentAssertions.AspNetCore.Mvc;
15+
using FluentAssertions.Execution;
16+
using Microsoft.AspNetCore.Http;
17+
using Microsoft.AspNetCore.Mvc.Rendering;
18+
using NUnit.Framework;
19+
using DigitalLearningSolutions.Data.Models.DelegateGroups;
20+
21+
namespace DigitalLearningSolutions.Web.Tests.Controllers.TrackingSystem.Delegates
222
{
3-
using System.Collections.Generic;
4-
using DigitalLearningSolutions.Data.Enums;
5-
using DigitalLearningSolutions.Data.Models.CustomPrompts;
6-
using DigitalLearningSolutions.Data.Models.DelegateGroups;
7-
using DigitalLearningSolutions.Data.Models.SearchSortFilterPaginate;
8-
using DigitalLearningSolutions.Data.Tests.TestHelpers;
9-
using DigitalLearningSolutions.Data.Utilities;
10-
using DigitalLearningSolutions.Web.Controllers.TrackingSystem.Delegates;
11-
using DigitalLearningSolutions.Web.Helpers;
12-
using DigitalLearningSolutions.Web.Services;
13-
using DigitalLearningSolutions.Web.Tests.ControllerHelpers;
14-
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Delegates.DelegateGroups;
15-
using FakeItEasy;
16-
using FluentAssertions;
17-
using FluentAssertions.AspNetCore.Mvc;
18-
using FluentAssertions.Execution;
19-
using Microsoft.AspNetCore.Http;
20-
using Microsoft.AspNetCore.Mvc.Rendering;
21-
using NUnit.Framework;
22-
2323
public class DelegateGroupsControllerTests
2424
{
2525
private const string CookieName = "DelegateGroupsFilter";
2626
private ICentreRegistrationPromptsService centreRegistrationPromptsService = null!;
2727
private DelegateGroupsController delegateGroupsController = null!;
2828
private IGroupsService groupsService = null!;
29+
private IPaginateService paginateService = null;
2930
private HttpRequest httpRequest = null!;
3031
private HttpResponse httpResponse = null!;
3132
private ISearchSortFilterPaginateService searchSortFilterPaginateService = null!;
@@ -36,15 +37,16 @@ public void Setup()
3637
centreRegistrationPromptsService = A.Fake<ICentreRegistrationPromptsService>();
3738
groupsService = A.Fake<IGroupsService>();
3839
searchSortFilterPaginateService = A.Fake<ISearchSortFilterPaginateService>();
39-
40+
paginateService = A.Fake<IPaginateService>();
4041
httpRequest = A.Fake<HttpRequest>();
4142
httpResponse = A.Fake<HttpResponse>();
4243
const string cookieValue = "LinkedToField|LinkedToField|0";
4344

4445
delegateGroupsController = new DelegateGroupsController(
4546
centreRegistrationPromptsService,
4647
groupsService,
47-
searchSortFilterPaginateService
48+
searchSortFilterPaginateService,
49+
paginateService
4850
)
4951
.WithMockHttpContext(httpRequest, CookieName, cookieValue, httpResponse)
5052
.WithMockUser(true)
@@ -61,13 +63,29 @@ public void Index_calls_expected_methods_and_returns_view()
6163
// Then
6264
using (new AssertionScope())
6365
{
64-
A.CallTo(() => groupsService.GetGroupsForCentre(A<int>._)).MustHaveHappened();
66+
A.CallTo(() => groupsService.GetGroupsForCentre(
67+
A<string>._,
68+
A<int>._,
69+
A<int>._,
70+
A<string>._,
71+
A<string>._,
72+
A<int>._,
73+
A<string>._,
74+
A<string>._
75+
)).MustHaveHappened();
76+
6577
A.CallTo(
66-
() => searchSortFilterPaginateService.SearchFilterSortAndPaginate(
67-
A<IEnumerable<Group>>._,
68-
A<SearchSortFilterAndPaginateOptions>._
78+
() => paginateService.Paginate(
79+
A<IEnumerable<Data.Models.DelegateGroups.Group>>._,
80+
A<int>._,
81+
A<PaginationOptions>._,
82+
A<Data.Models.SearchSortFilterPaginate.FilterOptions>._,
83+
A<string>._,
84+
A<string>._,
85+
A<string>._
6986
)
7087
).MustHaveHappened();
88+
7189
A.CallTo(
7290
() => httpResponse.Cookies.Append(
7391
CookieName,

DigitalLearningSolutions.Web.Tests/ServiceFilter/VerifyAdminUserCanAccessGroupCourseTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ public void SetUp()
2727
var delegateGroupsController = new DelegateGroupsController(
2828
A.Fake<ICentreRegistrationPromptsService>(),
2929
A.Fake<IGroupsService>(),
30-
A.Fake<ISearchSortFilterPaginateService>()
30+
A.Fake<ISearchSortFilterPaginateService>(),
31+
A.Fake<IPaginateService>()
3132
).WithDefaultContext().WithMockUser(true);
3233
context = ContextHelper.GetDefaultActionExecutingContext(delegateGroupsController);
3334
context.RouteData.Values["groupId"] = GroupId;

DigitalLearningSolutions.Web.Tests/ServiceFilter/VerifyAdminUserCanAccessGroupTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ private ActionExecutingContext GetDefaultContext()
5656
var delegateGroupsController = new DelegateGroupsController(
5757
A.Fake<ICentreRegistrationPromptsService>(),
5858
A.Fake<IGroupsService>(),
59-
A.Fake<ISearchSortFilterPaginateService>()
59+
A.Fake<ISearchSortFilterPaginateService>(),
60+
A.Fake<IPaginateService>()
6061
).WithDefaultContext().WithMockUser(true, UserCentreId);
6162
var context = ContextHelper.GetDefaultActionExecutingContext(delegateGroupsController);
6263
context.RouteData.Values["groupId"] = GroupId;

0 commit comments

Comments
 (0)