131 lines
4.6 KiB
TypeScript
131 lines
4.6 KiB
TypeScript
import type { ISeasonRepository } from '@gridpilot/racing/domain/repositories/ISeasonRepository';
|
|
import type { ILeagueScoringConfigRepository } from '@gridpilot/racing/domain/repositories/ILeagueScoringConfigRepository';
|
|
import type { IRaceRepository } from '@gridpilot/racing/domain/repositories/IRaceRepository';
|
|
import type { IResultRepository } from '@gridpilot/racing/domain/repositories/IResultRepository';
|
|
import type { IPenaltyRepository } from '@gridpilot/racing/domain/repositories/IPenaltyRepository';
|
|
import type { IChampionshipStandingRepository } from '@gridpilot/racing/domain/repositories/IChampionshipStandingRepository';
|
|
|
|
import type { ChampionshipConfig } from '@gridpilot/racing/domain/types/ChampionshipConfig';
|
|
import type { SessionType } from '@gridpilot/racing/domain/types/SessionType';
|
|
import type { ChampionshipStanding } from '@gridpilot/racing/domain/entities/ChampionshipStanding';
|
|
import { EventScoringService } from '@gridpilot/racing/domain/services/EventScoringService';
|
|
import { ChampionshipAggregator } from '@gridpilot/racing/domain/services/ChampionshipAggregator';
|
|
|
|
import type {
|
|
ChampionshipStandingsDTO,
|
|
ChampionshipStandingsRowDTO,
|
|
} from '../dto/ChampionshipStandingsDTO';
|
|
|
|
export class RecalculateChampionshipStandingsUseCase {
|
|
constructor(
|
|
private readonly seasonRepository: ISeasonRepository,
|
|
private readonly leagueScoringConfigRepository: ILeagueScoringConfigRepository,
|
|
private readonly raceRepository: IRaceRepository,
|
|
private readonly resultRepository: IResultRepository,
|
|
private readonly penaltyRepository: IPenaltyRepository,
|
|
private readonly championshipStandingRepository: IChampionshipStandingRepository,
|
|
private readonly eventScoringService: EventScoringService,
|
|
private readonly championshipAggregator: ChampionshipAggregator,
|
|
) {}
|
|
|
|
async execute(params: {
|
|
seasonId: string;
|
|
championshipId: string;
|
|
}): Promise<ChampionshipStandingsDTO> {
|
|
const { seasonId, championshipId } = params;
|
|
|
|
const season = await this.seasonRepository.findById(seasonId);
|
|
if (!season) {
|
|
throw new Error(`Season not found: ${seasonId}`);
|
|
}
|
|
|
|
const leagueScoringConfig =
|
|
await this.leagueScoringConfigRepository.findBySeasonId(seasonId);
|
|
if (!leagueScoringConfig) {
|
|
throw new Error(`League scoring config not found for season: ${seasonId}`);
|
|
}
|
|
|
|
const championship = this.findChampionshipConfig(
|
|
leagueScoringConfig.championships,
|
|
championshipId,
|
|
);
|
|
|
|
const races = await this.raceRepository.findByLeagueId(season.leagueId);
|
|
|
|
const eventPointsByEventId: Record<string, ReturnType<EventScoringService['scoreSession']>> =
|
|
{};
|
|
|
|
for (const race of races) {
|
|
// Map existing Race.sessionType into scoring SessionType where possible.
|
|
const sessionType = this.mapRaceSessionType(race.sessionType);
|
|
if (!championship.sessionTypes.includes(sessionType)) {
|
|
continue;
|
|
}
|
|
|
|
const results = await this.resultRepository.findByRaceId(race.id);
|
|
|
|
// Fetch penalties for this specific race
|
|
const penalties = await this.penaltyRepository.findByRaceId(race.id);
|
|
|
|
const participantPoints = this.eventScoringService.scoreSession({
|
|
seasonId,
|
|
championship,
|
|
sessionType,
|
|
results,
|
|
penalties,
|
|
});
|
|
|
|
eventPointsByEventId[race.id] = participantPoints;
|
|
}
|
|
|
|
const standings: ChampionshipStanding[] = this.championshipAggregator.aggregate({
|
|
seasonId,
|
|
championship,
|
|
eventPointsByEventId,
|
|
});
|
|
|
|
await this.championshipStandingRepository.saveAll(standings);
|
|
|
|
const rows: ChampionshipStandingsRowDTO[] = standings.map((s) => ({
|
|
participant: s.participant,
|
|
position: s.position,
|
|
totalPoints: s.totalPoints,
|
|
resultsCounted: s.resultsCounted,
|
|
resultsDropped: s.resultsDropped,
|
|
}));
|
|
|
|
const dto: ChampionshipStandingsDTO = {
|
|
seasonId,
|
|
championshipId: championship.id,
|
|
championshipName: championship.name,
|
|
rows,
|
|
};
|
|
|
|
return dto;
|
|
}
|
|
|
|
private findChampionshipConfig(
|
|
configs: ChampionshipConfig[],
|
|
championshipId: string,
|
|
): ChampionshipConfig {
|
|
const found = configs.find((c) => c.id === championshipId);
|
|
if (!found) {
|
|
throw new Error(`Championship config not found: ${championshipId}`);
|
|
}
|
|
return found;
|
|
}
|
|
|
|
private mapRaceSessionType(sessionType: string): SessionType {
|
|
if (sessionType === 'race') {
|
|
return 'main';
|
|
}
|
|
if (
|
|
sessionType === 'practice' ||
|
|
sessionType === 'qualifying' ||
|
|
sessionType === 'timeTrial'
|
|
) {
|
|
return sessionType;
|
|
}
|
|
return 'main';
|
|
}
|
|
} |