Skip to content

Commit

Permalink
Miscellaneous admin and support improvements (#53)
Browse files Browse the repository at this point in the history
* Support can now view challenges

* Support can now observe challenges

* Number of Challenges Deployed added to participation reports

- All participation reports now show the number of challenges deployed across their specific type
- Empty/null string correction added to all participation reports for more accuracy

* Number of teams and teams with sessions added to participation reports

- Participation reports now track the number of teams in in each participation category and the number of teams with sessions

* Naming change in ReportService.cs

* Ticket support intervals made more flexible via config

- Ticket support intervals can be configured in AppSettings.cs
  - To add an interval, add a new line to ShiftStrings
- Ticket support exports now dynamically adapt to the number of intervals and are not locked to specific shifts

* Board counting made more accurate

- Single-sponsor teams counted more accurately
  - Teams of one or teams with only one sponsor included in each sponsor row of the sponsor report
- Multisponsor team counts shown on board

* Shortened label for timezone in support reports

* Shifts can now be adjusted within appsettings.conf

* Minor comment correction
  • Loading branch information
sei-jperrino authored Sep 13, 2022
1 parent cded73b commit b7ee413
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 73 deletions.
14 changes: 13 additions & 1 deletion src/Gameboard.Api/Extensions/DefaultsStartupExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,23 @@ string contentRootPath
if (File.Exists(certificateFile))
certificateTemplate = File.ReadAllText(certificateFile);
var shiftTimezone = defaults.ShiftTimezone.NotEmpty() ? defaults.ShiftTimezone : Defaults.ShiftTimezoneFallback;
var shiftStrings = defaults.ShiftStrings != null ? defaults.ShiftStrings : Defaults.ShiftStringsFallback;
var shifts = defaults.ShiftStrings != null ? new System.DateTimeOffset[shiftStrings.Length][] : Defaults.ShiftsFallback;
if (defaults.ShiftStrings != null) {
for (int i = 0; i < shiftStrings.Length; i++) {
shifts[i] = new System.DateTimeOffset[] { Defaults.ConvertTime(shiftStrings[i][0], shiftTimezone), Defaults.ConvertTime(shiftStrings[i][1], shiftTimezone) };
}
}
return new Defaults {
FeedbackTemplate = feedbackTemplate,
FeedbackTemplateFile = defaults.FeedbackTemplateFile,
CertificateTemplate = certificateTemplate,
CertificateTemplateFile = defaults.CertificateTemplateFile
CertificateTemplateFile = defaults.CertificateTemplateFile,
ShiftStrings = shiftStrings,
Shifts = shifts,
ShiftTimezone = shiftTimezone
};
});

Expand Down
16 changes: 11 additions & 5 deletions src/Gameboard.Api/Features/Challenge/ChallengeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ public async Task<ConsoleSummary> GetConsole([FromBody]ConsoleRequest model)
AuthorizeAny(
() => Actor.IsDirector,
() => Actor.IsObserver,
() => Actor.IsSupport,
() => isTeamMember
);

Expand Down Expand Up @@ -318,7 +319,8 @@ public async Task<List<ObserveChallenge>> FindConsoles([FromQuery]string gid)
{
AuthorizeAny(
() => Actor.IsDirector,
() => Actor.IsObserver
() => Actor.IsObserver,
() => Actor.IsSupport
);
return await ChallengeService.GetChallengeConsoles(gid);
}
Expand All @@ -329,7 +331,8 @@ public ConsoleActor[] GetConsoleActors([FromQuery]string gid)
{
AuthorizeAny(
() => Actor.IsDirector,
() => Actor.IsObserver
() => Actor.IsObserver,
() => Actor.IsSupport
);
return ChallengeService.GetConsoleActors(gid);
}
Expand All @@ -340,7 +343,8 @@ public ConsoleActor GetConsoleActor([FromQuery]string uid)
{
AuthorizeAny(
() => Actor.IsDirector,
() => Actor.IsObserver
() => Actor.IsObserver,
() => Actor.IsSupport
);
return ChallengeService.GetConsoleActor(uid);
}
Expand All @@ -355,7 +359,8 @@ public ConsoleActor GetConsoleActor([FromQuery]string uid)
public async Task<ChallengeSummary[]> List([FromQuery] SearchFilter model)
{
AuthorizeAny(
() => Actor.IsDirector
() => Actor.IsDirector,
() => Actor.IsSupport
);

return await ChallengeService.List(model);
Expand Down Expand Up @@ -388,7 +393,8 @@ public async Task<ChallengeOverview[]> ListByUser([FromQuery] ChallengeSearchFil
public async Task<ArchivedChallenge[]> ListArchived([FromQuery] SearchFilter model)
{
AuthorizeAny(
() => Actor.IsDirector
() => Actor.IsDirector,
() => Actor.IsSupport
);

return await ChallengeService.ListArchived(model);
Expand Down
82 changes: 63 additions & 19 deletions src/Gameboard.Api/Features/Report/ReportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,24 @@ public ReportController(
GameService gameService,
ChallengeSpecService challengeSpecService,
FeedbackService feedbackService,
TicketService ticketService
TicketService ticketService,
Defaults defaults
): base(logger, cache)
{
Service = service;
GameService = gameService;
FeedbackService = feedbackService;
ChallengeSpecService = challengeSpecService;
TicketService = ticketService;
Defaults = defaults;
}

ReportService Service { get; }
GameService GameService { get; }
FeedbackService FeedbackService { get; }
ChallengeSpecService ChallengeSpecService { get; }
TicketService TicketService { get; }
Defaults Defaults { get; }

[HttpGet("/api/report/userstats")]
[Authorize]
Expand Down Expand Up @@ -485,7 +488,7 @@ public async Task<ActionResult<FeedbackStats>> GetFeedbackStats([FromQuery] Feed
#region Support Stats
[HttpGet("/api/report/supportdaystats")]
[Authorize]
public async Task<ActionResult<TicketDayGroup[]>> GetTicketVolumeStats([FromQuery] TicketReportFilter model)
public async Task<ActionResult<TicketDayReport>> GetTicketVolumeStats([FromQuery] TicketReportFilter model)
{
AuthorizeAny(
() => Actor.IsObserver
Expand Down Expand Up @@ -605,23 +608,64 @@ public async Task<IActionResult> ExportTicketDayStats([FromQuery] TicketReportFi

var result = await Service.GetTicketVolume(model);

List<Tuple<string, string, string, string, string, string>> dayStats = new List<Tuple<string, string, string, string, string, string>>();
dayStats.Add(new Tuple<string, string, string, string, string, string>("Date", "Day of Week", "Shift 1 Count", "Shift 2 Count", "Outside of Shifts Count", "Total Created"));

int[] sums = new int[4];

foreach (TicketDayGroup group in result)
{
dayStats.Add(new Tuple<string, string, string, string, string, string>(group.Date, group.DayOfWeek, group.Shift1Count.ToString(), group.Shift2Count.ToString(), group.OutsideShiftCount.ToString(), group.Count.ToString()));
sums[0] += group.Shift1Count;
sums[1] += group.Shift2Count;
sums[2] += group.OutsideShiftCount;
sums[3] += group.Count;
// Create the file result early on so we can manipulate its bits later
FileContentResult f = File(
Service.ConvertToBytes(""),
"application/octet-stream",
string.Format("ticket-day-stats-{0}", DateTime.UtcNow.ToString("yyyy-MM-dd")) + ".csv");
// Send the file contents to a list so it can be added to easily
List<byte> fc = f.FileContents.ToList();

// Create an array of titles for the first line of the CSV
string[] titles = new string[Defaults.ShiftStrings.Length + 4];
titles[0] = "Date";
titles[1] = "Day of Week";
titles[titles.Count() - 2] = "Outside of Shifts Count";
titles[titles.Count() - 1] = "Total Created";
// Create a new title for each shift
for (int i = 2; i < titles.Count() - 2; i++) {
titles[i] = "Shift " + (i - 1) + " Count";
}
// Add to the byte list and remove the whitespace and newline from the file
fc.AddRange(Service.ConvertToBytes(titles));
fc = fc.TakeLast(fc.Count() - 2).ToList();

// Create an array of sums
int[] sums = new int[titles.Count() - 2];

// Loop through each received TicketDayGroup
foreach (TicketDayGroup group in result.TicketDays) {
// Set each value within the row
string[] row = new string[titles.Length];
row[0] = group.Date;
row[1] = group.DayOfWeek;
for (int i = 2; i < row.Count() - 2; i++) {
row[i] = group.ShiftCounts[i - 2].ToString();
sums[i - 2] += group.ShiftCounts[i - 2];
}
// Add the outside row count and total count
row[row.Count() - 2] = group.OutsideShiftCount.ToString();
sums[sums.Count() - 2] += group.OutsideShiftCount;
row[row.Count() - 1] = group.Count.ToString();
sums[sums.Count() - 1] += group.Count;
// Add to the list
fc.AddRange(Service.ConvertToBytes(row));
}

dayStats.Add(new Tuple<string, string, string, string, string, string>("", "Total", sums[0].ToString(), sums[1].ToString(), sums[2].ToString(), sums[3].ToString()));
// Create a final row composing of totals of the numbered columns
string[] rowLater = new string[titles.Length];
rowLater[0] = "";
rowLater[1] = "Total";
for (int i = 2; i < rowLater.Length; i++) {
rowLater[i] = sums[i - 2].ToString();
}
// Add to the list
fc.AddRange(Service.ConvertToBytes(rowLater));

// Convert the final byte list back to an array and return the resulting file
f.FileContents = fc.ToArray();

return ConstructManyColumnTupleReport(dayStats, "day");
return f;
}

/// <summary>
Expand Down Expand Up @@ -864,12 +908,12 @@ public async Task<IActionResult> ExportCorrelationStats()

// Helper method to create participation reports
public FileContentResult ConstructParticipationReport(ParticipationReport report) {
List<Tuple<string, string, string, string>> participationStats = new List<Tuple<string, string, string, string>>();
participationStats.Add(new Tuple<string, string, string, string>(report.Key, "Game Count", "Player Count", "Players with Sessions Count"));
List<Tuple<string, string, string, string, string, string, string>> participationStats = new List<Tuple<string, string, string, string, string, string, string>>();
participationStats.Add(new Tuple<string, string, string, string, string, string, string>(report.Key, "Game Count", "Player Count", "Players with Sessions Count", "Team Count", "Teams with Session Count", "Challenges Deployed Count"));

foreach (ParticipationStat stat in report.Stats)
{
participationStats.Add(new Tuple<string, string, string, string>(stat.Key, stat.GameCount.ToString(), stat.PlayerCount.ToString(), stat.SessionPlayerCount.ToString()));
participationStats.Add(new Tuple<string, string, string, string, string, string, string>(stat.Key, stat.GameCount.ToString(), stat.PlayerCount.ToString(), stat.SessionPlayerCount.ToString(), stat.TeamCount.ToString(), stat.SessionTeamCount.ToString(), stat.ChallengesDeployedCount.ToString()));
}

return ConstructManyColumnTupleReport(participationStats, report.Key.ToLower());
Expand Down
3 changes: 3 additions & 0 deletions src/Gameboard.Api/Features/Report/ReportModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ public class ParticipationStat
public int GameCount { get; set; }
public int PlayerCount { get; set; }
public int SessionPlayerCount { get; set; }
public int TeamCount { get; set; }
public int SessionTeamCount { get; set; }
public int ChallengesDeployedCount { get; set; }
}

public class SeriesReport : ParticipationReport
Expand Down
Loading

0 comments on commit b7ee413

Please sign in to comment.