hopeful topstats fixes

This commit is contained in:
RaidMax 2022-01-24 15:08:31 -06:00
parent fb11bf54a6
commit 686b297d32
2 changed files with 47 additions and 22 deletions

View File

@ -79,7 +79,10 @@ namespace Stats.Client
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime)) .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime))
.Where(s => s.Skill > 0) .Where(s => s.Skill > 0)
.Where(s => s.EloRating > 0) .Where(s => s.EloRating > 0)
.MaxAsync(s => (double?)s.ZScore, token); .GroupBy(stat => stat.ClientId)
.Select(group =>
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed))
.MaxAsync(avgZScore => (double?) avgZScore, token);
return zScore ?? 0; return zScore ?? 0;
}, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30)); }, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30));
@ -138,6 +141,7 @@ namespace Stats.Client
{ {
return 0.0; return 0.0;
} }
var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma; var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma;
return zScore; return zScore;
} }
@ -145,7 +149,7 @@ namespace Stats.Client
public async Task<double?> GetRatingForZScore(double? value) public async Task<double?> GetRatingForZScore(double? value)
{ {
var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey); var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey);
return maxZScore == 0 ? 0 : value.GetRatingForZScore(maxZScore); return maxZScore == 0 ? null : value.GetRatingForZScore(maxZScore);
} }
} }
} }

View File

@ -121,7 +121,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
return (ranking) => ranking.ServerId == serverId return (ranking) => ranking.ServerId == serverId
&& ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned && ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned
&& ranking.Client.LastConnection >= Extensions.FifteenDaysAgo() && ranking.CreatedDateTime >= Extensions.FifteenDaysAgo()
&& ranking.ZScore != null && ranking.ZScore != null
&& ranking.PerformanceMetric != null && ranking.PerformanceMetric != null
&& ranking.Newest && ranking.Newest
@ -895,7 +895,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public async Task AddStandardKill(EFClient attacker, EFClient victim) public async Task AddStandardKill(EFClient attacker, EFClient victim)
{ {
long serverId = GetIdForServer(attacker.CurrentServer); var serverId = GetIdForServer(attacker.CurrentServer);
var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY); var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY); var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
@ -903,6 +903,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// update the total stats // update the total stats
_servers[serverId].ServerStatistics.TotalKills += 1; _servers[serverId].ServerStatistics.TotalKills += 1;
if (attackerStats == null)
{
_log.LogWarning("Stats for {Client} are not yet initialized", attacker.ToString());
return;
}
if (victimStats == null)
{
_log.LogWarning("Stats for {Client} are not yet initialized", victim.ToString());
return;
}
// this happens when the round has changed // this happens when the round has changed
if (attackerStats.SessionScore == 0) if (attackerStats.SessionScore == 0)
{ {
@ -961,7 +973,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// update their performance // update their performance
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >=
(Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 10.0 : 2.5)) (Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 5.0 : 2.5))
{ {
try try
{ {
@ -1178,16 +1190,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId) public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId)
{ {
await using var context = _contextFactory.CreateContext(); await using var context = _contextFactory.CreateContext();
var minPlayTime = _configHandler.Configuration().TopPlayersMinPlayTime;
var performances = await context.Set<EFClientStatistics>() var performances = await context.Set<EFClientStatistics>()
.AsNoTracking() .AsNoTracking()
.Where(stat => stat.ClientId == clientId) .Where(stat => stat.ClientId == clientId)
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking .Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
.Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo()) .Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo())
.Where(stats => stats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) .Where(stats => stats.TimePlayed >= minPlayTime)
.ToListAsync(); .ToListAsync();
if (clientStats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime) if (clientStats.TimePlayed >= minPlayTime)
{ {
clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId, clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId,
clientStats.Performance); clientStats.Performance);
@ -1198,7 +1211,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
_configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId)) _configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId))
.CountAsync(); .CountAsync();
var serverRankingSnapshot = new EFClientRankingHistory() var serverRankingSnapshot = new EFClientRankingHistory
{ {
ClientId = clientId, ClientId = clientId,
ServerId = serverId, ServerId = serverId,
@ -1215,14 +1228,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
performances.Add(clientStats); performances.Add(clientStats);
} }
if (performances.Any(performance => performance.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime)) if (performances.Any(performance => performance.TimePlayed >= minPlayTime))
{ {
var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), _configHandler.Configuration().TopPlayersMinPlayTime); var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), minPlayTime);
int? aggregateRanking = await context.Set<EFClientStatistics>() int? aggregateRanking = await context.Set<EFClientStatistics>()
.Where(stat => stat.ClientId != clientId) .Where(stat => stat.ClientId != clientId)
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(_configHandler.Configuration() .Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(minPlayTime))
.TopPlayersMinPlayTime))
.GroupBy(stat => stat.ClientId) .GroupBy(stat => stat.ClientId)
.Where(group => .Where(group =>
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) > group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) >
@ -1230,12 +1242,21 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.Select(c => c.Key) .Select(c => c.Key)
.CountAsync(); .CountAsync();
var aggregateRankingSnapshot = new EFClientRankingHistory() var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore);
if (newPerformanceMetric == null)
{
_log.LogWarning("Could not determine performance metric for {Client} {AggregateZScore}",
clientStats.Client?.ToString(), aggregateZScore);
return;
}
var aggregateRankingSnapshot = new EFClientRankingHistory
{ {
ClientId = clientId, ClientId = clientId,
ZScore = aggregateZScore, ZScore = aggregateZScore,
Ranking = aggregateRanking, Ranking = aggregateRanking,
PerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore), PerformanceMetric = newPerformanceMetric,
Newest = true, Newest = true,
}; };