wip
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { ChampionshipConfig } from '../value-objects/ChampionshipConfig';
|
||||
import type { ParticipantRef } from '../value-objects/ParticipantRef';
|
||||
import type { ChampionshipConfig } from '../types/ChampionshipConfig';
|
||||
import type { ParticipantRef } from '../types/ParticipantRef';
|
||||
import { ChampionshipStanding } from '../entities/ChampionshipStanding';
|
||||
import type { ParticipantEventPoints } from './EventScoringService';
|
||||
import { DropScoreApplier, type EventPointsEntry } from './DropScoreApplier';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DropScorePolicy } from '../value-objects/DropScorePolicy';
|
||||
import type { DropScorePolicy } from '../types/DropScorePolicy';
|
||||
import type { IDomainCalculationService } from '@gridpilot/shared/domain';
|
||||
|
||||
export interface EventPointsEntry {
|
||||
eventId: string;
|
||||
@@ -11,7 +12,16 @@ export interface DropScoreResult {
|
||||
totalPoints: number;
|
||||
}
|
||||
|
||||
export class DropScoreApplier {
|
||||
export interface DropScoreInput {
|
||||
policy: DropScorePolicy;
|
||||
events: EventPointsEntry[];
|
||||
}
|
||||
|
||||
export class DropScoreApplier implements IDomainCalculationService<DropScoreInput, DropScoreResult> {
|
||||
calculate(input: DropScoreInput): DropScoreResult {
|
||||
return this.apply(input.policy, input.events);
|
||||
}
|
||||
|
||||
apply(policy: DropScorePolicy, events: EventPointsEntry[]): DropScoreResult {
|
||||
if (policy.strategy === 'none' || events.length === 0) {
|
||||
const totalPoints = events.reduce((sum, e) => sum + e.points, 0);
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { ChampionshipConfig } from '../value-objects/ChampionshipConfig';
|
||||
import type { SessionType } from '../value-objects/SessionType';
|
||||
import type { ParticipantRef } from '../value-objects/ParticipantRef';
|
||||
import type { ChampionshipConfig } from '../types/ChampionshipConfig';
|
||||
import type { SessionType } from '../types/SessionType';
|
||||
import type { ParticipantRef } from '../types/ParticipantRef';
|
||||
import type { Result } from '../entities/Result';
|
||||
import type { Penalty } from '../entities/Penalty';
|
||||
import type { BonusRule } from '../value-objects/BonusRule';
|
||||
import type { ChampionshipType } from '../value-objects/ChampionshipType';
|
||||
import type { BonusRule } from '../types/BonusRule';
|
||||
import type { ChampionshipType } from '../types/ChampionshipType';
|
||||
|
||||
import type { PointsTable } from '../value-objects/PointsTable';
|
||||
import type { IDomainCalculationService } from '@gridpilot/shared/domain';
|
||||
|
||||
export interface ParticipantEventPoints {
|
||||
participant: ParticipantRef;
|
||||
@@ -16,6 +17,14 @@ export interface ParticipantEventPoints {
|
||||
totalPoints: number;
|
||||
}
|
||||
|
||||
export interface EventScoringInput {
|
||||
seasonId: string;
|
||||
championship: ChampionshipConfig;
|
||||
sessionType: SessionType;
|
||||
results: Result[];
|
||||
penalties: Penalty[];
|
||||
}
|
||||
|
||||
function createDriverParticipant(driverId: string): ParticipantRef {
|
||||
return {
|
||||
type: 'driver' as ChampionshipType,
|
||||
@@ -23,14 +32,14 @@ function createDriverParticipant(driverId: string): ParticipantRef {
|
||||
};
|
||||
}
|
||||
|
||||
export class EventScoringService {
|
||||
scoreSession(params: {
|
||||
seasonId: string;
|
||||
championship: ChampionshipConfig;
|
||||
sessionType: SessionType;
|
||||
results: Result[];
|
||||
penalties: Penalty[];
|
||||
}): ParticipantEventPoints[] {
|
||||
export class EventScoringService
|
||||
implements IDomainCalculationService<EventScoringInput, ParticipantEventPoints[]>
|
||||
{
|
||||
calculate(input: EventScoringInput): ParticipantEventPoints[] {
|
||||
return this.scoreSession(input);
|
||||
}
|
||||
|
||||
scoreSession(params: EventScoringInput): ParticipantEventPoints[] {
|
||||
const { championship, sessionType, results } = params;
|
||||
|
||||
const pointsTable = this.getPointsTableForSession(championship, sessionType);
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
/**
|
||||
* Domain Service Port: IImageService
|
||||
* Backwards-compat alias for legacy imports.
|
||||
*
|
||||
* Thin abstraction used by racing application use cases to obtain image URLs
|
||||
* for drivers, teams and leagues without depending directly on UI/media layers.
|
||||
* New code should depend on IImageServicePort from
|
||||
* packages/racing/application/ports/IImageServicePort.
|
||||
*/
|
||||
export interface IImageService {
|
||||
getDriverAvatar(driverId: string): string;
|
||||
getTeamLogo(teamId: string): string;
|
||||
getLeagueCover(leagueId: string): string;
|
||||
getLeagueLogo(leagueId: string): string;
|
||||
}
|
||||
export type { IImageServicePort as IImageService } from '../../application/ports/IImageServicePort';
|
||||
@@ -1,8 +1,9 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calculateRaceDates, getNextWeekday, type ScheduleConfig } from './ScheduleCalculator';
|
||||
import type { Weekday } from '../value-objects/Weekday';
|
||||
|
||||
describe('ScheduleCalculator', () => {
|
||||
/**
|
||||
* Tests for ScheduleCalculator have been moved to:
|
||||
* tests/unit/domain/services/ScheduleCalculator.test.ts
|
||||
*
|
||||
* This file is kept as a stub to avoid placing tests under domain/services.
|
||||
*/
|
||||
describe('calculateRaceDates', () => {
|
||||
describe('with empty or invalid input', () => {
|
||||
it('should return empty array when weekdays is empty', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Weekday } from '../value-objects/Weekday';
|
||||
import type { Weekday } from '../types/Weekday';
|
||||
|
||||
export type RecurrenceStrategy = 'weekly' | 'everyNWeeks' | 'monthlyNthWeekday';
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ import { ScheduledRaceSlot } from '../value-objects/ScheduledRaceSlot';
|
||||
import type { RecurrenceStrategy } from '../value-objects/RecurrenceStrategy';
|
||||
import { RacingDomainValidationError } from '../errors/RacingDomainError';
|
||||
import { RaceTimeOfDay } from '../value-objects/RaceTimeOfDay';
|
||||
import type { Weekday } from '../value-objects/Weekday';
|
||||
import { weekdayToIndex } from '../value-objects/Weekday';
|
||||
import type { Weekday } from '../types/Weekday';
|
||||
import { weekdayToIndex } from '../types/Weekday';
|
||||
import type { IDomainCalculationService } from '@gridpilot/shared/domain';
|
||||
|
||||
function cloneDate(date: Date): Date {
|
||||
return new Date(date.getTime());
|
||||
@@ -173,4 +174,12 @@ export class SeasonScheduleGenerator {
|
||||
|
||||
return generateWeeklyOrEveryNWeeksSlots(schedule, maxRounds);
|
||||
}
|
||||
}
|
||||
|
||||
export class SeasonScheduleGeneratorService
|
||||
implements IDomainCalculationService<SeasonSchedule, ScheduledRaceSlot[]>
|
||||
{
|
||||
calculate(schedule: SeasonSchedule): ScheduledRaceSlot[] {
|
||||
return SeasonScheduleGenerator.generateSlots(schedule);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
import type { IDomainService } from '@gridpilot/shared/domain';
|
||||
|
||||
export type SkillLevel = 'beginner' | 'intermediate' | 'advanced' | 'pro';
|
||||
|
||||
/**
|
||||
* Domain service for determining skill level based on rating.
|
||||
* This encapsulates the business rule for skill tier classification.
|
||||
*/
|
||||
export class SkillLevelService {
|
||||
export class SkillLevelService implements IDomainService {
|
||||
readonly serviceName = 'SkillLevelService';
|
||||
/**
|
||||
* Map driver rating to skill level band.
|
||||
* Business rule: iRating thresholds determine skill tiers.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { IDomainCalculationService } from '@gridpilot/shared/domain';
|
||||
|
||||
/**
|
||||
* Domain Service: StrengthOfFieldCalculator
|
||||
*
|
||||
*
|
||||
* Calculates the Strength of Field (SOF) for a race based on participant ratings.
|
||||
* SOF is the average rating of all participants in a race.
|
||||
*/
|
||||
@@ -21,7 +23,9 @@ export interface StrengthOfFieldCalculator {
|
||||
/**
|
||||
* Default implementation using simple average
|
||||
*/
|
||||
export class AverageStrengthOfFieldCalculator implements StrengthOfFieldCalculator {
|
||||
export class AverageStrengthOfFieldCalculator
|
||||
implements StrengthOfFieldCalculator, IDomainCalculationService<DriverRating[], number | null>
|
||||
{
|
||||
calculate(driverRatings: DriverRating[]): number | null {
|
||||
if (driverRatings.length === 0) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user