From 16acfd2b0573008b307e7602f5168d3842ccd8c0 Mon Sep 17 00:00:00 2001 From: voidarc Date: Wed, 6 May 2026 15:47:20 +0100 Subject: [PATCH] added script to reset and seed database (thanks opencode) --- .gitignore | 2 + .session | 15 +- bun.lock | 8 + drizzle/0000_seed_initial_data.sql | 7 - drizzle/meta/0000_snapshot.json | 603 ++++++++++++++++++++++++++++- drizzle/meta/_journal.json | 4 +- package.json | 3 + scripts/seed.ts | 177 +++++++++ src/lib/server/db/schema.ts | 103 ++++- tsconfig.json | 1 + 10 files changed, 896 insertions(+), 27 deletions(-) delete mode 100644 drizzle/0000_seed_initial_data.sql create mode 100644 scripts/seed.ts diff --git a/.gitignore b/.gitignore index 23be534..66891d6 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ vite.config.js.timestamp-* vite.config.ts.timestamp-* # SQLite *.db +.session +scripts/data/* diff --git a/.session b/.session index 7fb5e96..1064f59 100644 --- a/.session +++ b/.session @@ -16,16 +16,19 @@ badd +9 src/app.html badd +22 src/routes/+page.svelte badd +50 health:// badd +27 flake.nix -badd +13 src/lib/server/db/schema.ts +badd +57 src/lib/server/db/schema.ts badd +1 src/lib/server/db/index.ts badd +104 src/lib/server/db/auth.schema.ts badd +17 src/routes/+page.server.ts badd +1 src/hooks.server.ts +badd +1 scripts/seed.ts +badd +8 tsconfig.json +badd +1 drizzle.config.ts argglobal %argdel -edit src/lib/server/db/schema.ts +edit drizzle.config.ts argglobal -balt src/routes/+page.server.ts +balt src/lib/server/db/schema.ts setlocal foldmethod=manual setlocal foldexpr=0 setlocal foldmarker={{{,}}} @@ -36,12 +39,12 @@ setlocal foldnestmax=20 setlocal foldenable silent! normal! zE let &fdl = &fdl -let s:l = 13 - ((12 * winheight(0) + 27) / 54) +let s:l = 1 - ((0 * winheight(0) + 27) / 54) if s:l < 1 | let s:l = 1 | endif keepjumps exe s:l normal! zt -keepjumps 13 -normal! 033| +keepjumps 1 +normal! 0 tabnext 1 if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0 && getbufvar(s:wipebuf, '&buftype') isnot# 'terminal' silent exe 'bwipe ' . s:wipebuf diff --git a/bun.lock b/bun.lock index df3a80d..f7fc05e 100644 --- a/bun.lock +++ b/bun.lock @@ -6,6 +6,7 @@ "name": "score-system", "dependencies": { "@catppuccin/tailwindcss": "^1.0.0", + "csv-parse": "^6.2.1", "prettier-plugin-svelte": "^3.5.1", }, "devDependencies": { @@ -19,6 +20,7 @@ "@tailwindcss/forms": "^0.5.11", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.2.2", + "@types/bun": "^1.3.13", "@types/node": "^22", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", @@ -348,6 +350,8 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], @@ -418,6 +422,8 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "c12": ["c12@3.3.4", "", { "dependencies": { "chokidar": "^5.0.0", "confbox": "^0.2.4", "defu": "^6.1.6", "dotenv": "^17.3.1", "exsolve": "^1.0.8", "giget": "^3.2.0", "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^2.1.0", "pkg-types": "^2.3.0", "rc9": "^3.0.1" }, "peerDependencies": { "magicast": "*" }, "optionalPeers": ["magicast"] }, "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA=="], @@ -446,6 +452,8 @@ "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + "csv-parse": ["csv-parse@6.2.1", "", {}, "sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], diff --git a/drizzle/0000_seed_initial_data.sql b/drizzle/0000_seed_initial_data.sql deleted file mode 100644 index f87caf8..0000000 --- a/drizzle/0000_seed_initial_data.sql +++ /dev/null @@ -1,7 +0,0 @@ --- Custom SQL migration file, put your code below! -- -INSERT INTO houses (name, color, points) VALUES - ('County', 'red', 1), - ('East', 'pink', 100), - ('Laud', 'teal', 50), - ('School', 'green', 2), - ('West', 'yellow', 92); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 022feb6..6a6f617 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -1,14 +1,605 @@ { - "id": "c7461ef5-d32b-4f75-bde8-ab83440ceb7a", - "prevId": "00000000-0000-0000-0000-000000000000", + "id": "00000000-0000-0000-0000-000000000000", + "prevId": "", "version": "6", "dialect": "sqlite", - "tables": {}, - "views": {}, + "tables": { + "divisions": { + "name": "divisions", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "autoincrement": false, + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "eventAttributions": { + "name": "eventAttributions", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "eventID": { + "autoincrement": false, + "name": "eventID", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "playerID": { + "autoincrement": false, + "name": "playerID", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "placement": { + "default": 0, + "autoincrement": false, + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": { + "eventAttributions_placement_scoringPresets_placement_fk": { + "name": "eventAttributions_placement_scoringPresets_placement_fk", + "tableFrom": "eventAttributions", + "tableTo": "scoringPresets", + "columnsFrom": [ + "placement" + ], + "columnsTo": [ + "placement" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "eventAttributions_playerID_players_id_fk": { + "name": "eventAttributions_playerID_players_id_fk", + "tableFrom": "eventAttributions", + "tableTo": "players", + "columnsFrom": [ + "playerID" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "eventAttributions_eventID_events_id_fk": { + "name": "eventAttributions_eventID_events_id_fk", + "tableFrom": "eventAttributions", + "tableTo": "events", + "columnsFrom": [ + "eventID" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "events": { + "name": "events", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "autoincrement": false, + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "preset": { + "autoincrement": false, + "name": "preset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "division": { + "autoincrement": false, + "name": "division", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "order": { + "autoincrement": false, + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "state": { + "default": 0, + "autoincrement": false, + "name": "state", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "timeCompleted": { + "autoincrement": false, + "name": "timeCompleted", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": { + "events_division_divisions_id_fk": { + "name": "events_division_divisions_id_fk", + "tableFrom": "events", + "tableTo": "divisions", + "columnsFrom": [ + "division" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "events_preset_scoringPresets_preset_fk": { + "name": "events_preset_scoringPresets_preset_fk", + "tableFrom": "events", + "tableTo": "scoringPresets", + "columnsFrom": [ + "preset" + ], + "columnsTo": [ + "preset" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "ledger": { + "name": "ledger", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "timestamp": { + "default": "(unixepoch())", + "autoincrement": false, + "name": "timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "type": { + "default": "'event'", + "autoincrement": false, + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "event": { + "autoincrement": false, + "name": "event", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scorer": { + "autoincrement": false, + "name": "scorer", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": { + "ledger_scorer_scorers_id_fk": { + "name": "ledger_scorer_scorers_id_fk", + "tableFrom": "ledger", + "tableTo": "scorers", + "columnsFrom": [ + "scorer" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ledger_event_events_id_fk": { + "name": "ledger_event_events_id_fk", + "tableFrom": "ledger", + "tableTo": "events", + "columnsFrom": [ + "event" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "ledgerScores": { + "name": "ledgerScores", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "ledgerID": { + "autoincrement": false, + "name": "ledgerID", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "team": { + "autoincrement": false, + "name": "team", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "placement": { + "autoincrement": false, + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "points": { + "default": 0, + "autoincrement": false, + "name": "points", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": { + "ledgerScores_placement_scoringPresets_placement_fk": { + "name": "ledgerScores_placement_scoringPresets_placement_fk", + "tableFrom": "ledgerScores", + "tableTo": "scoringPresets", + "columnsFrom": [ + "placement" + ], + "columnsTo": [ + "placement" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ledgerScores_team_teams_id_fk": { + "name": "ledgerScores_team_teams_id_fk", + "tableFrom": "ledgerScores", + "tableTo": "teams", + "columnsFrom": [ + "team" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "ledgerScores_ledgerID_ledger_id_fk": { + "name": "ledgerScores_ledgerID_ledger_id_fk", + "tableFrom": "ledgerScores", + "tableTo": "ledger", + "columnsFrom": [ + "ledgerID" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "players": { + "name": "players", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "firstName": { + "autoincrement": false, + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastName": { + "autoincrement": false, + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "team": { + "autoincrement": false, + "name": "team", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "division": { + "autoincrement": false, + "name": "division", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": { + "players_division_divisions_id_fk": { + "name": "players_division_divisions_id_fk", + "tableFrom": "players", + "tableTo": "divisions", + "columnsFrom": [ + "division" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "players_team_teams_id_fk": { + "name": "players_team_teams_id_fk", + "tableFrom": "players", + "tableTo": "teams", + "columnsFrom": [ + "team" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scorers": { + "name": "scorers", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "firstName": { + "autoincrement": false, + "name": "firstName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastName": { + "autoincrement": false, + "name": "lastName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "autoincrement": false, + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "autoincrement": false, + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "displayName": { + "autoincrement": false, + "name": "displayName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "default": "'scorer'", + "autoincrement": false, + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "scoringPresets": { + "name": "scoringPresets", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "preset": { + "autoincrement": false, + "name": "preset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "placement": { + "default": 0, + "autoincrement": false, + "name": "placement", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "points": { + "default": 0, + "autoincrement": false, + "name": "points", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "teams": { + "name": "teams", + "columns": { + "id": { + "autoincrement": true, + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "name": { + "autoincrement": false, + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "color": { + "default": "'white'", + "autoincrement": false, + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "compositePrimaryKeys": {}, + "indexes": {}, + "foreignKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": { + "teamScoresView": { + "columns": { + "id": { + "autoincrement": false, + "name": "id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "name": { + "autoincrement": false, + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "autoincrement": false, + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "totalPoints": { + "autoincrement": false, + "name": "totalPoints", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "isExisting": false, + "name": "teamScoresView", + "definition": "select \"teams\".\"id\", \"teams\".\"name\", \"teams\".\"color\", sum(\"ledgerScores\".\"points\") as \"totalPoints\" from \"teams\" left join \"ledgerScores\" on \"teams\".\"id\" = \"ledgerScores\".\"team\" group by \"teams\".\"id\"" + } + }, "enums": {}, "_meta": { - "columns": {}, "schemas": {}, - "tables": {} + "tables": {}, + "columns": {} } } \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 21c942e..d928b28 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1777912799370, - "tag": "0000_seed_initial_data", + "when": 1778078709643, + "tag": "0000_broken_weapon_omega", "breakpoints": true } ] diff --git a/package.json b/package.json index da382c9..224c11f 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:studio": "drizzle-kit studio", + "db:seed": "bun scripts/seed.ts", "auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes", "lint": "prettier --check . && eslint .", "format": "prettier --write ." @@ -29,6 +30,7 @@ "@tailwindcss/forms": "^0.5.11", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.2.2", + "@types/bun": "^1.3.13", "@types/node": "^22", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", @@ -45,6 +47,7 @@ }, "dependencies": { "@catppuccin/tailwindcss": "^1.0.0", + "csv-parse": "^6.2.1", "prettier-plugin-svelte": "^3.5.1" } } diff --git a/scripts/seed.ts b/scripts/seed.ts new file mode 100644 index 0000000..5eaa933 --- /dev/null +++ b/scripts/seed.ts @@ -0,0 +1,177 @@ +import { drizzle } from 'drizzle-orm/libsql'; +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[] { + const content = readFileSync(join(dataDir, filename), 'utf-8'); + return parse(content, { columns: true, skip_empty_lines: true }); +} + +async function seed() { + console.log('Resetting database...'); + + 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.scoringPresets); + await db.delete(schema.divisions); + await db.delete(schema.teams); + + await client.execute('DELETE FROM sqlite_sequence'); + + console.log('Database reset complete.'); + + console.log('Seeding 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})`); + } + + console.log('Seeding 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})`); + } + + console.log('Seeding 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` + ); + } + + 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])); + + 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}`); + } + + 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])); + + const eventsCSV = readCSV('events.csv'); + for (const row of eventsCSV) { + const preset = parseInt(row.preset); + const divisionId = divisionMapForEvents.get(row.division); + if (!divisionId) throw new Error(`Division "${row.division}" not found`); + + const result = await db + .insert(schema.events) + .values({ + name: row.name, + preset, + division: divisionId, + order: parseInt(row.order), + state: parseInt(row.state) + }) + .returning(); + console.log(` Inserted event: ${result[0].name}`); + } + + 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])); + + const eventAttributionsCSV = readCSV('eventAttributions.csv'); + for (const row of eventAttributionsCSV) { + const eventId = eventMap.get(row.eventID); + const playerId = playerMap.get(row.playerID); + + if (!eventId) throw new Error(`Event "${row.eventID}" not found`); + if (!playerId) throw new Error(`Player "${row.playerID}" not found`); + + const result = await db + .insert(schema.eventAttributions) + .values({ + eventID: eventId, + playerID: playerId, + placement: 0 + }) + .returning(); + console.log( + ` Inserted attribution: event=${row.eventID}, player=${row.playerID}, placement=0 (blank)` + ); + } + + console.log('\nSeeding complete!'); + console.log('Ledger and ledgerScores tables are empty (reset).'); + + await client.close(); +} + +seed().catch((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 97e12e0..357c4cc 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -1,14 +1,105 @@ -import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import { sql, eq } from 'drizzle-orm'; +import { integer, sqliteTable, text, sqliteView } from 'drizzle-orm/sqlite-core'; -export const houses = sqliteTable('houses', { +export const teams = sqliteTable('teams', { id: integer('id').primaryKey({ autoIncrement: true }), name: text('name').notNull(), - color: text('color').notNull().default('white'), + 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(), + placement: integer('placement') + .references(() => scoringPresets.placement) + .default(0) + .notNull() +}); + +export const scoringPresets = sqliteTable('scoringPresets', { + id: integer('id').primaryKey({ autoIncrement: true }), + presetID: integer('preset').notNull(), + placement: integer('placement').notNull().default(0), points: integer('points').notNull().default(0) }); -export const consests = sqliteTable('contests', { +export const events = sqliteTable('events', { id: integer('id').primaryKey({ autoIncrement: true }), - event: text('event').notNull() - // house:dd make someting idk + name: text('name').notNull(), + preset: integer('preset') + .references(() => scoringPresets.presetID) + .notNull(), + division: integer('division') + .references(() => divisions.id) + .notNull(), + order: integer('order').notNull(), + state: integer('state').default(0).notNull(), + timeCompleted: integer('timeCompleted') +}); + +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) + .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: text('scorer').references(() => scorers.id) +}); + +export const ledgerScores = sqliteTable('ledgerScores', { + id: integer('id').primaryKey({ autoIncrement: true }), + ledgerID: integer('ledgerID') + .references(() => ledger.id) + .notNull(), + team: integer('team') + .references(() => teams.id) + .notNull(), + placement: integer('placement').references(() => scoringPresets.placement), + points: integer('points').notNull().default(0) +}); + +export const teamScoresView = sqliteView('teamScoresView').as((qb) => { + return qb + .select({ + teamId: teams.id, + teamName: teams.name, + teamColor: teams.color, + totalPoints: sql`sum(${ledgerScores.points})`.mapWith(Number).as('totalPoints') + }) + .from(teams) + .leftJoin(ledgerScores, eq(teams.id, ledgerScores.team)) + .groupBy(teams.id); }); diff --git a/tsconfig.json b/tsconfig.json index 2c2ed3c..ae4bff5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "allowJs": true, "checkJs": true, "esModuleInterop": true, + "types": ["bun"], "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "skipLibCheck": true,