new db schema works perfectly first try frfr
This commit is contained in:
186
scripts/seed.ts
186
scripts/seed.ts
@@ -1,4 +1,5 @@
|
|||||||
import { drizzle } from 'drizzle-orm/libsql';
|
import { drizzle } from 'drizzle-orm/libsql';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
import { createClient } from '@libsql/client';
|
import { createClient } from '@libsql/client';
|
||||||
import * as schema from '../src/lib/server/db/schema.js';
|
import * as schema from '../src/lib/server/db/schema.js';
|
||||||
import { parse } from 'csv-parse/sync';
|
import { parse } from 'csv-parse/sync';
|
||||||
@@ -15,160 +16,145 @@ if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
|
|||||||
const client = createClient({ url: process.env.DATABASE_URL });
|
const client = createClient({ url: process.env.DATABASE_URL });
|
||||||
const db = drizzle(client, { schema });
|
const db = drizzle(client, { schema });
|
||||||
|
|
||||||
function readCSV(filename: string): Record<string, string>[] {
|
function readCSV(filename: string): Record<string, any>[] {
|
||||||
const content = readFileSync(join(dataDir, filename), 'utf-8');
|
const content = readFileSync(join(dataDir, filename), 'utf-8');
|
||||||
return parse(content, { columns: true, skip_empty_lines: true });
|
return parse(content, {
|
||||||
|
columns: true,
|
||||||
|
skip_empty_lines: true,
|
||||||
|
cast: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function seed() {
|
async function seed() {
|
||||||
console.log('Resetting database...');
|
console.log('Resetting database...');
|
||||||
|
|
||||||
|
// Disable foreign keys globally during setup to prevent structural mismatches
|
||||||
await client.execute('PRAGMA foreign_keys = OFF');
|
await client.execute('PRAGMA foreign_keys = OFF');
|
||||||
|
|
||||||
await db.delete(schema.eventAttributions);
|
await db.delete(schema.scoreLedger);
|
||||||
await db.delete(schema.ledgerScores);
|
await db.delete(schema.mainLedger);
|
||||||
await db.delete(schema.ledger);
|
await db.delete(schema.registeredPlayers);
|
||||||
await db.delete(schema.events);
|
await db.delete(schema.registeredEvents);
|
||||||
await db.delete(schema.players);
|
await db.delete(schema.eventTypes);
|
||||||
await db.delete(schema.scorers);
|
|
||||||
await db.delete(schema.scoringPresets);
|
await db.delete(schema.scoringPresets);
|
||||||
|
await db.delete(schema.players);
|
||||||
await db.delete(schema.divisions);
|
await db.delete(schema.divisions);
|
||||||
await db.delete(schema.teams);
|
await db.delete(schema.teams);
|
||||||
|
|
||||||
await client.execute('DELETE FROM sqlite_sequence');
|
await client.execute('DELETE FROM sqlite_sequence');
|
||||||
|
|
||||||
console.log('Database reset complete.');
|
console.log('Database reset complete. Seeding...');
|
||||||
|
|
||||||
console.log('Seeding teams...');
|
// --- 1. Teams ---
|
||||||
const teamsCSV = readCSV('teams.csv');
|
const teamsCSV = readCSV('teams.csv');
|
||||||
for (const row of teamsCSV) {
|
for (const row of teamsCSV) {
|
||||||
const result = await db
|
await db.insert(schema.teams).values({ name: row.team_name, color: row.color });
|
||||||
.insert(schema.teams)
|
|
||||||
.values({ name: row.name, color: row.color })
|
|
||||||
.returning();
|
|
||||||
console.log(` Inserted team: ${result[0].name} (id: ${result[0].id})`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding divisions...');
|
// --- 2. Divisions ---
|
||||||
const divisionsCSV = readCSV('divisions.csv');
|
const divisionsCSV = readCSV('divisions.csv');
|
||||||
for (const row of divisionsCSV) {
|
for (const row of divisionsCSV) {
|
||||||
const result = await db.insert(schema.divisions).values({ name: row.name }).returning();
|
await db.insert(schema.divisions).values({ name: row.div_name });
|
||||||
console.log(` Inserted division: ${result[0].name} (id: ${result[0].id})`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding scoring presets...');
|
// --- 3. Scoring Presets ---
|
||||||
const scoringPresetsCSV = readCSV('scoringPresets.csv');
|
const scoringPresetsCSV = readCSV('scoringPresets.csv');
|
||||||
for (const row of scoringPresetsCSV) {
|
for (const row of scoringPresetsCSV) {
|
||||||
const result = await db
|
await db.insert(schema.scoringPresets).values({
|
||||||
.insert(schema.scoringPresets)
|
presetID: row.preset,
|
||||||
.values({
|
placement: row.placement,
|
||||||
presetID: parseInt(row.presetID),
|
points: row.points
|
||||||
placement: parseInt(row.placement),
|
});
|
||||||
points: parseInt(row.points)
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
console.log(
|
|
||||||
` Inserted preset: ${result[0].presetID} placement ${result[0].placement} = ${result[0].points}pts`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding scorers...');
|
// Maps for dynamic relational lookups
|
||||||
const scorersCSV = readCSV('scorers.csv');
|
const dbTeams = await db.select().from(schema.teams);
|
||||||
for (const row of scorersCSV) {
|
const dbDivisions = await db.select().from(schema.divisions);
|
||||||
const result = await db
|
const teamMap = new Map(dbTeams.map((t) => [t.name, t.id]));
|
||||||
.insert(schema.scorers)
|
const divisionMap = new Map(dbDivisions.map((d) => [d.name, d.id]));
|
||||||
.values({
|
|
||||||
firstName: row.firstName,
|
|
||||||
lastName: row.lastName,
|
|
||||||
email: row.email,
|
|
||||||
password: row.password,
|
|
||||||
displayName: row.displayName,
|
|
||||||
role: row.role
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
console.log(` Inserted scorer: ${result[0].displayName}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Seeding players...');
|
|
||||||
const teams = await db.select().from(schema.teams);
|
|
||||||
const divisions = await db.select().from(schema.divisions);
|
|
||||||
const teamMap = new Map(teams.map((t) => [t.name, t.id]));
|
|
||||||
const divisionMap = new Map(divisions.map((d) => [d.name, d.id]));
|
|
||||||
|
|
||||||
|
// --- 4. Players ---
|
||||||
const playersCSV = readCSV('players.csv');
|
const playersCSV = readCSV('players.csv');
|
||||||
for (const row of playersCSV) {
|
for (const row of playersCSV) {
|
||||||
const teamId = teamMap.get(row.team);
|
const teamId = teamMap.get(row.team);
|
||||||
const divisionId = divisionMap.get(row.division);
|
const divisionId = divisionMap.get(row.division);
|
||||||
if (!teamId) throw new Error(`Team "${row.team}" not found`);
|
if (!teamId) throw new Error(`Team "${row.team}" not found`);
|
||||||
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
|
||||||
|
|
||||||
const result = await db
|
await db.insert(schema.players).values({
|
||||||
.insert(schema.players)
|
firstName: row.firstName,
|
||||||
.values({
|
lastName: row.lastName,
|
||||||
firstName: row.firstName,
|
team: teamId,
|
||||||
lastName: row.lastName,
|
division: divisionId || null
|
||||||
team: teamId,
|
});
|
||||||
division: divisionId
|
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
console.log(` Inserted player: ${result[0].firstName} ${result[0].lastName}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding events...');
|
// --- 5. Event Types ---
|
||||||
const scoringPresets = await db.select().from(schema.scoringPresets);
|
const eventTypesCSV = readCSV('eventTypes.csv');
|
||||||
const presetMap = new Map(scoringPresets.map((p) => [p.presetID, p.presetID]));
|
for (const row of eventTypesCSV) {
|
||||||
const divisionMapForEvents = new Map(divisions.map((d) => [d.name, d.id]));
|
await db.insert(schema.eventTypes).values({
|
||||||
|
name: row.event_name,
|
||||||
|
preset: row.preset
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const eventsCSV = readCSV('events.csv');
|
const dbEventTypes = await db.select().from(schema.eventTypes);
|
||||||
for (const row of eventsCSV) {
|
const eventTypeMap = new Map(dbEventTypes.map((et) => [et.name, et.id]));
|
||||||
const preset = parseInt(row.preset);
|
|
||||||
const divisionId = divisionMapForEvents.get(row.division);
|
// --- 6. Registered Events ---
|
||||||
|
// Change the Map key type to a string (the event name)
|
||||||
|
const eventNameMap = new Map<string, number>();
|
||||||
|
const registeredEventsCSV = readCSV('registeredEvents.csv');
|
||||||
|
|
||||||
|
for (const row of registeredEventsCSV) {
|
||||||
|
const eventTypeId = eventTypeMap.get(row.event_type);
|
||||||
|
const divisionId = divisionMap.get(row.division);
|
||||||
|
|
||||||
|
if (!eventTypeId) throw new Error(`Event Type "${row.event_type}" not found`);
|
||||||
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
if (!divisionId) throw new Error(`Division "${row.division}" not found`);
|
||||||
|
|
||||||
const result = await db
|
const [inserted] = await db
|
||||||
.insert(schema.events)
|
.insert(schema.registeredEvents)
|
||||||
.values({
|
.values({
|
||||||
name: row.name,
|
eventType: eventTypeId,
|
||||||
preset,
|
|
||||||
division: divisionId,
|
division: divisionId,
|
||||||
order: parseInt(row.order),
|
state: row.event_state || 0,
|
||||||
state: parseInt(row.state)
|
timeCompleted: row.time_completed || null
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
console.log(` Inserted event: ${result[0].name}`);
|
|
||||||
|
// Map the textual event name (e.g., "100m Sprint") to the generated DB ID
|
||||||
|
eventNameMap.set(row.event_type, inserted.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Seeding event attributions...');
|
// --- 7. Registered Players ---
|
||||||
const events = await db.select().from(schema.events);
|
const dbPlayers = await db.select().from(schema.players);
|
||||||
const players = await db.select().from(schema.players);
|
const playerMap = new Map(dbPlayers.map((p) => [`${p.firstName} ${p.lastName}`, p.id]));
|
||||||
const eventMap = new Map(events.map((e) => [e.name, e.id]));
|
const registeredPlayersCSV = readCSV('registeredPlayers.csv');
|
||||||
const playerMap = new Map(players.map((p) => [`${p.firstName} ${p.lastName}`, p.id]));
|
|
||||||
|
|
||||||
const eventAttributionsCSV = readCSV('eventAttributions.csv');
|
for (const row of registeredPlayersCSV) {
|
||||||
for (const row of eventAttributionsCSV) {
|
const playerId = playerMap.get(row.player_registered);
|
||||||
const eventId = eventMap.get(row.eventID);
|
// Look up using the string event name now
|
||||||
const playerId = playerMap.get(row.playerID);
|
const actualEventId = eventNameMap.get(row.event_registered);
|
||||||
|
|
||||||
if (!eventId) throw new Error(`Event "${row.eventID}" not found`);
|
if (!playerId) throw new Error(`Player "${row.player_registered}" not found`);
|
||||||
if (!playerId) throw new Error(`Player "${row.playerID}" not found`);
|
if (!actualEventId)
|
||||||
|
throw new Error(`Registered Event named "${row.event_registered}" not found`);
|
||||||
|
|
||||||
const result = await db
|
await db.insert(schema.registeredPlayers).values({
|
||||||
.insert(schema.eventAttributions)
|
playerID: playerId,
|
||||||
.values({
|
registeredEventID: actualEventId,
|
||||||
eventID: eventId,
|
placement: row.player_placement || 0
|
||||||
playerID: playerId
|
});
|
||||||
})
|
|
||||||
.returning();
|
|
||||||
console.log(` Inserted attribution: event=${row.eventID}, player=${row.playerID}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\nSeeding complete!');
|
// Re-enable Foreign Key constraints now that the data is built cleanly
|
||||||
console.log('Ledger and ledgerScores tables are empty (reset).');
|
await client.execute('PRAGMA foreign_keys = ON');
|
||||||
|
|
||||||
|
console.log('\n✅ Seeding complete!');
|
||||||
await client.close();
|
await client.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
seed().catch((err) => {
|
seed().catch((err) => {
|
||||||
console.error('Seed failed:', err);
|
console.error('❌ Seed failed:', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { sql, eq } from 'drizzle-orm';
|
import { sql, eq } from 'drizzle-orm';
|
||||||
import { integer, sqliteTable, text, sqliteView } from 'drizzle-orm/sqlite-core';
|
import { integer, sqliteTable, text, sqliteView } from 'drizzle-orm/sqlite-core';
|
||||||
|
|
||||||
|
export const players = sqliteTable('players', {
|
||||||
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
|
firstName: text('firstName').notNull(),
|
||||||
|
lastName: text('lastName').notNull(),
|
||||||
|
team: integer('team')
|
||||||
|
.references(() => teams.id)
|
||||||
|
.notNull(),
|
||||||
|
division: integer('division').references(() => divisions.id)
|
||||||
|
});
|
||||||
|
|
||||||
export const teams = sqliteTable('teams', {
|
export const teams = sqliteTable('teams', {
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
name: text('name').notNull(),
|
name: text('team_name').notNull(),
|
||||||
color: text('color').notNull().default('white')
|
color: text('color').notNull().default('white')
|
||||||
});
|
});
|
||||||
|
|
||||||
export const divisions = sqliteTable('divisions', {
|
export const divisions = sqliteTable('divisions', {
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
name: text('name').notNull()
|
name: text('div_name').notNull()
|
||||||
});
|
|
||||||
|
|
||||||
export const eventAttributions = sqliteTable('eventAttributions', {
|
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
||||||
eventID: integer('eventID')
|
|
||||||
.references(() => events.id)
|
|
||||||
.notNull(),
|
|
||||||
playerID: integer('playerID')
|
|
||||||
.references(() => players.id)
|
|
||||||
.notNull()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const scoringPresets = sqliteTable('scoringPresets', {
|
export const scoringPresets = sqliteTable('scoringPresets', {
|
||||||
@@ -29,62 +29,56 @@ export const scoringPresets = sqliteTable('scoringPresets', {
|
|||||||
points: integer('points').notNull().default(0)
|
points: integer('points').notNull().default(0)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const events = sqliteTable('events', {
|
export const eventTypes = sqliteTable('eventTypes', {
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
name: text('name').notNull(),
|
name: text('event_name').notNull(),
|
||||||
preset: integer('preset')
|
preset: integer('preset')
|
||||||
.references(() => scoringPresets.presetID)
|
.references(() => scoringPresets.presetID)
|
||||||
|
.notNull()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const registeredEvents = sqliteTable('registeredEvents', {
|
||||||
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
|
eventType: integer('event_type')
|
||||||
|
.references(() => eventTypes.id)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
division: integer('division')
|
division: integer('division')
|
||||||
.references(() => divisions.id)
|
.references(() => divisions.id)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
order: integer('order').notNull(),
|
state: integer('event_state').notNull().default(0),
|
||||||
state: integer('state').default(0).notNull(),
|
timeCompleted: integer('time_completed')
|
||||||
timeCompleted: integer('timeCompleted')
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const players = sqliteTable('players', {
|
export const registeredPlayers = sqliteTable('registeredPlayers', {
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
firstName: text('firstName').notNull(),
|
playerID: integer('player_registered')
|
||||||
lastName: text('lastName').notNull(),
|
|
||||||
team: integer('team')
|
|
||||||
.references(() => teams.id)
|
|
||||||
.notNull(),
|
|
||||||
division: integer('division')
|
|
||||||
.references(() => divisions.id)
|
|
||||||
.notNull()
|
|
||||||
});
|
|
||||||
|
|
||||||
export const scorers = sqliteTable('scorers', {
|
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
||||||
firstName: text('firstName').notNull(),
|
|
||||||
lastName: text('lastName').notNull(),
|
|
||||||
email: text('email').notNull(),
|
|
||||||
password: text('password').notNull(),
|
|
||||||
displayName: text('displayName').notNull(),
|
|
||||||
role: text('role').notNull().default('scorer')
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ledger = sqliteTable('ledger', {
|
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
||||||
timestamp: integer('timestamp', { mode: 'timestamp' })
|
|
||||||
.notNull()
|
|
||||||
.default(sql`(unixepoch())`),
|
|
||||||
type: text('type').notNull().default('event'),
|
|
||||||
event: integer('event').references(() => events.id),
|
|
||||||
scorer: integer('scorer').references(() => scorers.id)
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ledgerScores = sqliteTable('ledgerScores', {
|
|
||||||
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
||||||
ledgerID: integer('ledgerID')
|
|
||||||
.references(() => ledger.id)
|
|
||||||
.notNull(),
|
|
||||||
player: integer('player')
|
|
||||||
.references(() => players.id)
|
.references(() => players.id)
|
||||||
.notNull(),
|
.notNull(),
|
||||||
placement: integer('placement').references(() => scoringPresets.placement),
|
registeredEventID: integer('event_registered')
|
||||||
points: integer('points').notNull().default(0)
|
.references(() => registeredEvents.id)
|
||||||
|
.notNull(),
|
||||||
|
placement: integer('player_placement').notNull().default(0)
|
||||||
|
});
|
||||||
|
|
||||||
|
export const mainLedger = sqliteTable('mainLedger', {
|
||||||
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
|
timestamp: integer('ledger_timestamp', { mode: 'timestamp' })
|
||||||
|
.notNull()
|
||||||
|
.default(sql`(unixepoch())`),
|
||||||
|
updateType: text('update_type').notNull().default('event'),
|
||||||
|
registeredEvent: integer('registered_event_reference').references(() => registeredEvents.id),
|
||||||
|
scorer: text('scorer_logged').default('some dude idk')
|
||||||
|
});
|
||||||
|
|
||||||
|
export const scoreLedger = sqliteTable('scoresLedger', {
|
||||||
|
id: integer('id').primaryKey({ autoIncrement: true }),
|
||||||
|
ledgerID: integer('ledger_address')
|
||||||
|
.references(() => mainLedger.id)
|
||||||
|
.notNull(),
|
||||||
|
teamID: integer('team_being_scored')
|
||||||
|
.references(() => teams.id)
|
||||||
|
.notNull(),
|
||||||
|
points: integer('pointsAwarded').default(0).notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const teamScoresView = sqliteView('teamScoresView').as((qb) => {
|
export const teamScoresView = sqliteView('teamScoresView').as((qb) => {
|
||||||
@@ -93,28 +87,9 @@ export const teamScoresView = sqliteView('teamScoresView').as((qb) => {
|
|||||||
teamId: teams.id,
|
teamId: teams.id,
|
||||||
teamName: teams.name,
|
teamName: teams.name,
|
||||||
teamColor: teams.color,
|
teamColor: teams.color,
|
||||||
totalPoints: sql<number>`sum(${ledgerScores.points})`.mapWith(Number).as('totalPoints')
|
totalPoints: sql<number>`sum(${scoreLedger.points})`.mapWith(Number).as('totalPoints')
|
||||||
})
|
})
|
||||||
.from(teams)
|
.from(teams)
|
||||||
.leftJoin(ledgerScores, eq(teams.id, ledgerScores.player))
|
.leftJoin(scoreLedger, eq(teams.id, scoreLedger.teamID))
|
||||||
.groupBy(teams.id);
|
.groupBy(teams.id, teams.name, teams.color);
|
||||||
});
|
|
||||||
|
|
||||||
export const playerDetailsView = sqliteView('playerDetailsView').as((qb) => {
|
|
||||||
return qb
|
|
||||||
.select({
|
|
||||||
playerId: players.id,
|
|
||||||
firstName: players.firstName,
|
|
||||||
lastName: players.lastName,
|
|
||||||
teamID: teams.id,
|
|
||||||
teamName: teams.name,
|
|
||||||
teamColor: teams.color,
|
|
||||||
divisionId: divisions.id,
|
|
||||||
eventId: events.id
|
|
||||||
})
|
|
||||||
.from(players)
|
|
||||||
.innerJoin(teams, eq(players.team, teams.id))
|
|
||||||
.innerJoin(divisions, eq(players.division, divisions.id))
|
|
||||||
.leftJoin(eventAttributions, eq(players.id, eventAttributions.playerID))
|
|
||||||
.leftJoin(events, eq(eventAttributions.eventID, events.id));
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user