Files
gridpilot.gg/packages/racing/application/use-cases/GetRaceWithSOFUseCase.ts
2025-12-11 00:57:32 +01:00

85 lines
2.9 KiB
TypeScript

/**
* Use Case: GetRaceWithSOFUseCase
*
* Returns race details enriched with calculated Strength of Field (SOF).
* SOF is calculated from participant ratings if not already stored on the race.
* Orchestrates domain logic and delegates presentation to the presenter.
*/
import type { IRaceRepository } from '../../domain/repositories/IRaceRepository';
import type { IRaceRegistrationRepository } from '../../domain/repositories/IRaceRegistrationRepository';
import type { IResultRepository } from '../../domain/repositories/IResultRepository';
import type { DriverRatingProvider } from '../ports/DriverRatingProvider';
import {
AverageStrengthOfFieldCalculator,
type StrengthOfFieldCalculator,
} from '../../domain/services/StrengthOfFieldCalculator';
import type { IRaceWithSOFPresenter } from '../presenters/IRaceWithSOFPresenter';
export interface GetRaceWithSOFQueryParams {
raceId: string;
}
export class GetRaceWithSOFUseCase {
private readonly sofCalculator: StrengthOfFieldCalculator;
constructor(
private readonly raceRepository: IRaceRepository,
private readonly registrationRepository: IRaceRegistrationRepository,
private readonly resultRepository: IResultRepository,
private readonly driverRatingProvider: DriverRatingProvider,
public readonly presenter: IRaceWithSOFPresenter,
sofCalculator?: StrengthOfFieldCalculator,
) {
this.sofCalculator = sofCalculator ?? new AverageStrengthOfFieldCalculator();
}
async execute(params: GetRaceWithSOFQueryParams): Promise<void> {
const { raceId } = params;
const race = await this.raceRepository.findById(raceId);
if (!race) {
return null;
}
// Get participant IDs based on race status
let participantIds: string[] = [];
if (race.status === 'completed') {
// For completed races, use results
const results = await this.resultRepository.findByRaceId(raceId);
participantIds = results.map(r => r.driverId);
} else {
// For upcoming/running races, use registrations
participantIds = await this.registrationRepository.getRegisteredDrivers(raceId);
}
// Use stored SOF if available, otherwise calculate
let strengthOfField = race.strengthOfField ?? null;
if (strengthOfField === null && participantIds.length > 0) {
const ratings = this.driverRatingProvider.getRatings(participantIds);
const driverRatings = participantIds
.filter(id => ratings.has(id))
.map(id => ({ driverId: id, rating: ratings.get(id)! }));
strengthOfField = this.sofCalculator.calculate(driverRatings);
}
this.presenter.present(
race.id,
race.leagueId,
race.scheduledAt,
race.track,
race.trackId,
race.car,
race.carId,
race.sessionType,
race.status,
strengthOfField,
race.registeredCount ?? participantIds.length,
race.maxParticipants,
participantIds.length
);
}
}