Skip to content

Commit b2a9745

Browse files
committed
TD-4469 Implements usage detail tab in course report spreadsheet
1 parent e6e21e5 commit b2a9745

File tree

4 files changed

+209
-8
lines changed

4 files changed

+209
-8
lines changed

DigitalLearningSolutions.Data/DataServices/ActivityDataService.cs

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,24 @@ IEnumerable<ActivityLog> GetFilteredActivity(
1616
int? courseCategoryId,
1717
int? customisationId
1818
);
19-
19+
int GetActivityDetailRowCount(
20+
int centreId,
21+
DateTime startDate,
22+
DateTime? endDate,
23+
int? jobGroupId,
24+
int? courseCategoryId,
25+
int? customisationId
26+
);
27+
IEnumerable<ActivityLogDetail> GetFilteredActivityDetail(
28+
int centreId,
29+
DateTime startDate,
30+
DateTime? endDate,
31+
int? jobGroupId,
32+
int? courseCategoryId,
33+
int? customisationId,
34+
int exportQueryRowLimit,
35+
int currentRun
36+
);
2037
DateTime? GetStartOfActivityForCentre(int centreId, int? courseCategoryId = null);
2138
}
2239

@@ -74,7 +91,101 @@ GROUP BY Cast(LogDate As Date), LogYear,
7491
}
7592
);
7693
}
77-
94+
public int GetActivityDetailRowCount(
95+
int centreId,
96+
DateTime startDate,
97+
DateTime? endDate,
98+
int? jobGroupId,
99+
int? courseCategoryId,
100+
int? customisationId
101+
)
102+
{
103+
return connection.QuerySingleOrDefault<int>(
104+
@"SELECT COUNT(1) FROM
105+
tActivityLog AS al
106+
WHERE(al.LogDate >= @startDate) AND(@endDate IS NULL OR
107+
al.LogDate <= @endDate) AND(al.CentreID = @centreId) AND(@jobGroupId IS NULL OR
108+
al.JobGroupID = @jobGroupId) AND(@customisationId IS NULL OR
109+
al.CustomisationID = @customisationId) AND(@courseCategoryId IS NULL OR
110+
al.CourseCategoryID = @courseCategoryId) AND(al.Registered = 1 OR
111+
al.Completed = 1 OR
112+
al.Evaluated = 1) AND EXISTS
113+
(SELECT ApplicationID
114+
FROM Applications AS ap
115+
WHERE (ApplicationID = al.ApplicationID) AND
116+
(DefaultContentTypeID<> 4))",
117+
new
118+
{
119+
centreId,
120+
startDate,
121+
endDate,
122+
jobGroupId,
123+
customisationId,
124+
courseCategoryId
125+
}
126+
);
127+
}
128+
public IEnumerable<ActivityLogDetail> GetFilteredActivityDetail(
129+
int centreId,
130+
DateTime startDate,
131+
DateTime? endDate,
132+
int? jobGroupId,
133+
int? courseCategoryId,
134+
int? customisationId,
135+
int exportQueryRowLimit,
136+
int currentRun
137+
)
138+
{
139+
return connection.Query<ActivityLogDetail>(
140+
@"SELECT al.LogID,
141+
al.LogDate,
142+
a.ApplicationName AS CourseName,
143+
c.CustomisationName,
144+
u.FirstName,
145+
u.LastName,
146+
u.PrimaryEmail,
147+
da.CandidateNumber AS DelegateId,
148+
da.Answer1,
149+
da.Answer2,
150+
da.Answer3,
151+
da.Answer4,
152+
da.Answer5,
153+
da.Answer6,
154+
al.Registered,
155+
al.Completed,
156+
al.Evaluated
157+
FROM Applications AS a INNER JOIN
158+
tActivityLog AS al ON a.ApplicationID = al.ApplicationID INNER JOIN
159+
Users AS u INNER JOIN
160+
DelegateAccounts AS da ON u.ID = da.UserID ON al.CandidateID = da.ID INNER JOIN
161+
Customisations AS c ON al.CustomisationID = c.CustomisationID
162+
WHERE (al.LogDate >= @startDate) AND (@endDate IS NULL OR
163+
al.LogDate <= @endDate) AND (al.CentreID = @centreId) AND (@jobGroupId IS NULL OR
164+
al.JobGroupID = @jobGroupId) AND (@customisationId IS NULL OR
165+
al.CustomisationID = @customisationId) AND (@courseCategoryId IS NULL OR
166+
al.CourseCategoryID = @courseCategoryId) AND (al.Registered = 1 OR
167+
al.Completed = 1 OR
168+
al.Evaluated = 1) AND EXISTS
169+
(SELECT ApplicationID
170+
FROM Applications AS ap
171+
WHERE (ApplicationID = al.ApplicationID) AND (DefaultContentTypeID <> 4))
172+
ORDER BY al.LogDate DESC
173+
OFFSET @exportQueryRowLimit * (@currentRun - 1) ROWS
174+
FETCH NEXT @exportQueryRowLimit ROWS ONLY"
175+
,
176+
new
177+
{
178+
centreId,
179+
startDate,
180+
endDate,
181+
jobGroupId,
182+
customisationId,
183+
courseCategoryId,
184+
exportQueryRowLimit,
185+
currentRun
186+
}
187+
);
188+
}
78189
public DateTime? GetStartOfActivityForCentre(int centreId, int? courseCategoryId = null)
79190
{
80191
return connection.QuerySingleOrDefault<DateTime?>(
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace DigitalLearningSolutions.Data.Models.TrackingSystem
2+
{
3+
using System;
4+
5+
public class ActivityLogDetail
6+
{
7+
public DateTime LogDate { get; set; }
8+
public string? CourseName { get; set; }
9+
public string? CustomisationName { get; set; }
10+
public string? FirstName { get; set; }
11+
public string? LastName { get; set; }
12+
public string? PrimaryEmail { get; set; }
13+
public string? DelegateId { get; set; }
14+
public string? Answer1 { get; set; }
15+
public string? Answer2 { get; set; }
16+
public string? Answer3 { get; set; }
17+
public string? Answer4 { get; set; }
18+
public string? Answer5 { get; set; }
19+
public string? Answer6 { get; set; }
20+
public bool Registered { get; set; }
21+
public bool Completed { get; set; }
22+
public bool Evaluated { get; set; }
23+
}
24+
}

DigitalLearningSolutions.Web.Tests/Services/ActivityServiceTests.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Configuration;
56
using System.IO;
67
using System.Linq;
78
using ClosedXML.Excel;
@@ -18,6 +19,7 @@
1819
using FluentAssertions;
1920
using FluentAssertions.Execution;
2021
using NUnit.Framework;
22+
using Microsoft.Extensions.Configuration;
2123

2224
public class ActivityServiceTests
2325
{
@@ -33,7 +35,8 @@ public class ActivityServiceTests
3335
private ICommonService commonService = null!;
3436
private IClockUtility clockUtility = null!;
3537
private IReportFilterService reportFilterService = null!;
36-
38+
private IConfiguration configuration = null!;
39+
private ICentreRegistrationPromptsService registrationPromptsService = null!;
3740
[SetUp]
3841
public void SetUp()
3942
{
@@ -46,12 +49,16 @@ public void SetUp()
4649
selfAssessmentDataService = A.Fake<ISelfAssessmentDataService>();
4750
commonService = A.Fake<ICommonService>();
4851
clockUtility = A.Fake<IClockUtility>();
52+
configuration = A.Fake<IConfiguration>();
53+
registrationPromptsService = A.Fake<ICentreRegistrationPromptsService>();
4954
activityService = new ActivityService(
5055
activityDataService,
5156
jobGroupsDataService,
5257
courseCategoriesDataService,
5358
courseDataService,
54-
clockUtility
59+
clockUtility,
60+
configuration,
61+
registrationPromptsService
5562
);
5663
reportFilterService = new ReportFilterService(
5764
courseCategoriesDataService,

DigitalLearningSolutions.Web/Services/ActivityService.cs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
using DigitalLearningSolutions.Data.DataServices;
99
using DigitalLearningSolutions.Data.Enums;
1010
using DigitalLearningSolutions.Data.Helpers;
11-
using DigitalLearningSolutions.Data.Models.Courses;
1211
using DigitalLearningSolutions.Data.Models.TrackingSystem;
1312
using DigitalLearningSolutions.Data.Utilities;
13+
using Microsoft.Extensions.Configuration;
1414

1515
public interface IActivityService
1616
{
@@ -27,28 +27,44 @@ int centreId
2727

2828
public class ActivityService : IActivityService
2929
{
30-
private const string SheetName = "Usage Statistics";
30+
private const string SheetName = "Usage summary";
3131
private static readonly XLTableTheme TableTheme = XLTableTheme.TableStyleLight9;
3232
private readonly IActivityDataService activityDataService;
3333
private readonly ICourseCategoriesDataService courseCategoriesDataService;
3434
private readonly ICourseDataService courseDataService;
3535
private readonly IJobGroupsDataService jobGroupsDataService;
3636
private readonly IClockUtility clockUtility;
37+
private readonly IConfiguration configuration;
38+
private readonly ICentreRegistrationPromptsService registrationPromptsService;
3739

3840
public ActivityService(
3941
IActivityDataService activityDataService,
4042
IJobGroupsDataService jobGroupsDataService,
4143
ICourseCategoriesDataService courseCategoriesDataService,
4244
ICourseDataService courseDataService,
43-
IClockUtility clockUtility
45+
IClockUtility clockUtility,
46+
IConfiguration configuration,
47+
ICentreRegistrationPromptsService registrationPromptsService
4448
)
4549
{
4650
this.activityDataService = activityDataService;
4751
this.jobGroupsDataService = jobGroupsDataService;
4852
this.courseCategoriesDataService = courseCategoriesDataService;
4953
this.courseDataService = courseDataService;
5054
this.clockUtility = clockUtility;
55+
this.configuration = configuration;
56+
this.registrationPromptsService = registrationPromptsService;
5157
}
58+
public int GetActivityDetailRowCount(int centreId, ActivityFilterData filterData)
59+
{
60+
return activityDataService.GetActivityDetailRowCount(centreId,
61+
filterData.StartDate,
62+
filterData.EndDate,
63+
filterData.JobGroupId,
64+
filterData.CourseCategoryId,
65+
filterData.CustomisationId);
66+
}
67+
5268
public IEnumerable<PeriodOfActivity> GetFilteredActivity(int centreId, ActivityFilterData filterData)
5369
{
5470
var activityData = activityDataService
@@ -146,7 +162,7 @@ public byte[] GetActivityDataFileForCentre(int centreId, ActivityFilterData filt
146162
var table = sheet.Cell(1, 1).InsertTable(workbookData);
147163
table.Theme = TableTheme;
148164
sheet.Columns().AdjustToContents();
149-
165+
AddActivityDetailSheet(workbook, centreId, filterData);
150166
using var stream = new MemoryStream();
151167
workbook.SaveAs(stream);
152168
return stream.ToArray();
@@ -207,6 +223,49 @@ ReportInterval interval
207223
)
208224
);
209225
}
226+
private void AddActivityDetailSheet(XLWorkbook workbook, int centreId, ActivityFilterData filterData)
227+
{
228+
var itemsPerPage = Data.Extensions.ConfigurationExtensions.GetExportQueryRowLimit(configuration);
229+
var resultCount = GetActivityDetailRowCount(centreId, filterData);
230+
int totalRun = (int)(resultCount / itemsPerPage) + ((resultCount % itemsPerPage) > 0 ? 1 : 0);
231+
int currentRun = 1;
232+
List<ActivityLogDetail> activityLogDetails = new List<ActivityLogDetail>();
233+
while (totalRun >= currentRun)
234+
{
235+
activityLogDetails.AddRange(activityDataService.GetFilteredActivityDetail(
236+
centreId,
237+
filterData.StartDate,
238+
filterData.EndDate,
239+
filterData.JobGroupId,
240+
filterData.CourseCategoryId,
241+
filterData.CustomisationId,
242+
itemsPerPage,
243+
currentRun));
244+
currentRun++;
245+
}
246+
var customRegistrationPrompts = registrationPromptsService.GetCentreRegistrationPromptsByCentreId(centreId);
247+
var sheet = workbook.Worksheets.Add("Usage detail");
248+
var table = sheet.Cell(1, 1).InsertTable(activityLogDetails);
249+
table.Theme = TableTheme;
250+
table.Field(0).Name = "Date";
251+
foreach (var prompt in customRegistrationPrompts.CustomPrompts)
252+
{
253+
var promptName = prompt.PromptText;
254+
var fieldNum = prompt.RegistrationField.ToString().Last();
255+
var promptLabel = "Answer" + fieldNum;
256+
table.Field(promptLabel).Name = promptName;
257+
}
258+
for (int i = 1; i < 7; i++)
259+
{
260+
var answerLabel = "Answer" + i.ToString();
261+
262+
if (table.Fields.Any(f => f.Name == answerLabel))
263+
{
264+
table.Field(answerLabel).Delete();
265+
}
266+
}
267+
sheet.Columns().AdjustToContents();
268+
}
210269
private static int GetFirstMonthOfQuarter(int quarter)
211270
{
212271
return quarter * 3 - 2;

0 commit comments

Comments
 (0)