83 lines
3.1 KiB
TypeScript
83 lines
3.1 KiB
TypeScript
import type { IStandingRepository } from '../../domain/repositories/IStandingRepository';
|
|
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
|
|
import type { IPenaltyRepository } from '../../domain/repositories/IPenaltyRepository';
|
|
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
|
|
import type { ILeagueDriverSeasonStatsPresenter } from '../presenters/ILeagueDriverSeasonStatsPresenter';
|
|
|
|
export interface DriverRatingPort {
|
|
getRating(driverId: string): { rating: number | null; ratingChange: number | null };
|
|
}
|
|
|
|
export interface GetLeagueDriverSeasonStatsUseCaseParams {
|
|
leagueId: string;
|
|
}
|
|
|
|
/**
|
|
* 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: GetLeagueDriverSeasonStatsUseCaseParams): Promise<void> {
|
|
const { leagueId } = params;
|
|
|
|
// Get standings and races for the league
|
|
const [standings, races] = await Promise.all([
|
|
this.standingRepository.findByLeagueId(leagueId),
|
|
this.raceRepository.findByLeagueId(leagueId),
|
|
]);
|
|
|
|
// Fetch all penalties for all races in the league
|
|
const penaltiesArrays = await Promise.all(
|
|
races.map(race => this.penaltyRepository.findByRaceId(race.id))
|
|
);
|
|
const penaltiesForLeague = penaltiesArrays.flat();
|
|
|
|
// Group penalties by driver for quick lookup
|
|
const penaltiesByDriver = new Map<string, { baseDelta: number; bonusDelta: number }>();
|
|
for (const p of penaltiesForLeague) {
|
|
// Only count applied penalties
|
|
if (p.status !== 'applied') continue;
|
|
|
|
const current = penaltiesByDriver.get(p.driverId) ?? { baseDelta: 0, bonusDelta: 0 };
|
|
|
|
// Convert penalty to points delta based on type
|
|
if (p.type === 'points_deduction' && p.value) {
|
|
// Points deductions are negative
|
|
current.baseDelta -= p.value;
|
|
}
|
|
|
|
penaltiesByDriver.set(p.driverId, current);
|
|
}
|
|
|
|
// Collect driver ratings
|
|
const driverRatings = new Map<string, { rating: number | null; ratingChange: number | null }>();
|
|
for (const standing of standings) {
|
|
const ratingInfo = this.driverRatingPort.getRating(standing.driverId);
|
|
driverRatings.set(standing.driverId, ratingInfo);
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
this.presenter.present(
|
|
leagueId,
|
|
standings,
|
|
penaltiesByDriver,
|
|
driverResults,
|
|
driverRatings
|
|
);
|
|
}
|
|
} |