This commit is contained in:
2025-12-10 18:28:32 +01:00
parent 6d61be9c51
commit 1303a14493
108 changed files with 3366 additions and 1559 deletions

View File

@@ -2,26 +2,31 @@ import type { IStandingRepository } from '../../domain/repositories/IStandingRep
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { LeagueDriverSeasonStatsDTO } from '../dto/LeagueDriverSeasonStatsDTO';
import type { ILeagueDriverSeasonStatsPresenter } from '../presenters/ILeagueDriverSeasonStatsPresenter';
export interface DriverRatingPort {
getRating(driverId: string): { rating: number | null; ratingChange: number | null };
}
export interface GetLeagueDriverSeasonStatsQueryParamsDTO {
export interface GetLeagueDriverSeasonStatsUseCaseParams {
leagueId: string;
}
export class GetLeagueDriverSeasonStatsQuery {
/**
* Use Case for retrieving league driver season statistics.
* Orchestrates domain logic and delegates presentation to the presenter.
*/
export class GetLeagueDriverSeasonStatsUseCase {
constructor(
private readonly standingRepository: IStandingRepository,
private readonly resultRepository: IResultRepository,
private readonly penaltyRepository: IPenaltyRepository,
private readonly raceRepository: IRaceRepository,
private readonly driverRatingPort: DriverRatingPort,
public readonly presenter: ILeagueDriverSeasonStatsPresenter,
) {}
async execute(params: GetLeagueDriverSeasonStatsQueryParamsDTO): Promise<LeagueDriverSeasonStatsDTO[]> {
async execute(params: GetLeagueDriverSeasonStatsUseCaseParams): Promise<void> {
const { leagueId } = params;
// Get standings and races for the league
@@ -53,59 +58,26 @@ export class GetLeagueDriverSeasonStatsQuery {
penaltiesByDriver.set(p.driverId, current);
}
// Build basic stats per driver from standings
const statsByDriver = new Map<string, LeagueDriverSeasonStatsDTO>();
// Collect driver ratings
const driverRatings = new Map<string, { rating: number | null; ratingChange: number | null }>();
for (const standing of standings) {
const penalty = penaltiesByDriver.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 = this.driverRatingPort.getRating(standing.driverId);
const dto: LeagueDriverSeasonStatsDTO = {
leagueId,
driverId: standing.driverId,
position: standing.position,
driverName: '',
teamId: undefined,
teamName: undefined,
totalPoints: standing.points + totalPenaltyPoints + bonusPoints,
basePoints: standing.points,
penaltyPoints: Math.abs(totalPenaltyPoints),
bonusPoints,
pointsPerRace,
racesStarted: racesCompleted,
racesFinished: racesCompleted,
dnfs: 0,
noShows: 0,
avgFinish: null,
rating: ratingInfo.rating,
ratingChange: ratingInfo.ratingChange,
};
statsByDriver.set(standing.driverId, dto);
driverRatings.set(standing.driverId, ratingInfo);
}
// Enhance stats with basic finish-position-based avgFinish from results
for (const [driverId, dto] of statsByDriver.entries()) {
const driverResults = await this.resultRepository.findByDriverIdAndLeagueId(driverId, leagueId);
if (driverResults.length > 0) {
const totalPositions = driverResults.reduce((sum, r) => sum + r.position, 0);
const avgFinish = totalPositions / driverResults.length;
dto.avgFinish = Number.isFinite(avgFinish) ? Number(avgFinish.toFixed(2)) : null;
dto.racesStarted = driverResults.length;
dto.racesFinished = driverResults.length;
}
statsByDriver.set(driverId, dto);
// Collect driver results
const driverResults = new Map<string, Array<{ position: number }>>();
for (const standing of standings) {
const results = await this.resultRepository.findByDriverIdAndLeagueId(standing.driverId, leagueId);
driverResults.set(standing.driverId, results);
}
// Ensure ordering by position
const result = Array.from(statsByDriver.values()).sort((a, b) => a.position - b.position);
return result;
this.presenter.present(
leagueId,
standings,
penaltiesByDriver,
driverResults,
driverRatings
);
}
}