83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
/**
|
|
* 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 GetLeagueStatsUseCaseParams {
|
|
leagueId: string;
|
|
}
|
|
|
|
/**
|
|
* Use Case for retrieving league statistics including average SOF across completed races.
|
|
*/
|
|
export class GetLeagueStatsUseCase {
|
|
private readonly sofCalculator: StrengthOfFieldCalculator;
|
|
|
|
constructor(
|
|
private readonly leagueRepository: ILeagueRepository,
|
|
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: GetLeagueStatsUseCaseParams): Promise<void> {
|
|
const { leagueId } = params;
|
|
|
|
const league = await this.leagueRepository.findById(leagueId);
|
|
if (!league) {
|
|
throw new Error(`League ${leagueId} not found`);
|
|
}
|
|
|
|
const races = await this.raceRepository.findByLeagueId(leagueId);
|
|
const completedRaces = races.filter(r => r.status === 'completed');
|
|
const scheduledRaces = races.filter(r => r.status === 'scheduled');
|
|
|
|
// Calculate SOF for each completed race
|
|
const sofValues: number[] = [];
|
|
|
|
for (const race of completedRaces) {
|
|
// Use stored SOF if available
|
|
if (race.strengthOfField) {
|
|
sofValues.push(race.strengthOfField);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise calculate from results
|
|
const results = await this.resultRepository.findByRaceId(race.id);
|
|
if (results.length === 0) continue;
|
|
|
|
const driverIds = results.map(r => r.driverId);
|
|
const ratings = this.driverRatingProvider.getRatings(driverIds);
|
|
const driverRatings = driverIds
|
|
.filter(id => ratings.has(id))
|
|
.map(id => ({ driverId: id, rating: ratings.get(id)! }));
|
|
|
|
const sof = this.sofCalculator.calculate(driverRatings);
|
|
if (sof !== null) {
|
|
sofValues.push(sof);
|
|
}
|
|
}
|
|
|
|
this.presenter.present(
|
|
leagueId,
|
|
races.length,
|
|
completedRaces.length,
|
|
scheduledRaces.length,
|
|
sofValues
|
|
);
|
|
}
|
|
} |