seed data

This commit is contained in:
2025-12-30 00:15:35 +01:00
parent 7a853d4e43
commit ccaa39c39c
22 changed files with 1342 additions and 173 deletions

View File

@@ -259,6 +259,101 @@ export class RacingStewardingFactory {
}
}
// Add comprehensive penalty coverage for all types and statuses
// This ensures we have examples of every penalty type in every status
const penaltyTypes = ['time_penalty', 'grid_penalty', 'points_deduction', 'disqualification', 'warning', 'license_points', 'probation', 'fine', 'race_ban'] as const;
const penaltyStatuses = ['pending', 'applied', 'appealed', 'overturned'] as const;
// Get some races and members for penalty seeding
const allLeagueIds = Array.from(racesByLeague.keys());
for (const leagueId of allLeagueIds) {
const members = activeMembersByLeague.get(leagueId) ?? [];
if (members.length < 2) continue;
const leagueRaces = racesByLeague.get(leagueId) ?? [];
const completedRaces = leagueRaces.filter((r) => r.status.toString() === 'completed');
if (completedRaces.length === 0) continue;
const steward = members[0]!;
const targetDriver = members[1]!;
// Create one penalty for each type/status combination
let penaltyIndex = 0;
for (const type of penaltyTypes) {
for (const status of penaltyStatuses) {
// Skip some combinations to avoid too many records
if (faker.number.int({ min: 0, max: 2 }) > 0) continue;
const race = faker.helpers.arrayElement(completedRaces);
const value = type === 'time_penalty' ? faker.number.int({ min: 5, max: 30 }) :
type === 'grid_penalty' ? faker.number.int({ min: 1, max: 5 }) :
type === 'points_deduction' ? faker.number.int({ min: 2, max: 10 }) :
type === 'license_points' ? faker.number.int({ min: 1, max: 4 }) :
type === 'fine' ? faker.number.int({ min: 50, max: 500 }) :
type === 'race_ban' ? faker.number.int({ min: 1, max: 3 }) :
type === 'warning' ? 1 :
1; // disqualification, probation have no value
const penaltyData: {
id: string;
leagueId: string;
raceId: string;
driverId: string;
type: typeof penaltyTypes[number];
value?: number;
reason: string;
issuedBy: string;
status: typeof penaltyStatuses[number];
issuedAt: Date;
appliedAt?: Date;
notes?: string;
} = {
id: seedId(`penalty-${leagueId}-${type}-${status}-${penaltyIndex}`, this.persistence),
leagueId,
raceId: race.id.toString(),
driverId: targetDriver,
type,
reason: this.getPenaltyReason(type),
issuedBy: steward,
status,
issuedAt: faker.date.recent({ days: faker.number.int({ min: 1, max: 30 }), refDate: this.baseDate }),
};
// Add value only for types that require it
if (type !== 'warning') {
penaltyData.value = value;
}
if (status === 'applied') {
penaltyData.appliedAt = faker.date.recent({ days: faker.number.int({ min: 1, max: 20 }), refDate: this.baseDate });
}
if (type === 'race_ban') {
penaltyData.notes = 'Multiple serious violations';
}
penalties.push(Penalty.create(penaltyData));
penaltyIndex++;
}
}
}
return { protests, penalties };
}
private getPenaltyReason(type: string): string {
const reasons = {
time_penalty: ['Avoidable contact', 'Track limits abuse', 'Unsafe rejoin', 'Blocking'],
grid_penalty: ['Qualifying infringement', 'Parc fermé violation', 'Practice session breach'],
points_deduction: ['Serious breach of rules', 'Multiple incidents', 'Unsportsmanlike conduct'],
disqualification: ['Severe dangerous driving', 'Gross misconduct', 'Multiple serious violations'],
warning: ['Track limits reminder', 'Minor contact', 'Procedure reminder'],
license_points: ['General misconduct', 'Minor incidents', 'Warning escalation'],
probation: ['Pattern of minor violations', 'Behavioral concerns', 'Conditional status'],
fine: ['Financial penalty for rule breach', 'Administrative violation', 'Late entry fee'],
race_ban: ['Multiple race bans', 'Severe dangerous driving', 'Gross misconduct'],
};
return faker.helpers.arrayElement(reasons[type as keyof typeof reasons] || ['Rule violation']);
}
}