139 lines
4.7 KiB
TypeScript
139 lines
4.7 KiB
TypeScript
/**
|
|
* Application Use Case: TeamRankingUseCase
|
|
*
|
|
* Computes team rankings from rating snapshots (ledger-based).
|
|
* Orchestrates repositories to provide team ranking data to presentation layer.
|
|
* Evolved from direct standings to use team rating events and snapshots.
|
|
*/
|
|
|
|
import type { Logger } from '@core/shared/application';
|
|
import type { ITeamRatingRepository } from '../../domain/repositories/ITeamRatingRepository';
|
|
import type { ITeamRepository } from '../../domain/repositories/ITeamRepository';
|
|
import type { ITeamRankingUseCase, TeamRanking } from './ITeamRankingUseCase';
|
|
|
|
export class TeamRankingUseCase implements ITeamRankingUseCase {
|
|
constructor(
|
|
private readonly teamRatingRepository: ITeamRatingRepository,
|
|
private readonly teamRepository: ITeamRepository,
|
|
private readonly logger: Logger
|
|
) {
|
|
this.logger.info('[TeamRankingUseCase] Initialized with ledger-based team rating repositories');
|
|
}
|
|
|
|
async getAllTeamRankings(): Promise<TeamRanking[]> {
|
|
this.logger.debug('[TeamRankingUseCase] Computing rankings from team rating snapshots');
|
|
|
|
try {
|
|
// Get all teams for name resolution
|
|
const teams = await this.teamRepository.findAll();
|
|
const teamMap = new Map(teams.map(t => [t.id, t]));
|
|
|
|
// Get all team IDs
|
|
const teamIds = Array.from(teamMap.keys());
|
|
|
|
if (teamIds.length === 0) {
|
|
this.logger.warn('[TeamRankingUseCase] No teams found');
|
|
return [];
|
|
}
|
|
|
|
// Get rating snapshots for all teams
|
|
const rankingPromises = teamIds.map(async (teamId) => {
|
|
const snapshot = await this.teamRatingRepository.findByTeamId(teamId);
|
|
const team = teamMap.get(teamId);
|
|
|
|
if (!snapshot || !team) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
teamId,
|
|
teamName: team.name.toString(),
|
|
drivingRating: snapshot.driving.value,
|
|
adminTrustRating: snapshot.adminTrust.value,
|
|
overallRating: snapshot.overall,
|
|
eventCount: snapshot.eventCount,
|
|
lastUpdated: snapshot.lastUpdated,
|
|
overallRank: null, // Will be assigned after sorting
|
|
} as TeamRanking;
|
|
});
|
|
|
|
const rankings = (await Promise.all(rankingPromises)).filter(
|
|
(r): r is TeamRanking => r !== null
|
|
);
|
|
|
|
if (rankings.length === 0) {
|
|
this.logger.warn('[TeamRankingUseCase] No team rating snapshots found');
|
|
return [];
|
|
}
|
|
|
|
// Sort by overall rating descending and assign ranks
|
|
rankings.sort((a, b) => b.overallRating - a.overallRating);
|
|
rankings.forEach((r, idx) => r.overallRank = idx + 1);
|
|
|
|
this.logger.info(`[TeamRankingUseCase] Computed rankings for ${rankings.length} teams`);
|
|
|
|
return rankings;
|
|
} catch (error) {
|
|
this.logger.error('[TeamRankingUseCase] Error computing rankings:', error instanceof Error ? error : new Error(String(error)));
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async getTeamRanking(teamId: string): Promise<TeamRanking | null> {
|
|
this.logger.debug(`[TeamRankingUseCase] Getting ranking for team ${teamId}`);
|
|
|
|
try {
|
|
const snapshot = await this.teamRatingRepository.findByTeamId(teamId);
|
|
|
|
if (!snapshot) {
|
|
this.logger.warn(`[TeamRankingUseCase] No rating snapshot found for team ${teamId}`);
|
|
return null;
|
|
}
|
|
|
|
const team = await this.teamRepository.findById(teamId);
|
|
|
|
if (!team) {
|
|
this.logger.warn(`[TeamRankingUseCase] Team ${teamId} not found`);
|
|
return null;
|
|
}
|
|
|
|
// Get all teams to calculate rank
|
|
const allTeams = await this.teamRepository.findAll();
|
|
const allRankings: TeamRanking[] = [];
|
|
|
|
for (const t of allTeams) {
|
|
const s = await this.teamRatingRepository.findByTeamId(t.id);
|
|
if (s) {
|
|
allRankings.push({
|
|
teamId: t.id,
|
|
teamName: t.name.toString(),
|
|
drivingRating: s.driving.value,
|
|
adminTrustRating: s.adminTrust.value,
|
|
overallRating: s.overall,
|
|
eventCount: s.eventCount,
|
|
lastUpdated: s.lastUpdated,
|
|
overallRank: null,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Sort and assign rank
|
|
allRankings.sort((a, b) => b.overallRating - a.overallRating);
|
|
const rank = allRankings.findIndex(r => r.teamId === teamId) + 1;
|
|
|
|
return {
|
|
teamId,
|
|
teamName: team.name.toString(),
|
|
drivingRating: snapshot.driving.value,
|
|
adminTrustRating: snapshot.adminTrust.value,
|
|
overallRating: snapshot.overall,
|
|
eventCount: snapshot.eventCount,
|
|
lastUpdated: snapshot.lastUpdated,
|
|
overallRank: rank,
|
|
};
|
|
} catch (error) {
|
|
this.logger.error(`[TeamRankingUseCase] Error getting team ranking:`, error instanceof Error ? error : new Error(String(error)));
|
|
throw error;
|
|
}
|
|
}
|
|
} |