238 lines
7.6 KiB
TypeScript
238 lines
7.6 KiB
TypeScript
import { drizzle } from 'drizzle-orm/libsql';
|
||
import { eq } from 'drizzle-orm';
|
||
import { createClient } from '@libsql/client';
|
||
import * as schema from '../src/lib/server/db/schema.js';
|
||
import { parse } from 'csv-parse/sync';
|
||
import { readFileSync } from 'fs';
|
||
import { fileURLToPath } from 'url';
|
||
import { dirname, join } from 'path';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = dirname(__filename);
|
||
const dataDir = join(__dirname, 'data');
|
||
|
||
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
||
|
||
const client = createClient({ url: process.env.DATABASE_URL });
|
||
const db = drizzle(client, { schema });
|
||
|
||
function readCSV(filename: string): Record<string, any>[] {
|
||
const content = readFileSync(join(dataDir, filename), 'utf-8');
|
||
return parse(content, {
|
||
columns: true,
|
||
skip_empty_lines: true,
|
||
cast: true
|
||
});
|
||
}
|
||
|
||
async function seed() {
|
||
console.log('Resetting database...');
|
||
|
||
// Temporarily disable FK checks during reset
|
||
await client.execute('PRAGMA foreign_keys = OFF');
|
||
|
||
await db.delete(schema.scoreLedger);
|
||
await db.delete(schema.mainLedger);
|
||
await db.delete(schema.registeredPlayers);
|
||
await db.delete(schema.registeredEvents);
|
||
await db.delete(schema.registeredResults);
|
||
await db.delete(schema.resultPresets);
|
||
await db.delete(schema.eventTypes);
|
||
await db.delete(schema.scoringPresets);
|
||
await db.delete(schema.brackets);
|
||
await db.delete(schema.players);
|
||
await db.delete(schema.divisions);
|
||
await db.delete(schema.teams);
|
||
await db.delete(schema.scorers);
|
||
await db.delete(schema.sessions);
|
||
|
||
await client.execute('DELETE FROM sqlite_sequence');
|
||
|
||
console.log('Database reset complete. Seeding...');
|
||
|
||
const { hash } = await import('../src/lib/server/password.js');
|
||
let passwordHash = await hash('password');
|
||
await db
|
||
.insert(schema.scorers)
|
||
.values({
|
||
id: crypto.randomUUID(),
|
||
username: 'admin',
|
||
role: 'admin',
|
||
passwordHash: passwordHash
|
||
});
|
||
|
||
// Seed teams
|
||
const teamsCSV = readCSV('teams.csv');
|
||
for (const row of teamsCSV) {
|
||
await db.insert(schema.teams).values({ name: row.team_name, color: row.color });
|
||
console.log(` → Team: ${row.team_name} (${row.color})`);
|
||
}
|
||
|
||
// Seed divisions
|
||
const divisionsCSV = readCSV('divisions.csv');
|
||
for (const row of divisionsCSV) {
|
||
await db.insert(schema.divisions).values({ name: row.div_name });
|
||
console.log(` → Division: ${row.div_name}`);
|
||
}
|
||
|
||
// Seed brackets
|
||
const bracketsCSV = readCSV('brackets.csv');
|
||
for (const row of bracketsCSV) {
|
||
await db.insert(schema.brackets).values({ name: row.bracket_name });
|
||
console.log(` → Bracket: ${row.bracket_name}`);
|
||
}
|
||
|
||
// Seed result presets
|
||
const resultPresetsCSV = readCSV('resultPresets.csv');
|
||
for (const row of resultPresetsCSV) {
|
||
await db.insert(schema.resultPresets).values({
|
||
presetName: row.presetName,
|
||
numberOfResults: row.numberOfResults,
|
||
unit: row.unit,
|
||
lowerIsBetter: row.lowerIsBetter,
|
||
averageResults: row.averageResults
|
||
});
|
||
console.log(
|
||
` → Result Preset ${row.presetName}: ${row.numberOfResults} results, measured in ${row.unit}`
|
||
);
|
||
}
|
||
|
||
// Seed scoring presets
|
||
const scoringPresetsCSV = readCSV('scoringPresets.csv');
|
||
for (const row of scoringPresetsCSV) {
|
||
await db.insert(schema.scoringPresets).values({
|
||
presetID: row.preset,
|
||
placement: row.placement,
|
||
points: row.points
|
||
});
|
||
console.log(` → Preset ${row.preset}: placement ${row.placement} = ${row.points}pts`);
|
||
}
|
||
|
||
// Build lookup maps for relational seeding
|
||
const dbTeams = await db.select().from(schema.teams);
|
||
const dbDivisions = await db.select().from(schema.divisions);
|
||
const dbResults = await db.select().from(schema.resultPresets);
|
||
const dbBrackets = await db.select().from(schema.brackets);
|
||
|
||
const teamMap = new Map(dbTeams.map((t) => [t.name, t.id]));
|
||
const divisionMap = new Map(dbDivisions.map((d) => [d.name, d.id]));
|
||
const divisionNameMap = new Map([...divisionMap.entries()].map(([name, id]) => [id, name]));
|
||
const bracketMap = new Map(dbBrackets.map((b) => [b.name, b.id]));
|
||
const resultPresetMap = new Map(dbResults.map((b) => [b.presetName, b.id]));
|
||
|
||
// Seed players
|
||
const playersCSV = readCSV('players.csv');
|
||
for (const row of playersCSV) {
|
||
const teamId = teamMap.get(row.team);
|
||
const divisionId = divisionMap.get(row.division);
|
||
if (!teamId) throw new Error(`Team "${row.team}" not found`);
|
||
|
||
let firstName = row.firstName;
|
||
let lastName = row.lastName;
|
||
|
||
// If lastName is empty, default to a space (column is nullable in practice)
|
||
if (!lastName) {
|
||
lastName = ' ';
|
||
}
|
||
|
||
// If firstName contains a space, treat the second word as lastName (only if lastName was blank)
|
||
if (lastName === ' ' && firstName && firstName.includes(' ')) {
|
||
const parts = firstName.split(' ');
|
||
firstName = parts[0];
|
||
lastName = parts.slice(1).join(' ');
|
||
}
|
||
|
||
await db.insert(schema.players).values({
|
||
firstName,
|
||
lastName,
|
||
team: teamId,
|
||
division: divisionId || null
|
||
});
|
||
console.log(
|
||
` → Player: ${row.firstName} ${row.lastName} | team ${teamId} | division ${divisionId}`
|
||
);
|
||
}
|
||
|
||
// Seed event types
|
||
const eventTypesCSV = readCSV('eventTypes.csv');
|
||
for (const row of eventTypesCSV) {
|
||
const presetId = resultPresetMap.get(row.resultPreset);
|
||
if (!presetId) throw new Error(`Team "${row.resultPreset}" not found`);
|
||
await db.insert(schema.eventTypes).values({
|
||
name: row.event_name,
|
||
scoringPreset: row.preset,
|
||
resultPreset: presetId
|
||
});
|
||
console.log(
|
||
` → Event Type: ${row.event_name} (preset ${row.preset}, resultPreset: ${row.resultPreset})`
|
||
);
|
||
}
|
||
|
||
const dbEventTypes = await db.select().from(schema.eventTypes);
|
||
const eventTypeMap = new Map(dbEventTypes.map((et) => [et.name, et.id]));
|
||
|
||
// Seed registered events (auto-generated from event types × divisions, ordered by division then event type)
|
||
const eventNameMap = new Map<string, number>();
|
||
|
||
for (const division of dbDivisions) {
|
||
for (const eventType of dbEventTypes) {
|
||
const [inserted] = await db
|
||
.insert(schema.registeredEvents)
|
||
.values({
|
||
eventType: eventType.id,
|
||
division: division.id,
|
||
state: 0,
|
||
timeCompleted: null,
|
||
teamWinner: null
|
||
})
|
||
.returning();
|
||
console.log(
|
||
` → Registered Event [id:${inserted.id}]: ${eventType.name} | ${division.name}`
|
||
);
|
||
|
||
eventNameMap.set(`${eventType.name}|${division.name}`, inserted.id);
|
||
}
|
||
}
|
||
|
||
// Seed registered players (linking players to events)
|
||
const dbPlayers = await db.select().from(schema.players);
|
||
const playerMap = new Map(dbPlayers.map((p) => [`${p.firstName} ${p.lastName}`, p]));
|
||
|
||
const registeredPlayersCSV = readCSV('registeredPlayers.csv');
|
||
for (const row of registeredPlayersCSV) {
|
||
const player = playerMap.get(row.player_registered);
|
||
const divisionName = divisionNameMap.get(player?.division ?? -1);
|
||
const actualEventId = eventNameMap.get(`${row.event_registered}|${divisionName}`);
|
||
|
||
const bracketId = bracketMap.get(row.bracket);
|
||
|
||
if (!player) throw new Error(`Player "${row.player_registered}" not found`);
|
||
if (!actualEventId)
|
||
throw new Error(
|
||
`Registered Event "${row.event_registered}" for division "${divisionName}" not found`
|
||
);
|
||
if (!bracketId) throw new Error(`Bracket "${row.bracket}" not found in database`);
|
||
|
||
await db.insert(schema.registeredPlayers).values({
|
||
playerID: player.id,
|
||
registeredEventID: actualEventId,
|
||
bracket: bracketId,
|
||
placement: row.player_placement || 0
|
||
});
|
||
console.log(
|
||
` → Registered Player: ${row.player_registered} → ${row.event_registered} (division: ${divisionName}, bracket: ${row.bracket} [id:${bracketId}]) [eventId:${actualEventId}]`
|
||
);
|
||
}
|
||
|
||
// Re-enable FK checks
|
||
await client.execute('PRAGMA foreign_keys = ON');
|
||
|
||
console.log('\n✅ Seeding complete!');
|
||
client.close();
|
||
}
|
||
|
||
seed().catch((err) => {
|
||
console.error('❌ Seed failed:', err);
|
||
process.exit(1);
|
||
});
|