Merge pull request #16 from exogen/feature/leaderboard-time-period

Add time period support to leaderboard queries
This commit is contained in:
Anthony Mineo 2021-09-26 09:36:45 -04:00 committed by GitHub
commit 7f1f34364e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 3 deletions

View file

@ -40,6 +40,9 @@ export class TopAccuracyQueryDto {
@IsPositive() @IsPositive()
@Max(100) @Max(100)
limit: number; limit: number;
@IsOptional()
timePeriod: string;
} }
export class TopWinsQueryDto { export class TopWinsQueryDto {
@ -51,4 +54,7 @@ export class TopWinsQueryDto {
@IsPositive() @IsPositive()
@Max(100) @Max(100)
limit: number; limit: number;
@IsOptional()
timePeriod: string;
} }

View file

@ -32,6 +32,7 @@ export class PlayersController {
minGames = 10, minGames = 10,
minShots = 100, minShots = 100,
limit = 10, limit = 10,
timePeriod,
} = topPlayersQuery; } = topPlayersQuery;
return this.playerService.findTopAccuracy({ return this.playerService.findTopAccuracy({
stat, stat,
@ -39,6 +40,7 @@ export class PlayersController {
minGames, minGames,
minShots, minShots,
limit, limit,
timePeriod,
}); });
} }
@ -48,10 +50,11 @@ export class PlayersController {
summary: 'Return a leaderboard of players for win percentage', summary: 'Return a leaderboard of players for win percentage',
}) })
findTopWins(@Query() topPlayersQuery: TopWinsQueryDto) { findTopWins(@Query() topPlayersQuery: TopWinsQueryDto) {
const { minGames = 100, limit = 10 } = topPlayersQuery; const { minGames = 100, limit = 10, timePeriod } = topPlayersQuery;
return this.playerService.findTopWins({ return this.playerService.findTopWins({
minGames, minGames,
limit, limit,
timePeriod,
}); });
} }
} }

View file

@ -56,6 +56,7 @@ export class PlayersService {
minGames, minGames,
minShots, minShots,
limit, limit,
timePeriod = null,
} = topAccuracyQuery; } = topAccuracyQuery;
const shotsStat = { const shotsStat = {
@ -102,6 +103,8 @@ export class PlayersService {
// Cast to float to avoid integer division truncating the result. // Cast to float to avoid integer division truncating the result.
const aggregatedAccuracy = `(${aggregatedHits}::float / ${aggregatedShots}::float)`; const aggregatedAccuracy = `(${aggregatedHits}::float / ${aggregatedShots}::float)`;
const sinceDate = '(now() - (:timePeriod)::interval)';
// TODO: This whole query could probably be turned into a `ViewEntity` at // TODO: This whole query could probably be turned into a `ViewEntity` at
// some point, but I couldn't get that to work. // some point, but I couldn't get that to work.
@ -112,6 +115,7 @@ export class PlayersService {
shotsStat, shotsStat,
minGames, minGames,
minShots, minShots,
timePeriod,
}) })
.select([ .select([
'player.player_guid', 'player.player_guid',
@ -121,6 +125,10 @@ export class PlayersService {
'stats.shots', 'stats.shots',
'stats.accuracy', 'stats.accuracy',
]) ])
.addSelect(
timePeriod ? sinceDate : 'NULL',
'since_date'
)
.innerJoin( .innerJoin(
(subQuery) => { (subQuery) => {
let statsQuery = subQuery let statsQuery = subQuery
@ -131,6 +139,7 @@ export class PlayersService {
.addSelect(aggregatedShots, 'shots') .addSelect(aggregatedShots, 'shots')
.addSelect(aggregatedAccuracy, 'accuracy') .addSelect(aggregatedAccuracy, 'accuracy')
.where(`${shotsValue} > 0`) .where(`${shotsValue} > 0`)
.andWhere(timePeriod ? `game.datestamp >= ${sinceDate}` : 'TRUE')
.groupBy('game.player_guid'); .groupBy('game.player_guid');
if (excludeDiscJumps) { if (excludeDiscJumps) {
@ -190,16 +199,20 @@ export class PlayersService {
minGames, minGames,
minShots, minShots,
limit, limit,
timePeriod,
sinceDate: rows.length ? rows[0].since_date : null,
players, players,
}; };
} }
async findTopWins(topWinsQuery: TopWinsQueryDto) { async findTopWins(topWinsQuery: TopWinsQueryDto) {
const { minGames, limit } = topWinsQuery; const { minGames, limit, timePeriod = null } = topWinsQuery;
const sinceDate = '(now() - (:timePeriod)::interval)';
const query = this.playersRepository const query = this.playersRepository
.createQueryBuilder('player') .createQueryBuilder('player')
.setParameters({ minGames }) .setParameters({ minGames, timePeriod })
.select(['stats.player_name', 'stats.player_guid']) .select(['stats.player_name', 'stats.player_guid'])
.addSelect('COUNT(stats.game_id)::integer', 'game_count') .addSelect('COUNT(stats.game_id)::integer', 'game_count')
.addSelect( .addSelect(
@ -218,6 +231,10 @@ export class PlayersService {
"(COUNT(stats.player_match_result = 'win' OR NULL)::float + COUNT(stats.player_match_result = 'draw' OR NULL)::float / 2.0) / COUNT(stats.game_id)::float", "(COUNT(stats.player_match_result = 'win' OR NULL)::float + COUNT(stats.player_match_result = 'draw' OR NULL)::float / 2.0) / COUNT(stats.game_id)::float",
'win_percent', 'win_percent',
) )
.addSelect(
timePeriod ? sinceDate : 'NULL',
'since_date'
)
.innerJoin( .innerJoin(
(qb) => { (qb) => {
return ( return (
@ -309,6 +326,8 @@ export class PlayersService {
// Each team must have at least 2 players. // Each team must have at least 2 players.
.andWhere('join_g.team_size_storm >= 2') .andWhere('join_g.team_size_storm >= 2')
.andWhere('join_g.team_size_inferno >= 2') .andWhere('join_g.team_size_inferno >= 2')
// Must fall within the specified time period.
.andWhere(timePeriod ? `game.datestamp >= ${sinceDate}` : 'TRUE')
); );
}, },
'stats', 'stats',
@ -351,6 +370,8 @@ export class PlayersService {
minGames, minGames,
gameType: ['CTFGame', 'SCtFGame'], gameType: ['CTFGame', 'SCtFGame'],
limit, limit,
timePeriod,
sinceDate: rows.length ? rows[0].since_date : null,
players, players,
}; };
} }