109 lines
4.0 KiB
TypeScript
109 lines
4.0 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/IRaceRepository';
|
|
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
|
|
import type { ILeagueStatsPresenter } from '../presenters/ILeagueStatsPresenter';
|
|
import type { AsyncUseCase } from '@gridpilot/shared/application';
|
|
import { ILogger } from '../../../shared/src/logging/ILogger';
|
|
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
|
|
implements AsyncUseCase<GetLeagueStatsUseCaseParams, void> {
|
|
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,
|
|
private readonly logger: ILogger,
|
|
sofCalculator?: StrengthOfFieldCalculator,
|
|
) {
|
|
this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator();
|
|
}
|
|
|
|
async execute(params: GetLeagueStatsUseCaseParams): Promise<void> {
|
|
this.logger.debug(
|
|
`Executing GetLeagueStatsUseCase with params: ${JSON.stringify(params)}`,
|
|
);
|
|
const { leagueId } = params;
|
|
|
|
try {
|
|
const league = await this.leagueRepository.findById(leagueId);
|
|
if (!league) {
|
|
this.logger.error(`League ${leagueId} not found`);
|
|
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');
|
|
this.logger.info(
|
|
`Found ${races.length} races for league ${leagueId}: ${completedRaces.length} completed, ${scheduledRaces.length} scheduled. `,
|
|
);
|
|
|
|
// Calculate SOF for each completed race
|
|
const sofValues: number[] = [];
|
|
|
|
for (const race of completedRaces) {
|
|
// Use stored SOF if available
|
|
if (race.strengthOfField) {
|
|
this.logger.debug(
|
|
`Using stored Strength of Field for race ${race.id}: ${race.strengthOfField}`,
|
|
);
|
|
sofValues.push(race.strengthOfField);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise calculate from results
|
|
const results = await this.resultRepository.findByRaceId(race.id);
|
|
if (results.length === 0) {
|
|
this.logger.debug(`No results found for race ${race.id}. Skipping SOF calculation.`);
|
|
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) {
|
|
this.logger.debug(`Calculated Strength of Field for race ${race.id}: ${sof}`);
|
|
sofValues.push(sof);
|
|
} else {
|
|
this.logger.warn(`Could not calculate Strength of Field for race ${race.id}`);
|
|
}
|
|
}
|
|
|
|
this.presenter.present(
|
|
leagueId,
|
|
races.length,
|
|
completedRaces.length,
|
|
scheduledRaces.length,
|
|
sofValues,
|
|
);
|
|
this.logger.info(`Successfully presented league statistics for league ${leagueId}.`);
|
|
} catch (error) {
|
|
this.logger.error(`Error in GetLeagueStatsUseCase: ${error.message}`);
|
|
throw error;
|
|
}
|
|
}
|
|
} |