website refactor
This commit is contained in:
@@ -1,139 +1,22 @@
|
||||
/**
|
||||
* Application Use Case: TeamRankingUseCase
|
||||
* Application Use Case Interface: ITeamRankingUseCase
|
||||
*
|
||||
* 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.
|
||||
* Use case for computing team rankings from rating snapshots.
|
||||
* This is an application layer concern that orchestrates domain data.
|
||||
*/
|
||||
|
||||
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 interface TeamRanking {
|
||||
teamId: string;
|
||||
teamName: string;
|
||||
drivingRating: number;
|
||||
adminTrustRating: number;
|
||||
overallRating: number;
|
||||
eventCount: number;
|
||||
lastUpdated: Date;
|
||||
overallRank: number | null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
export interface TeamRankingUseCase {
|
||||
getAllTeamRankings(): Promise<TeamRanking[]>;
|
||||
getTeamRanking(teamId: string): Promise<TeamRanking | null>;
|
||||
}
|
||||
Reference in New Issue
Block a user