From 3624ce9c00567fa14dda5c9deb4a8adf0e4b8111 Mon Sep 17 00:00:00 2001 From: voidarc Date: Fri, 22 May 2026 17:22:24 +0100 Subject: [PATCH] new db schema works perfectly first try frfr --- scripts/seed.ts | 186 +++++++++++++++++------------------- src/lib/server/db/schema.ts | 131 ++++++++++--------------- 2 files changed, 139 insertions(+), 178 deletions(-) diff --git a/scripts/seed.ts b/scripts/seed.ts index d1f136c..3ae3302 100644 --- a/scripts/seed.ts +++ b/scripts/seed.ts @@ -1,4 +1,5 @@ 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'; @@ -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 db = drizzle(client, { schema }); -function readCSV(filename: string): Record[] { +function readCSV(filename: string): Record[] { 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() { console.log('Resetting database...'); + // Disable foreign keys globally during setup to prevent structural mismatches await client.execute('PRAGMA foreign_keys = OFF'); - await db.delete(schema.eventAttributions); - await db.delete(schema.ledgerScores); - await db.delete(schema.ledger); - await db.delete(schema.events); - await db.delete(schema.players); - await db.delete(schema.scorers); + await db.delete(schema.scoreLedger); + await db.delete(schema.mainLedger); + await db.delete(schema.registeredPlayers); + await db.delete(schema.registeredEvents); + await db.delete(schema.eventTypes); await db.delete(schema.scoringPresets); + await db.delete(schema.players); await db.delete(schema.divisions); await db.delete(schema.teams); 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'); for (const row of teamsCSV) { - const result = await db - .insert(schema.teams) - .values({ name: row.name, color: row.color }) - .returning(); - console.log(` Inserted team: ${result[0].name} (id: ${result[0].id})`); + await db.insert(schema.teams).values({ name: row.team_name, color: row.color }); } - console.log('Seeding divisions...'); + // --- 2. Divisions --- const divisionsCSV = readCSV('divisions.csv'); for (const row of divisionsCSV) { - const result = await db.insert(schema.divisions).values({ name: row.name }).returning(); - console.log(` Inserted division: ${result[0].name} (id: ${result[0].id})`); + await db.insert(schema.divisions).values({ name: row.div_name }); } - console.log('Seeding scoring presets...'); + // --- 3. Scoring Presets --- const scoringPresetsCSV = readCSV('scoringPresets.csv'); for (const row of scoringPresetsCSV) { - const result = await db - .insert(schema.scoringPresets) - .values({ - presetID: parseInt(row.presetID), - placement: parseInt(row.placement), - points: parseInt(row.points) - }) - .returning(); - console.log( - ` Inserted preset: ${result[0].presetID} placement ${result[0].placement} = ${result[0].points}pts` - ); + await db.insert(schema.scoringPresets).values({ + presetID: row.preset, + placement: row.placement, + points: row.points + }); } - console.log('Seeding scorers...'); - const scorersCSV = readCSV('scorers.csv'); - for (const row of scorersCSV) { - const result = await db - .insert(schema.scorers) - .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])); + // Maps for dynamic relational lookups + const dbTeams = await db.select().from(schema.teams); + const dbDivisions = await db.select().from(schema.divisions); + const teamMap = new Map(dbTeams.map((t) => [t.name, t.id])); + const divisionMap = new Map(dbDivisions.map((d) => [d.name, d.id])); + // --- 4. 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`); - if (!divisionId) throw new Error(`Division "${row.division}" not found`); - const result = await db - .insert(schema.players) - .values({ - firstName: row.firstName, - lastName: row.lastName, - team: teamId, - division: divisionId - }) - .returning(); - console.log(` Inserted player: ${result[0].firstName} ${result[0].lastName}`); + await db.insert(schema.players).values({ + firstName: row.firstName, + lastName: row.lastName, + team: teamId, + division: divisionId || null + }); } - console.log('Seeding events...'); - const scoringPresets = await db.select().from(schema.scoringPresets); - const presetMap = new Map(scoringPresets.map((p) => [p.presetID, p.presetID])); - const divisionMapForEvents = new Map(divisions.map((d) => [d.name, d.id])); + // --- 5. Event Types --- + const eventTypesCSV = readCSV('eventTypes.csv'); + for (const row of eventTypesCSV) { + await db.insert(schema.eventTypes).values({ + name: row.event_name, + preset: row.preset + }); + } - const eventsCSV = readCSV('events.csv'); - for (const row of eventsCSV) { - const preset = parseInt(row.preset); - const divisionId = divisionMapForEvents.get(row.division); + const dbEventTypes = await db.select().from(schema.eventTypes); + const eventTypeMap = new Map(dbEventTypes.map((et) => [et.name, et.id])); + + // --- 6. Registered Events --- + // Change the Map key type to a string (the event name) + const eventNameMap = new Map(); + 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`); - const result = await db - .insert(schema.events) + const [inserted] = await db + .insert(schema.registeredEvents) .values({ - name: row.name, - preset, + eventType: eventTypeId, division: divisionId, - order: parseInt(row.order), - state: parseInt(row.state) + state: row.event_state || 0, + timeCompleted: row.time_completed || null }) .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...'); - const events = await db.select().from(schema.events); - const players = await db.select().from(schema.players); - const eventMap = new Map(events.map((e) => [e.name, e.id])); - const playerMap = new Map(players.map((p) => [`${p.firstName} ${p.lastName}`, p.id])); + // --- 7. Registered Players --- + const dbPlayers = await db.select().from(schema.players); + const playerMap = new Map(dbPlayers.map((p) => [`${p.firstName} ${p.lastName}`, p.id])); + const registeredPlayersCSV = readCSV('registeredPlayers.csv'); - const eventAttributionsCSV = readCSV('eventAttributions.csv'); - for (const row of eventAttributionsCSV) { - const eventId = eventMap.get(row.eventID); - const playerId = playerMap.get(row.playerID); + for (const row of registeredPlayersCSV) { + const playerId = playerMap.get(row.player_registered); + // Look up using the string event name now + const actualEventId = eventNameMap.get(row.event_registered); - if (!eventId) throw new Error(`Event "${row.eventID}" not found`); - if (!playerId) throw new Error(`Player "${row.playerID}" not found`); + if (!playerId) throw new Error(`Player "${row.player_registered}" not found`); + if (!actualEventId) + throw new Error(`Registered Event named "${row.event_registered}" not found`); - const result = await db - .insert(schema.eventAttributions) - .values({ - eventID: eventId, - playerID: playerId - }) - .returning(); - console.log(` Inserted attribution: event=${row.eventID}, player=${row.playerID}`); + await db.insert(schema.registeredPlayers).values({ + playerID: playerId, + registeredEventID: actualEventId, + placement: row.player_placement || 0 + }); } - console.log('\nSeeding complete!'); - console.log('Ledger and ledgerScores tables are empty (reset).'); + // Re-enable Foreign Key constraints now that the data is built cleanly + await client.execute('PRAGMA foreign_keys = ON'); + console.log('\nāœ… Seeding complete!'); await client.close(); } seed().catch((err) => { - console.error('Seed failed:', err); + console.error('āŒ Seed failed:', err); process.exit(1); }); diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index a1c4446..1c8cc9b 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -1,25 +1,25 @@ import { sql, eq } from 'drizzle-orm'; 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', { id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), + name: text('team_name').notNull(), color: text('color').notNull().default('white') }); export const divisions = sqliteTable('divisions', { id: integer('id').primaryKey({ autoIncrement: true }), - name: text('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() + name: text('div_name').notNull() }); export const scoringPresets = sqliteTable('scoringPresets', { @@ -29,62 +29,56 @@ export const scoringPresets = sqliteTable('scoringPresets', { points: integer('points').notNull().default(0) }); -export const events = sqliteTable('events', { +export const eventTypes = sqliteTable('eventTypes', { id: integer('id').primaryKey({ autoIncrement: true }), - name: text('name').notNull(), + name: text('event_name').notNull(), preset: integer('preset') .references(() => scoringPresets.presetID) + .notNull() +}); + +export const registeredEvents = sqliteTable('registeredEvents', { + id: integer('id').primaryKey({ autoIncrement: true }), + eventType: integer('event_type') + .references(() => eventTypes.id) .notNull(), division: integer('division') .references(() => divisions.id) .notNull(), - order: integer('order').notNull(), - state: integer('state').default(0).notNull(), - timeCompleted: integer('timeCompleted') + state: integer('event_state').notNull().default(0), + timeCompleted: integer('time_completed') }); -export const players = sqliteTable('players', { +export const registeredPlayers = sqliteTable('registeredPlayers', { 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) - .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') + playerID: integer('player_registered') .references(() => players.id) .notNull(), - placement: integer('placement').references(() => scoringPresets.placement), - points: integer('points').notNull().default(0) + registeredEventID: integer('event_registered') + .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) => { @@ -93,28 +87,9 @@ export const teamScoresView = sqliteView('teamScoresView').as((qb) => { teamId: teams.id, teamName: teams.name, teamColor: teams.color, - totalPoints: sql`sum(${ledgerScores.points})`.mapWith(Number).as('totalPoints') + totalPoints: sql`sum(${scoreLedger.points})`.mapWith(Number).as('totalPoints') }) .from(teams) - .leftJoin(ledgerScores, eq(teams.id, ledgerScores.player)) - .groupBy(teams.id); -}); - -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)); + .leftJoin(scoreLedger, eq(teams.id, scoreLedger.teamID)) + .groupBy(teams.id, teams.name, teams.color); });