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

@@ -1,33 +1,26 @@
/**
* Application Query: GetLeagueStatsQuery
*
* Returns league statistics including average SOF across completed races.
* Use Case for retrieving league statistics.
* Orchestrates domain logic and delegates presentation to the presenter.
*/
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import type { ILeagueStatsPresenter } from '../presenters/ILeagueStatsPresenter';
import {
AverageStrengthOfFieldCalculator,
type StrengthOfFieldCalculator,
} from '../../domain/services/StrengthOfFieldCalculator';
export interface GetLeagueStatsQueryParams {
export interface GetLeagueStatsUseCaseParams {
leagueId: string;
}
export interface LeagueStatsDTO {
leagueId: string;
totalRaces: number;
completedRaces: number;
scheduledRaces: number;
averageSOF: number | null;
highestSOF: number | null;
lowestSOF: number | null;
}
export class GetLeagueStatsQuery {
/**
* Use Case for retrieving league statistics including average SOF across completed races.
*/
export class GetLeagueStatsUseCase {
private readonly sofCalculator: StrengthOfFieldCalculator;
constructor(
@@ -35,17 +28,18 @@ export class GetLeagueStatsQuery {
private readonly raceRepository: IRaceRepository,
private readonly resultRepository: IResultRepository,
private readonly driverRatingProvider: DriverRatingProvider,
public readonly presenter: ILeagueStatsPresenter,
sofCalculator?: StrengthOfFieldCalculator,
) {
this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator();
}
async execute(params: GetLeagueStatsQueryParams): Promise<LeagueStatsDTO | null> {
async execute(params: GetLeagueStatsUseCaseParams): Promise<void> {
const { leagueId } = params;
const league = await this.leagueRepository.findById(leagueId);
if (!league) {
return null;
throw new Error(`League ${leagueId} not found`);
}
const races = await this.raceRepository.findByLeagueId(leagueId);
@@ -78,22 +72,12 @@ export class GetLeagueStatsQuery {
}
}
// Calculate aggregate stats
const averageSOF = sofValues.length > 0
? Math.round(sofValues.reduce((a, b) => a + b, 0) / sofValues.length)
: null;
const highestSOF = sofValues.length > 0 ? Math.max(...sofValues) : null;
const lowestSOF = sofValues.length > 0 ? Math.min(...sofValues) : null;
return {
this.presenter.present(
leagueId,
totalRaces: races.length,
completedRaces: completedRaces.length,
scheduledRaces: scheduledRaces.length,
averageSOF,
highestSOF,
lowestSOF,
};
races.length,
completedRaces.length,
scheduledRaces.length,
sofValues
);
}
}