misc performance graph display tweaks

This commit is contained in:
RaidMax 2022-07-16 09:56:41 -05:00
parent b78c467539
commit bef8c08d90
8 changed files with 59 additions and 50 deletions

View File

@ -7,8 +7,6 @@ namespace Data.Models.Client.Stats
{ {
public class EFClientRankingHistory: AuditFields public class EFClientRankingHistory: AuditFields
{ {
public const int MaxRankingCount = 1728;
[Key] [Key]
public long ClientRankingHistoryId { get; set; } public long ClientRankingHistoryId { get; set; }

View File

@ -78,7 +78,7 @@ namespace Stats.Helpers
.Where(r => r.ClientId == clientInfo.ClientId) .Where(r => r.ClientId == clientInfo.ClientId)
.Where(r => r.ServerId == serverId) .Where(r => r.ServerId == serverId)
.Where(r => r.Ranking != null) .Where(r => r.Ranking != null)
.OrderByDescending(r => r.UpdatedDateTime) .OrderByDescending(r => r.CreatedDateTime)
.Take(250) .Take(250)
.ToListAsync(); .ToListAsync();

View File

@ -138,6 +138,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.CountAsync(); .CountAsync();
} }
public class RankingSnapshot
{
public int ClientId { get; set; }
public string Name { get; set; }
public DateTime LastConnection { get; set; }
public double? PerformanceMetric { get; set; }
public double? ZScore { get; set; }
public int? Ranking { get; set; }
public DateTime CreatedDateTime { get; set; }
}
public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null) public async Task<List<TopStatsInfo>> GetNewTopStats(int start, int count, long? serverId = null)
{ {
await using var context = _contextFactory.CreateContext(false); await using var context = _contextFactory.CreateContext(false);
@ -150,24 +161,29 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.Take(count) .Take(count)
.ToListAsync(); .ToListAsync();
var rankings = await context.Set<EFClientRankingHistory>() var rankingsDict = new Dictionary<int, List<RankingSnapshot>>();
.Where(ranking => clientIdsList.Contains(ranking.ClientId))
.Where(ranking => ranking.ServerId == serverId)
.Select(ranking => new
{
ranking.ClientId,
ranking.Client.CurrentAlias.Name,
ranking.Client.LastConnection,
ranking.PerformanceMetric,
ranking.ZScore,
ranking.Ranking,
ranking.CreatedDateTime
})
.ToListAsync();
var rankingsDict = rankings.GroupBy(rank => rank.ClientId)
.ToDictionary(rank => rank.Key, rank => rank.OrderBy(r => r.CreatedDateTime).ToList());
foreach (var clientId in clientIdsList)
{
var eachRank = await context.Set<EFClientRankingHistory>()
.Where(ranking => ranking.ClientId == clientId)
.Where(ranking => ranking.ServerId == serverId)
.OrderByDescending(ranking => ranking.CreatedDateTime)
.Select(ranking => new RankingSnapshot
{
ClientId = ranking.ClientId,
Name = ranking.Client.CurrentAlias.Name,
LastConnection = ranking.Client.LastConnection,
PerformanceMetric = ranking.PerformanceMetric,
ZScore = ranking.ZScore,
Ranking = ranking.Ranking,
CreatedDateTime = ranking.CreatedDateTime
})
.Take(60)
.ToListAsync();
rankingsDict.Add(clientId, eachRank);
}
var statsInfo = await context.Set<EFClientStatistics>() var statsInfo = await context.Set<EFClientStatistics>()
.Where(stat => clientIdsList.Contains(stat.ClientId)) .Where(stat => clientIdsList.Contains(stat.ClientId))
.Where(stat => stat.TimePlayed > 0) .Where(stat => stat.TimePlayed > 0)
@ -185,9 +201,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
UpdatedAt = s.Max(c => c.UpdatedAt) UpdatedAt = s.Max(c => c.UpdatedAt)
}) })
.ToListAsync(); .ToListAsync();
var finished = statsInfo var finished = statsInfo
.OrderByDescending(stat => rankingsDict[stat.ClientId].Last().PerformanceMetric) .OrderByDescending(stat => rankingsDict[stat.ClientId].First().PerformanceMetric)
.Select((s, index) => new TopStatsInfo .Select((s, index) => new TopStatsInfo
{ {
ClientId = s.ClientId, ClientId = s.ClientId,
@ -195,24 +211,23 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
Deaths = s.Deaths, Deaths = s.Deaths,
Kills = s.Kills, Kills = s.Kills,
KDR = Math.Round(s.KDR, 2), KDR = Math.Round(s.KDR, 2),
LastSeen = (DateTime.UtcNow - (s.UpdatedAt ?? rankingsDict[s.ClientId].Last().LastConnection)) LastSeen = (DateTime.UtcNow - (s.UpdatedAt ?? rankingsDict[s.ClientId].First().LastConnection))
.HumanizeForCurrentCulture(1, TimeUnit.Week, TimeUnit.Second, ",", false), .HumanizeForCurrentCulture(1, TimeUnit.Week, TimeUnit.Second, ",", false),
LastSeenValue = DateTime.UtcNow - (s.UpdatedAt ?? rankingsDict[s.ClientId].Last().LastConnection), LastSeenValue = DateTime.UtcNow - (s.UpdatedAt ?? rankingsDict[s.ClientId].First().LastConnection),
Name = rankingsDict[s.ClientId].First().Name, Name = rankingsDict[s.ClientId].First().Name,
Performance = Math.Round(rankingsDict[s.ClientId].Last().PerformanceMetric ?? 0, 2), Performance = Math.Round(rankingsDict[s.ClientId].First().PerformanceMetric ?? 0, 2),
RatingChange = (rankingsDict[s.ClientId].First().Ranking - RatingChange = (rankingsDict[s.ClientId].Last().Ranking -
rankingsDict[s.ClientId].Last().Ranking) ?? 0, rankingsDict[s.ClientId].First().Ranking) ?? 0,
PerformanceHistory = rankingsDict[s.ClientId].Select(ranking => new PerformanceHistory PerformanceHistory = rankingsDict[s.ClientId].Select(ranking => new PerformanceHistory
{ Performance = ranking.PerformanceMetric ?? 0, OccurredAt = ranking.CreatedDateTime }) { Performance = ranking.PerformanceMetric ?? 0, OccurredAt = ranking.CreatedDateTime })
.ToList(), .ToList(),
TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"), TimePlayed = Math.Round(s.TotalTimePlayed / 3600.0, 1).ToString("#,##0"),
TimePlayedValue = TimeSpan.FromSeconds(s.TotalTimePlayed), TimePlayedValue = TimeSpan.FromSeconds(s.TotalTimePlayed),
Ranking = index + start + 1, Ranking = index + start + 1,
ZScore = rankingsDict[s.ClientId].Last().ZScore, ZScore = rankingsDict[s.ClientId].First().ZScore,
ServerId = serverId ServerId = serverId
}) })
.OrderBy(r => r.Ranking) .OrderBy(r => r.Ranking)
.Take(60)
.ToList(); .ToList();
return finished; return finished;
@ -1322,14 +1337,20 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
context.Update(mostRecent); context.Update(mostRecent);
} }
if (totalRankingEntries > EFClientRankingHistory.MaxRankingCount) const int maxRankingCount = 1728; // 60 / 2.5 * 24 * 3 ( 3 days at sample every 2.5 minutes)
if (totalRankingEntries > maxRankingCount)
{ {
var lastRating = await context.Set<EFClientRankingHistory>() var lastRating = await context.Set<EFClientRankingHistory>()
.Where(r => r.ClientId == clientId) .Where(r => r.ClientId == clientId)
.Where(r => r.ServerId == serverId) .Where(r => r.ServerId == serverId)
.OrderBy(r => r.CreatedDateTime) .OrderBy(r => r.CreatedDateTime)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
context.Remove(lastRating);
if (lastRating is not null)
{
context.Remove(lastRating);
}
} }
} }

View File

@ -192,17 +192,7 @@ namespace SharedLibraryCore.Services
.Where(FilterById); .Where(FilterById);
return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync(); return await activePenaltiesIds.Select(ids => ids.Penalty).ToListAsync();
} }
public async Task<List<EFPenalty>> GetActivePenaltiesByClientId(int clientId)
{
await using var context = _contextFactory.CreateContext(false);
return await context.PenaltyIdentifiers
.Where(identifier => identifier.Penalty.Offender.ClientId == clientId)
.Select(identifier => identifier.Penalty)
.Where(Filter)
.ToListAsync();
}
public async Task<List<EFPenalty>> ActivePenaltiesByRecentIdentifiers(int linkId) public async Task<List<EFPenalty>> ActivePenaltiesByRecentIdentifiers(int linkId)
{ {
await using var context = _contextFactory.CreateContext(false); await using var context = _contextFactory.CreateContext(false);

View File

@ -124,11 +124,12 @@
var spm = Model.ServerId != null ? serverLegacyStat?.SPM.ToNumericalString() : Model.LegacyStats.WeightValueByPlaytime(nameof(EFClientStatistics.SPM), 0).ToNumericalString(); var spm = Model.ServerId != null ? serverLegacyStat?.SPM.ToNumericalString() : Model.LegacyStats.WeightValueByPlaytime(nameof(EFClientStatistics.SPM), 0).ToNumericalString();
var performanceHistory = Model.Ratings var performanceHistory = Model.Ratings
.OrderBy(rating => rating.CreatedDateTime)
.Select(rating => new PerformanceHistory { Performance = rating.PerformanceMetric, OccurredAt = rating.CreatedDateTime }); .Select(rating => new PerformanceHistory { Performance = rating.PerformanceMetric, OccurredAt = rating.CreatedDateTime });
if (performance != null) if (performance != null && performance != Model.Ratings.FirstOrDefault().PerformanceMetric)
{ {
performanceHistory = performanceHistory.Append(new PerformanceHistory { Performance = performance.Value, OccurredAt = DateTime.UtcNow }); performanceHistory = performanceHistory.Append(new PerformanceHistory { Performance = performance.Value, OccurredAt = Model.Ratings.FirstOrDefault()?.CreatedDateTime ?? DateTime.UtcNow });
} }
var score = allPerServer.Any() var score = allPerServer.Any()
@ -285,7 +286,7 @@
<!-- history graph --> <!-- history graph -->
@if (performanceHistory.Count() > 5) @if (performanceHistory.Count() > 5)
{ {
<div class="w-half m-auto ml-lg-auto " id="client_performance_history_container"> <div class="w-full w-lg-half m-auto ml-lg-auto" id="client_performance_history_container">
<canvas id="client_performance_history" data-history="@(JsonSerializer.Serialize(performanceHistory))"></canvas> <canvas id="client_performance_history" data-history="@(JsonSerializer.Serialize(performanceHistory))"></canvas>
</div> </div>
} }

View File

@ -1,5 +1,4 @@
@using IW4MAdmin.Plugins.Stats @using IW4MAdmin.Plugins.Stats
@using System.Text.Json.Serialization
@using System.Text.Json @using System.Text.Json
@model List<IW4MAdmin.Plugins.Stats.Web.Dtos.TopStatsInfo> @model List<IW4MAdmin.Plugins.Stats.Web.Dtos.TopStatsInfo>
@{ @{
@ -86,7 +85,7 @@
</div> </div>
</div> </div>
<div class="w-full w-md-half client-rating-graph pt-10 pb-10"> <div class="w-full w-md-half client-rating-graph pt-10 pb-10">
<canvas id="rating_history_@(stat.ClientId + "_" + stat.Id)" data-history="@(JsonSerializer.Serialize(stat.PerformanceHistory))"></canvas> <canvas id="rating_history_@(stat.ClientId + "_" + stat.Id)" data-history="@(JsonSerializer.Serialize(stat.PerformanceHistory.OrderBy(perf => perf.OccurredAt)))"></canvas>
</div> </div>
<div class="w-quarter align-self-center d-flex justify-content-center"> <div class="w-quarter align-self-center d-flex justify-content-center">
<img class="w-100 h-100" src="~/images/stats/ranks/rank_@(stat.ZScore.RankIconIndexForZScore()).png" alt="@stat.Performance"/> <img class="w-100 h-100" src="~/images/stats/ranks/rank_@(stat.ZScore.RankIconIndexForZScore()).png" alt="@stat.Performance"/>

View File

@ -26,7 +26,7 @@ function setupPerformanceGraph() {
} }
const chart = $('#client_performance_history'); const chart = $('#client_performance_history');
const container = $('#client_performance_history_container'); const container = $('#client_performance_history_container');
chart.attr('height', summary.height()); chart.attr('height', summary.height() * 1.5);
chart.attr('width', container.width()); chart.attr('width', container.width());
renderPerformanceChart(); renderPerformanceChart();
} }
@ -394,7 +394,7 @@ function renderPerformanceChart() {
position: 'right', position: 'right',
ticks: { ticks: {
precision: 0, precision: 0,
stepSize: 3, stepSize: max - min / 2,
callback: function (value, index, values) { callback: function (value, index, values) {
if (index === values.length - 1) { if (index === values.length - 1) {
return min; return min;

View File

@ -88,7 +88,7 @@ function getStatsChart(id) {
position: 'right', position: 'right',
ticks: { ticks: {
precision: 0, precision: 0,
stepSize: 3, stepSize: max - min / 2,
callback: function (value, index, values) { callback: function (value, index, values) {
if (index === values.length - 1) { if (index === values.length - 1) {
return min; return min;