78 lines
2.6 KiB
TypeScript
78 lines
2.6 KiB
TypeScript
import type {
|
|
ILeagueDriverSeasonStatsPresenter,
|
|
LeagueDriverSeasonStatsItemViewModel,
|
|
LeagueDriverSeasonStatsViewModel,
|
|
} from '@gridpilot/racing/application/presenters/ILeagueDriverSeasonStatsPresenter';
|
|
|
|
export class LeagueDriverSeasonStatsPresenter implements ILeagueDriverSeasonStatsPresenter {
|
|
private viewModel: LeagueDriverSeasonStatsViewModel | null = null;
|
|
|
|
present(
|
|
leagueId: string,
|
|
standings: Array<{
|
|
driverId: string;
|
|
position: number;
|
|
points: number;
|
|
racesCompleted: number;
|
|
}>,
|
|
penalties: Map<string, { baseDelta: number; bonusDelta: number }>,
|
|
driverResults: Map<string, Array<{ position: number }>>,
|
|
driverRatings: Map<string, { rating: number | null; ratingChange: number | null }>
|
|
): LeagueDriverSeasonStatsViewModel {
|
|
const stats: LeagueDriverSeasonStatsItemViewModel[] = standings.map((standing) => {
|
|
const penalty = penalties.get(standing.driverId) ?? { baseDelta: 0, bonusDelta: 0 };
|
|
const totalPenaltyPoints = penalty.baseDelta;
|
|
const bonusPoints = penalty.bonusDelta;
|
|
|
|
const racesCompleted = standing.racesCompleted;
|
|
const pointsPerRace = racesCompleted > 0 ? standing.points / racesCompleted : 0;
|
|
|
|
const ratingInfo = driverRatings.get(standing.driverId) ?? { rating: null, ratingChange: null };
|
|
|
|
const results = driverResults.get(standing.driverId) ?? [];
|
|
let avgFinish: number | null = null;
|
|
if (results.length > 0) {
|
|
const totalPositions = results.reduce((sum, r) => sum + r.position, 0);
|
|
const avg = totalPositions / results.length;
|
|
avgFinish = Number.isFinite(avg) ? Number(avg.toFixed(2)) : null;
|
|
}
|
|
|
|
return {
|
|
leagueId,
|
|
driverId: standing.driverId,
|
|
position: standing.position,
|
|
driverName: '',
|
|
teamId: '',
|
|
teamName: '',
|
|
totalPoints: standing.points + totalPenaltyPoints + bonusPoints,
|
|
basePoints: standing.points,
|
|
penaltyPoints: Math.abs(totalPenaltyPoints),
|
|
bonusPoints,
|
|
pointsPerRace,
|
|
racesStarted: results.length,
|
|
racesFinished: results.length,
|
|
dnfs: 0,
|
|
noShows: 0,
|
|
avgFinish,
|
|
rating: ratingInfo.rating,
|
|
ratingChange: ratingInfo.ratingChange,
|
|
};
|
|
});
|
|
|
|
stats.sort((a, b) => a.position - b.position);
|
|
|
|
this.viewModel = {
|
|
leagueId,
|
|
stats,
|
|
};
|
|
|
|
return this.viewModel;
|
|
}
|
|
|
|
getViewModel(): LeagueDriverSeasonStatsViewModel {
|
|
if (!this.viewModel) {
|
|
throw new Error('Presenter has not been called yet');
|
|
}
|
|
return this.viewModel;
|
|
}
|
|
} |