From 953b2744bddc04051d163fd99a26bc727a53f5c8 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sat, 19 Sep 2026 18:20:05 +0000 Subject: [PATCH 1/5] add header border --- src/routes/+layout.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e83bae3..b704e30 100755 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -64,7 +64,7 @@
-
+
Stan Runge
From 97cbf90c3d8505404f00bcab828688f89cddc159 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sat, 19 Sep 2026 18:23:19 +0000 Subject: [PATCH 2/5] remove drizzle and vscode files --- .gitignore | 2 + .../migration.sql | 198 -- .../snapshot.json | 2251 ----------------- 3 files changed, 2 insertions(+), 2449 deletions(-) delete mode 100644 drizzle/20260830223753_illegal_lord_tyger/migration.sql delete mode 100644 drizzle/20260830223753_illegal_lord_tyger/snapshot.json diff --git a/.gitignore b/.gitignore index 79518f7..4262dd2 100755 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ Thumbs.db # Vite vite.config.js.timestamp-* vite.config.ts.timestamp-* + +.vscode/ diff --git a/drizzle/20260830223753_illegal_lord_tyger/migration.sql b/drizzle/20260830223753_illegal_lord_tyger/migration.sql deleted file mode 100644 index 769e8d6..0000000 --- a/drizzle/20260830223753_illegal_lord_tyger/migration.sql +++ /dev/null @@ -1,198 +0,0 @@ -CREATE TYPE "availability" AS ENUM('catchable', 'trade_only', 'transfer_only', 'event_only');--> statement-breakpoint -CREATE TABLE "animals" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), - "species" text NOT NULL, - "breed" text, - "animal_name" text, - "description" text, - "ai_breed_suggestion" text, - "ai_breed_confidence" real, - "submitted_at" timestamp with time zone DEFAULT now() NOT NULL, - "accepted_at" timestamp with time zone, - "denied_at" timestamp with time zone -); ---> statement-breakpoint -CREATE TABLE "blog_articles" ( - "id" serial PRIMARY KEY, - "title" varchar(256), - "slug" varchar(256), - "content" varchar(8192), - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "certificates" ( - "id" serial PRIMARY KEY, - "name" varchar(256), - "progress" integer DEFAULT 0, - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "game" ( - "id" serial PRIMARY KEY, - "name" varchar(256) NOT NULL, - "generation" integer DEFAULT 1 NOT NULL -); ---> statement-breakpoint -CREATE TABLE "photos" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), - "sighting_id" uuid NOT NULL, - "r2_key" text NOT NULL, - "url" text NOT NULL, - "sort_order" integer DEFAULT 0 NOT NULL, - "uploaded_at" timestamp with time zone DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pokemon" ( - "id" serial PRIMARY KEY, - "number" integer NOT NULL, - "generation" integer DEFAULT 1 NOT NULL, - "name" varchar(256) NOT NULL, - "type1" varchar(256) NOT NULL, - "type2" varchar(256), - "hp" integer DEFAULT 0 NOT NULL, - "attack" integer DEFAULT 0 NOT NULL, - "defense" integer DEFAULT 0 NOT NULL, - "special_attack" integer DEFAULT 0 NOT NULL, - "special_defense" integer DEFAULT 0 NOT NULL, - "speed" integer DEFAULT 0 NOT NULL -); ---> statement-breakpoint -CREATE TABLE "pokemon_game_entry" ( - "id" serial PRIMARY KEY, - "pokemon_id" integer NOT NULL, - "game_id" integer NOT NULL, - "availability" "availability" DEFAULT 'catchable'::"availability" NOT NULL, - "note" varchar(256), - CONSTRAINT "pokemon_game_entry_pokemon_id_game_id_unique" UNIQUE("pokemon_id","game_id") -); ---> statement-breakpoint -CREATE TABLE "publications" ( - "id" serial PRIMARY KEY, - "name" varchar(256), - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "sightings" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), - "animal_id" uuid NOT NULL, - "reporter_name" text, - "seen_at" timestamp with time zone NOT NULL, - "lat" double precision NOT NULL, - "lng" double precision NOT NULL, - "submitted_at" timestamp with time zone DEFAULT now() NOT NULL, - "accepted_at" timestamp with time zone, - "denied_at" timestamp with time zone -); ---> statement-breakpoint -CREATE TABLE "tasks" ( - "id" serial PRIMARY KEY, - "name" varchar(256), - "current_points" integer DEFAULT 0, - "total_points" integer DEFAULT 1, - "notes" varchar(8192), - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "tasks_to_topics" ( - "task_id" integer NOT NULL, - "topic_id" integer NOT NULL -); ---> statement-breakpoint -CREATE TABLE "topics" ( - "id" serial PRIMARY KEY, - "name" varchar(256), - "emoji" varchar(256), - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "universities" ( - "id" serial PRIMARY KEY, - "name" varchar(256), - "progress" integer DEFAULT 0, - "created_at" timestamp DEFAULT now(), - "updated_at" timestamp DEFAULT now() -); ---> statement-breakpoint -CREATE TABLE "user_entry" ( - "id" serial PRIMARY KEY, - "pokemon_game_entry_id" integer NOT NULL, - "user_id" text NOT NULL, - CONSTRAINT "user_entry_pokemon_game_entry_id_user_id_unique" UNIQUE("pokemon_game_entry_id","user_id") -); ---> statement-breakpoint -CREATE TABLE "user_game" ( - "id" serial PRIMARY KEY, - "user_id" text NOT NULL, - "game_id" integer NOT NULL, - CONSTRAINT "user_game_user_id_game_id_unique" UNIQUE("user_id","game_id") -); ---> statement-breakpoint -CREATE TABLE "account" ( - "id" text PRIMARY KEY, - "account_id" text NOT NULL, - "provider_id" text NOT NULL, - "user_id" text NOT NULL, - "access_token" text, - "refresh_token" text, - "id_token" text, - "access_token_expires_at" timestamp, - "refresh_token_expires_at" timestamp, - "scope" text, - "password" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE "session" ( - "id" text PRIMARY KEY, - "expires_at" timestamp NOT NULL, - "token" text NOT NULL UNIQUE, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp NOT NULL, - "ip_address" text, - "user_agent" text, - "user_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "user" ( - "id" text PRIMARY KEY, - "name" text NOT NULL, - "email" text NOT NULL UNIQUE, - "email_verified" boolean DEFAULT false NOT NULL, - "image" text, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE TABLE "verification" ( - "id" text PRIMARY KEY, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expires_at" timestamp NOT NULL, - "created_at" timestamp DEFAULT now() NOT NULL, - "updated_at" timestamp DEFAULT now() NOT NULL -); ---> statement-breakpoint -CREATE INDEX "pokemon_game_entry_game_id_index" ON "pokemon_game_entry" ("game_id");--> statement-breakpoint -CREATE INDEX "user_entry_user_id_index" ON "user_entry" ("user_id");--> statement-breakpoint -CREATE INDEX "user_game_user_id_index" ON "user_game" ("user_id");--> statement-breakpoint -CREATE INDEX "account_userId_idx" ON "account" ("user_id");--> statement-breakpoint -CREATE INDEX "session_userId_idx" ON "session" ("user_id");--> statement-breakpoint -CREATE INDEX "verification_identifier_idx" ON "verification" ("identifier");--> statement-breakpoint -ALTER TABLE "photos" ADD CONSTRAINT "photos_sighting_id_sightings_id_fkey" FOREIGN KEY ("sighting_id") REFERENCES "sightings"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "pokemon_game_entry" ADD CONSTRAINT "pokemon_game_entry_pokemon_id_pokemon_id_fkey" FOREIGN KEY ("pokemon_id") REFERENCES "pokemon"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "pokemon_game_entry" ADD CONSTRAINT "pokemon_game_entry_game_id_game_id_fkey" FOREIGN KEY ("game_id") REFERENCES "game"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "sightings" ADD CONSTRAINT "sightings_animal_id_animals_id_fkey" FOREIGN KEY ("animal_id") REFERENCES "animals"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "tasks_to_topics" ADD CONSTRAINT "tasks_to_topics_task_id_tasks_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id");--> statement-breakpoint -ALTER TABLE "tasks_to_topics" ADD CONSTRAINT "tasks_to_topics_topic_id_topics_id_fkey" FOREIGN KEY ("topic_id") REFERENCES "topics"("id");--> statement-breakpoint -ALTER TABLE "user_entry" ADD CONSTRAINT "user_entry_pokemon_game_entry_id_pokemon_game_entry_id_fkey" FOREIGN KEY ("pokemon_game_entry_id") REFERENCES "pokemon_game_entry"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "user_entry" ADD CONSTRAINT "user_entry_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "user_game" ADD CONSTRAINT "user_game_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "user_game" ADD CONSTRAINT "user_game_game_id_game_id_fkey" FOREIGN KEY ("game_id") REFERENCES "game"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE;--> statement-breakpoint -ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE; \ No newline at end of file diff --git a/drizzle/20260830223753_illegal_lord_tyger/snapshot.json b/drizzle/20260830223753_illegal_lord_tyger/snapshot.json deleted file mode 100644 index 9b0e792..0000000 --- a/drizzle/20260830223753_illegal_lord_tyger/snapshot.json +++ /dev/null @@ -1,2251 +0,0 @@ -{ - "version": "8", - "dialect": "postgres", - "id": "288e35c9-ef00-4e98-8a34-55caa83c4842", - "prevIds": [ - "00000000-0000-0000-0000-000000000000" - ], - "ddl": [ - { - "values": [ - "catchable", - "trade_only", - "transfer_only", - "event_only" - ], - "name": "availability", - "entityType": "enums", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "animals", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "blog_articles", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "certificates", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "game", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "photos", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "pokemon", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "pokemon_game_entry", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "publications", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "sightings", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "tasks", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "tasks_to_topics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "topics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "universities", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user_entry", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user_game", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "gen_random_uuid()", - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "species", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "breed", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "animal_name", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "description", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ai_breed_suggestion", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "real", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ai_breed_confidence", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "submitted_at", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "accepted_at", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "denied_at", - "entityType": "columns", - "schema": "public", - "table": "animals" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "title", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "varchar(8192)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "content", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "blog_articles" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "certificates" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "certificates" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "progress", - "entityType": "columns", - "schema": "public", - "table": "certificates" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "certificates" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "certificates" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "game" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "game" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "1", - "generated": null, - "identity": null, - "name": "generation", - "entityType": "columns", - "schema": "public", - "table": "game" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "gen_random_uuid()", - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "sighting_id", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "r2_key", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "url", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "sort_order", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "uploaded_at", - "entityType": "columns", - "schema": "public", - "table": "photos" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "number", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "1", - "generated": null, - "identity": null, - "name": "generation", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "type1", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "type2", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "hp", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "attack", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "defense", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "special_attack", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "special_defense", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "speed", - "entityType": "columns", - "schema": "public", - "table": "pokemon" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "pokemon_id", - "entityType": "columns", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "game_id", - "entityType": "columns", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "type": "availability", - "typeSchema": "public", - "notNull": true, - "dimensions": 0, - "default": "'catchable'", - "generated": null, - "identity": null, - "name": "availability", - "entityType": "columns", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "note", - "entityType": "columns", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "publications" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "publications" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "publications" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "publications" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "gen_random_uuid()", - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "animal_id", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "reporter_name", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "seen_at", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "double precision", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "lat", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "double precision", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "lng", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "submitted_at", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "accepted_at", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "denied_at", - "entityType": "columns", - "schema": "public", - "table": "sightings" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "current_points", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "1", - "generated": null, - "identity": null, - "name": "total_points", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "varchar(8192)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "notes", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "tasks" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "task_id", - "entityType": "columns", - "schema": "public", - "table": "tasks_to_topics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "topic_id", - "entityType": "columns", - "schema": "public", - "table": "tasks_to_topics" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "topics" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "topics" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "emoji", - "entityType": "columns", - "schema": "public", - "table": "topics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "topics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "topics" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "universities" - }, - { - "type": "varchar(256)", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "universities" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "progress", - "entityType": "columns", - "schema": "public", - "table": "universities" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "universities" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "universities" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user_entry" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "pokemon_game_entry_id", - "entityType": "columns", - "schema": "public", - "table": "user_entry" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "user_entry" - }, - { - "type": "serial", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user_game" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "user_game" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "game_id", - "entityType": "columns", - "schema": "public", - "table": "user_game" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "game_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "pokemon_game_entry_game_id_index", - "entityType": "indexes", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_entry_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "user_entry" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_game_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "user_game" - }, - { - "nameExplicit": true, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_userId_idx", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": true, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_userId_idx", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": true, - "columns": [ - { - "value": "identifier", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "verification_identifier_idx", - "entityType": "indexes", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - "sighting_id" - ], - "schemaTo": "public", - "tableTo": "sightings", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "photos_sighting_id_sightings_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "photos" - }, - { - "nameExplicit": false, - "columns": [ - "pokemon_id" - ], - "schemaTo": "public", - "tableTo": "pokemon", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "pokemon_game_entry_pokemon_id_pokemon_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "nameExplicit": false, - "columns": [ - "game_id" - ], - "schemaTo": "public", - "tableTo": "game", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "pokemon_game_entry_game_id_game_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "nameExplicit": false, - "columns": [ - "animal_id" - ], - "schemaTo": "public", - "tableTo": "animals", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "sightings_animal_id_animals_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "sightings" - }, - { - "nameExplicit": false, - "columns": [ - "task_id" - ], - "schemaTo": "public", - "tableTo": "tasks", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "NO ACTION", - "name": "tasks_to_topics_task_id_tasks_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "tasks_to_topics" - }, - { - "nameExplicit": false, - "columns": [ - "topic_id" - ], - "schemaTo": "public", - "tableTo": "topics", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "NO ACTION", - "name": "tasks_to_topics_topic_id_topics_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "tasks_to_topics" - }, - { - "nameExplicit": false, - "columns": [ - "pokemon_game_entry_id" - ], - "schemaTo": "public", - "tableTo": "pokemon_game_entry", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "user_entry_pokemon_game_entry_id_pokemon_game_entry_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "user_entry" - }, - { - "nameExplicit": false, - "columns": [ - "user_id" - ], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "user_entry_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "user_entry" - }, - { - "nameExplicit": false, - "columns": [ - "user_id" - ], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "user_game_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "user_game" - }, - { - "nameExplicit": false, - "columns": [ - "game_id" - ], - "schemaTo": "public", - "tableTo": "game", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "user_game_game_id_game_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "user_game" - }, - { - "nameExplicit": false, - "columns": [ - "user_id" - ], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - "user_id" - ], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": [ - "id" - ], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "animals_pkey", - "schema": "public", - "table": "animals", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "blog_articles_pkey", - "schema": "public", - "table": "blog_articles", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "certificates_pkey", - "schema": "public", - "table": "certificates", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "game_pkey", - "schema": "public", - "table": "game", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "photos_pkey", - "schema": "public", - "table": "photos", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "pokemon_pkey", - "schema": "public", - "table": "pokemon", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "pokemon_game_entry_pkey", - "schema": "public", - "table": "pokemon_game_entry", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "publications_pkey", - "schema": "public", - "table": "publications", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "sightings_pkey", - "schema": "public", - "table": "sightings", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "tasks_pkey", - "schema": "public", - "table": "tasks", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "topics_pkey", - "schema": "public", - "table": "topics", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "universities_pkey", - "schema": "public", - "table": "universities", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "user_entry_pkey", - "schema": "public", - "table": "user_entry", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "user_game_pkey", - "schema": "public", - "table": "user_game", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": [ - "id" - ], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": [ - "pokemon_id", - "game_id" - ], - "nullsNotDistinct": false, - "name": "pokemon_game_entry_pokemon_id_game_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "pokemon_game_entry" - }, - { - "nameExplicit": false, - "columns": [ - "pokemon_game_entry_id", - "user_id" - ], - "nullsNotDistinct": false, - "name": "user_entry_pokemon_game_entry_id_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "user_entry" - }, - { - "nameExplicit": false, - "columns": [ - "user_id", - "game_id" - ], - "nullsNotDistinct": false, - "name": "user_game_user_id_game_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "user_game" - }, - { - "nameExplicit": false, - "columns": [ - "token" - ], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": [ - "email" - ], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - } - ], - "renames": [] -} \ No newline at end of file From 6ec596bc246d72f159310d606b5b91fc35a71cc9 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sun, 20 Sep 2026 02:23:12 +0000 Subject: [PATCH 3/5] add derby page --- src/lib/derby/data.ts | 57 +++ src/lib/derby/model.test.ts | 37 ++ src/lib/derby/model.ts | 184 +++++++ src/lib/derby/practice.ts | 44 ++ src/routes/projects/derby/+page.svelte | 662 +++++++++++++++++++++++++ static/derby-favicon.svg | 5 + 6 files changed, 989 insertions(+) create mode 100644 src/lib/derby/data.ts create mode 100644 src/lib/derby/model.test.ts create mode 100644 src/lib/derby/model.ts create mode 100644 src/lib/derby/practice.ts create mode 100644 src/routes/projects/derby/+page.svelte create mode 100644 static/derby-favicon.svg diff --git a/src/lib/derby/data.ts b/src/lib/derby/data.ts new file mode 100644 index 0000000..9a4fc68 --- /dev/null +++ b/src/lib/derby/data.ts @@ -0,0 +1,57 @@ +// Derby R 2026 · Duindigt · zondag 20 september · 2600 m autostart +// Bronnen: NDR deelnemerslijst, ZEturf koerskaart (records over 2600 m), +// Geny uitslagen Derby Selectie R (heat 1) en Merrie Derby R (heat 2), 4 september 2026. + +export type Result = number | 'D'; + +export type Horse = { + n: number; + name: string; + driver: string; + trainer: string; + sex: 'M' | 'H' | 'R'; + /** laatste vijf uitslagen, nieuwste eerst; D = gediskwalificeerd */ + form: Result[]; + /** beste kilometertijd over 2600 m in seconden, null = onbekend */ + record: number | null; + /** Derbyproef 4 september (2000 m autostart) */ + trial: { heat: 1 | 2; pos: number; time: number; odds: number }; + note?: string; +}; + +export const race = { + name: 'Derby R 2026', + date: 'zo 20 sep · 17:40', + track: 'Duindigt', + distance: 2600, + start: 'Autostart', + purse: 74000, + surface: 'Sintelbaan · goed', + sources: [ + { + label: 'NDR deelnemerslijst', + href: 'https://ndr.nl/wp-content/uploads/2026/09/Ingeschreven-paarden-in-de-Derby-R-Duindigt-zondag-20-september.pdf' + }, + { + label: 'ZEturf koerskaart', + href: 'https://www.zeturf.be/nl/course-du-jour/2026-09-20/R14C6-duindigt-derby-r' + }, + { label: 'Proef heat 1', href: 'https://www.geny.com/course/1681375/arrivee-rapports' }, + { label: 'Proef heat 2', href: 'https://www.geny.com/course/1681376/arrivee-rapports' } + ] +}; + +const t = (m: number, s: number) => m * 60 + s; + +export const horses: Horse[] = [ + { n: 1, name: 'Rihane', driver: 'Micha Brouwer', trainer: 'Danny Brouwer', sex: 'M', form: [2, 3, 9, 7, 4], record: t(1, 13.7), trial: { heat: 2, pos: 2, time: t(1, 17.7), odds: 3.0 } }, + { n: 2, name: 'Rose Starlake', driver: 'Jeffrey Mieras', trainer: 'Dion Tesselaar', sex: 'M', form: [1, 2, 2, 1, 'D'], record: t(1, 13.4), trial: { heat: 2, pos: 1, time: t(1, 17.3), odds: 3.5 } }, + { n: 3, name: 'Rising Sun Boko', driver: 'Danny Brouwer', trainer: 'Danny Brouwer', sex: 'H', form: [2, 3, 1, 4, 1], record: t(1, 14.2), trial: { heat: 1, pos: 2, time: t(1, 13.8), odds: 23.1 } }, + { n: 4, name: 'Roy Schermer', driver: 'Robin Bakker', trainer: 'Paul Hagoort', sex: 'R', form: [3, 2, 'D', 2, 1], record: t(1, 11.8), trial: { heat: 1, pos: 3, time: t(1, 14.5), odds: 7.8 } }, + { n: 5, name: 'Rolleman Flevo', driver: 'Dion Tesselaar', trainer: 'Dion Tesselaar', sex: 'H', form: [1, 1, 1, 1, 1], record: null, trial: { heat: 1, pos: 1, time: t(1, 13.8), odds: 1.3 }, note: '13 uit 13' }, + { n: 6, name: 'Royal Express', driver: 'Jim Veldman', trainer: 'Paul Hagoort', sex: 'M', form: [3, 1, 6, 1, 3], record: t(1, 12.4), trial: { heat: 2, pos: 3, time: t(1, 17.8), odds: 1.8 } }, + { n: 7, name: 'Ram Jam', driver: 'Michel Rothengatter', trainer: 'Michel Rothengatter', sex: 'R', form: [4, 1, 6, 1, 1], record: t(1, 11.5), trial: { heat: 1, pos: 4, time: t(1, 14.6), odds: 8.8 } }, + { n: 8, name: 'Rex Beuckenswijk', driver: 'Jaap van Rijn', trainer: 'Jeroen Engwerda', sex: 'R', form: [6, 4, 2, 1, 1], record: null, trial: { heat: 1, pos: 6, time: t(1, 15.2), odds: 34.9 } }, + { n: 9, name: 'Ragna L Kite', driver: 'Rob de Vlieger', trainer: 'Rob de Vlieger', sex: 'M', form: [4, 2, 3, 1, 3], record: t(1, 14.8), trial: { heat: 2, pos: 4, time: t(1, 17.8), odds: 6.8 } }, + { n: 10, name: 'Ritchy Diamond', driver: 'Patrick de Haan', trainer: 'Michel Rothengatter', sex: 'R', form: [5, 3, 1, 'D', 2], record: null, trial: { heat: 1, pos: 5, time: t(1, 14.7), odds: 20.9 } } +]; diff --git a/src/lib/derby/model.test.ts b/src/lib/derby/model.test.ts new file mode 100644 index 0000000..279d936 --- /dev/null +++ b/src/lib/derby/model.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from 'vitest'; +import { horses } from './data'; +import { betValue, defaultWeights, placings, predict } from './model'; + +describe('derby model', () => { + it('produces a probability distribution', () => { + const preds = predict(horses, defaultWeights); + const sum = preds.reduce((a, r) => a + r.p, 0); + expect(sum).toBeCloseTo(1, 9); + expect(preds[0].horse.n).toBe(5); + }); + + it('renormalises after scratching', () => { + const preds = predict(horses.filter((h) => h.n !== 5), defaultWeights); + expect(preds.reduce((a, r) => a + r.p, 0)).toBeCloseTo(1, 9); + expect(preds.find((r) => r.horse.n === 5)).toBeUndefined(); + }); + + it('place probabilities are consistent', () => { + const preds = predict(horses, defaultWeights); + const { top, exacta, koppel, trio } = placings(preds, defaultWeights.lambda); + for (const key of ['win', 'place2', 'place3'] as const) { + expect(top.reduce((a, t) => a + t[key], 0)).toBeCloseTo(1, 9); + } + expect(exacta.reduce((a, e) => a + e.p, 0)).toBeCloseTo(1, 9); + expect(koppel.reduce((a, e) => a + e.p, 0)).toBeCloseTo(1, 9); + expect(trio.reduce((a, e) => a + e.p, 0)).toBeCloseTo(1, 9); + expect(koppel.length).toBe(45); + expect(trio.length).toBe(720); + }); + + it('kelly is zero without edge', () => { + expect(betValue(0.2, 4).kelly).toBe(0); + expect(betValue(0.5, 3).edge).toBeCloseTo(0.5); + expect(betValue(0.5, 3).kelly).toBeCloseTo(0.25); + }); +}); diff --git a/src/lib/derby/model.ts b/src/lib/derby/model.ts new file mode 100644 index 0000000..b3ba615 --- /dev/null +++ b/src/lib/derby/model.ts @@ -0,0 +1,184 @@ +import type { Horse, Result } from './data'; + +export type Weights = { + form: number; + trial: number; + record: number; + draw: number; + market: number; + risk: number; + /** halveringstijd van de vormweging, in koersen */ + halfLife: number; + /** seconden die de (langzamere) merrieproef wordt bestraft t.o.v. heat 1 */ + heatGap: number; + /** softmax-temperatuur: >1 vlakker, <1 scherper */ + temperature: number; + /** Stern-demping voor plaatskansen: lagere posities gebruiken p^lambda */ + lambda: number; +}; + +export const defaultWeights: Weights = { + form: 1, + trial: 1, + record: 0.6, + draw: 0.4, + market: 0.6, + risk: 1, + halfLife: 2, + heatGap: 0.8, + temperature: 1, + lambda: 0.8 +}; + +export const presets: Record> = { + Standaard: {}, + Proef: { form: 0.5, trial: 1.6, record: 0.3, draw: 0.4, market: 0 }, + Vorm: { form: 1.6, trial: 0.5, record: 0.5, draw: 0.3, market: 0 }, + Markt: { form: 0.3, trial: 0.3, record: 0.3, draw: 0.2, market: 1.6 } +}; + +export const featureKeys = ['form', 'trial', 'record', 'draw', 'market'] as const; +export type FeatureKey = (typeof featureKeys)[number]; + +/** logit-schaal: z-scores × gewichten worden hiermee vermenigvuldigd */ +const SCALE = 0.55; + +const POSITION_POINTS: Record = { 1: 1, 2: 0.75, 3: 0.6, 4: 0.45, 5: 0.35, 6: 0.25 }; +const positionPoints = (r: Result) => (r === 'D' ? 0 : (POSITION_POINTS[r] ?? 0.1)); + +/** binnenste startnummers zijn goud, 9 en 10 staan in de tweede rij */ +const DRAW_SCORE = [0, 0.6, 0.7, 0.6, 0.45, 0.4, 0.25, 0.1, 0, -0.2, -0.3]; + +export type Features = { + form: number; + trial: number; + record: number; + draw: number; + market: number; + pFail: number; + recordEstimated: boolean; +}; + +export function features(h: Horse, all: Horse[], w: Weights): Features { + const decay = h.form.map((_, i) => Math.pow(0.5, i / w.halfLife)); + const form = + h.form.reduce((sum: number, r, i) => sum + decay[i] * positionPoints(r), 0) / + decay.reduce((a, b) => a + b, 0); + + const heatWinner = Math.min(...all.filter((o) => o.trial.heat === h.trial.heat).map((o) => o.trial.time)); + const trial = -(h.trial.time - heatWinner) - (h.trial.heat === 2 ? w.heatGap : 0); + + const known = all.filter((o) => o.record !== null).map((o) => o.record as number); + const recordEstimated = h.record === null; + const record = -(h.record ?? known.reduce((a, b) => a + b, 0) / known.length); + + const disq = h.form.filter((r) => r === 'D').length; + const poor = h.form.filter((r) => r !== 'D' && r >= 7).length; + const pFail = Math.min(0.5, 0.03 + 0.1 * disq + 0.03 * poor); + + return { form, trial, record, draw: DRAW_SCORE[h.n], market: -Math.log(h.trial.odds), pFail, recordEstimated }; +} + +function zscores(values: number[]): number[] { + const mean = values.reduce((a, b) => a + b, 0) / values.length; + const sd = Math.sqrt(values.reduce((s, v) => s + (v - mean) ** 2, 0) / values.length); + return values.map((v) => (sd === 0 ? 0 : (v - mean) / sd)); +} + +export type Prediction = { + horse: Horse; + features: Features; + /** bijdrage per feature aan de logit (gewicht × z × schaal) */ + contributions: Record; + riskPenalty: number; + logit: number; + p: number; +}; + +export function predict(runners: Horse[], w: Weights): Prediction[] { + if (runners.length === 0) return []; + const feats = runners.map((h) => features(h, runners, w)); + const z = Object.fromEntries(featureKeys.map((k) => [k, zscores(feats.map((f) => f[k]))])) as Record; + + const rows = runners.map((horse, i) => { + const contributions = Object.fromEntries( + featureKeys.map((k) => [k, w[k] * z[k][i] * SCALE]) + ) as Record; + const riskPenalty = w.risk * Math.log(1 - feats[i].pFail); + const logit = Object.values(contributions).reduce((a, b) => a + b, 0) + riskPenalty; + return { horse, features: feats[i], contributions, riskPenalty, logit, p: 0 }; + }); + + const max = Math.max(...rows.map((r) => r.logit)); + const exp = rows.map((r) => Math.exp((r.logit - max) / w.temperature)); + const total = exp.reduce((a, b) => a + b, 0); + rows.forEach((r, i) => (r.p = exp[i] / total)); + return rows.sort((a, b) => b.p - a.p); +} + +export type Placings = { + /** kans op 1e, 2e, 3e per paard (index = positie in `preds`) */ + top: { win: number; place2: number; place3: number }[]; + exacta: { a: number; b: number; p: number }[]; + koppel: { a: number; b: number; p: number }[]; + trio: { a: number; b: number; c: number; p: number }[]; +}; + +/** + * Exacte Plackett–Luce-uitwerking over alle geordende drietallen, met + * Stern-demping: voor de 2e en 3e plaats wordt p^lambda gebruikt, wat de + * neiging van pure PL corrigeert om de favoriet te vaak tweede te laten worden. + */ +export function placings(preds: Prediction[], lambda: number): Placings { + const n = preds.length; + const p1 = preds.map((r) => r.p); + const p2 = p1.map((p) => Math.pow(p, lambda)); + const s2 = p2.reduce((a, b) => a + b, 0); + + const top = preds.map(() => ({ win: 0, place2: 0, place3: 0 })); + const exacta: Placings['exacta'] = []; + const trio: Placings['trio'] = []; + + for (let a = 0; a < n; a++) { + top[a].win = p1[a]; + for (let b = 0; b < n; b++) { + if (b === a) continue; + const pab = p1[a] * (p2[b] / (s2 - p2[a])); + exacta.push({ a, b, p: pab }); + top[b].place2 += pab; + for (let c = 0; c < n; c++) { + if (c === a || c === b) continue; + const pabc = pab * (p2[c] / (s2 - p2[a] - p2[b])); + trio.push({ a, b, c, p: pabc }); + top[c].place3 += pabc; + } + } + } + + const koppelMap = new Map(); + for (const e of exacta) { + const [a, b] = e.a < e.b ? [e.a, e.b] : [e.b, e.a]; + const key = `${a}-${b}`; + const cur = koppelMap.get(key) ?? { a, b, p: 0 }; + cur.p += e.p; + koppelMap.set(key, cur); + } + + const desc = (xs: T[]) => xs.sort((x, y) => y.p - x.p); + return { top, exacta: desc(exacta), koppel: desc([...koppelMap.values()]), trio: desc(trio) }; +} + +/** verwachte waarde per euro en Kelly-fractie voor een winweddenschap */ +export function betValue(p: number, odds: number) { + const edge = p * odds - 1; + const kelly = odds > 1 ? Math.max(0, edge / (odds - 1)) : 0; + return { edge, kelly }; +} + +export const fairOdds = (p: number) => (p > 0 ? 1 / p : Infinity); + +export function formatTime(seconds: number) { + const m = Math.floor(seconds / 60); + const s = seconds - m * 60; + return `${m}'${s.toFixed(1).padStart(4, '0').replace('.', '"')}`; +} diff --git a/src/lib/derby/practice.ts b/src/lib/derby/practice.ts new file mode 100644 index 0000000..8ec9bdb --- /dev/null +++ b/src/lib/derby/practice.ts @@ -0,0 +1,44 @@ +// Oefenrondes: verzonnen toto-noteringen om de pagina te leren lezen vóór de echte koers. + +export type Scenario = { + key: string; + title: string; + story: string; + lesson: string; + scratched: number[]; + /** win-noteringen per startnummer; ontbreekt = wordt uit de modelkans afgeleid */ + odds?: Record; + /** plaatsnoteringen per startnummer */ + place?: Record; +}; + +export const scenarios: Scenario[] = [ + { + key: 'overladen', + title: 'De favoriet wordt overladen', + story: 'Iedereen op de baan zet op Rolleman Flevo. Zijn notering zakt naar 1,20 en de rest wordt duur.', + lesson: + 'Rolleman winnend is rood: bij 1,20 moet hij meer dan 83% van de tijd winnen om quitte te spelen, en zo zeker is zelfs een ongeslagen paard niet. De waarde zit bij plaatsweddenschappen op paarden die de toto vergeet.', + scratched: [], + odds: { 5: 1.2, 3: 16, 2: 11, 4: 28, 7: 22, 6: 14, 1: 35, 9: 70, 10: 90, 8: 120 }, + place: { 3: 3.2, 2: 2.6, 7: 4.5, 4: 5 } + }, + { + key: 'eens', + title: 'De toto is het eens met het model', + story: 'De noteringen liggen precies in lijn met de kansen van het model, met de gebruikelijke marge van 20% erop.', + lesson: + 'Alles is rood. Als de kansen kloppen, wint de marge van de toto altijd. Niet wedden is dan de juiste zet — en dat is een prima uitkomst van een middag kijken.', + scratched: [] + }, + { + key: 'nietstarter', + title: 'Rolleman Flevo start niet', + story: 'Vlak voor de start wordt nummer 5 teruggetrokken. De toto moet zich in een paar minuten herstellen.', + lesson: + 'Zonder de favoriet worden de kansen herverdeeld en worden Rising Sun Boko en Rose Starlake de favorieten. Vink een niet-starter altijd meteen uit, anders klopt geen enkel getal meer.', + scratched: [5], + odds: { 3: 3.5, 2: 3.2, 4: 7, 7: 6, 6: 5, 1: 12, 9: 25, 10: 30, 8: 40 }, + place: { 3: 1.6, 2: 1.5, 7: 2.2 } + } +]; diff --git a/src/routes/projects/derby/+page.svelte b/src/routes/projects/derby/+page.svelte new file mode 100644 index 0000000..0598eb6 --- /dev/null +++ b/src/routes/projects/derby/+page.svelte @@ -0,0 +1,662 @@ + + + + Derby Lens — Duindigt 2026 + + + +
+
+ + + {#if scenario} +
+ Oefenmodus · verzonnen noteringen — {scenario.title} + +
+ {/if} + +
+
+
Kansmodel · 6 signalen
+

DERBY
R 2026

+
+
{race.distance.toLocaleString('nl')} m{race.start}
+
€{(race.purse / 1000).toFixed(0)}KPrijzengeld
+
{runners.length}Starters
+
GoedSintelbaan
+
+
+ {#if favourite} +
+
Model favoriet · nr {favourite.horse.n}
+
{favourite.horse.name}
+
+ {pct(favourite.p)} + + winkans · fair notering {oddsFmt(fairOdds(favourite.p))}
+ bij de eerste drie: {pct(pl.top[0].win + pl.top[0].place2 + pl.top[0].place3, 0)} + {#if favourite.horse.note}
{favourite.horse.note} gewonnen{/if} +
+
+
+ {/if} +
+ +
+
+
Zo gebruik je dit op de dag zelf
+

Vier stappen, twintig minuten voor 17:40

+

De toto houdt ongeveer 20% van elke euro in. Structureel winnen lukt bijna niemand, ook dit model niet. Wat deze pagina wél doet: de weddenschappen die zéker verliezen eruit halen, en de rest klein en netjes doseren. Tijdens de koers zelf hoef je niets te doen — die duurt ruim drie minuten.

+
+
    + {#each steps as st, i (st.title)} +
  1. + {st.done ? '✓' : i + 1} +
    + {st.title} +

    {st.detail}

    + {#if i === 0} + + {:else} + {st.status} + {/if} +
    +
  2. + {/each} +
+
+
Oefenen
+

Laad verzonnen noteringen en kijk wat er gebeurt. Wis daarna alles voordat je de echte invult.

+
+ {#each scenarios as sc (sc.key)} + + {/each} + +
+ {#if scenario} +
+

Situatie. {scenario.story}

+

Wat je moet zien. {scenario.lesson}

+ Naar de lijst ↓ +
+ {/if} +
+
+ +
+ + +
+
+
{runners.length} starters{#if scenario} · oefening{/if}

Winkans

+
+ {#if help}

Kans dat elk paard wint volgens het model, hoogste bovenaan. Fair is de notering waarbij een weddenschap precies quitte speelt. Vul bij Notering de echte toto-notering in: staat er dan een groene Waarde, dan schat het model dat paard hoger in dan de toto. Vink een paard uit als het niet start.
Onder de naam: rijder en de laatste vijf uitslagen, nieuwste eerst. D = gediskwalificeerd.

{/if} +
+
+ #Paard + KansFairNoteringWaarde +
+ {#each preds as r, i (r.horse.n)} + {@const o = odds[r.horse.n]} + {@const v = o && o > 1 ? betValue(r.p, o) : null} +
+ toggleScratch(r.horse)} /> +
{r.horse.n}
+
+
{r.horse.name}
+
{r.horse.driver} · {r.horse.form.join('-')}
+
+
+
{pct(r.p)}
+
{oddsFmt(fairOdds(r.p))}
+ +
0} class:neg={v && v.edge <= 0}>{v ? signed(v.edge * 100, 0) + '%' : ''}
+
+ {/each} + {#each horses.filter((h) => scratched.includes(h.n)) as h (h.n)} +
+ toggleScratch(h)} /> +
{h.n}
+
{h.name}
niet-starter
+
+ {/each} +
+
+
+ +
+
+
Wedvormen naast winnen · {blending ? 'model + toto gemengd' : 'volgens het model'}
+

Plaats & combinaties

+ {#if help}

Bij een plaatsweddenschap win je als het paard bij de eerste drie eindigt — kleinere uitbetaling, grotere kans. Vul bij Toto de plaatsnotering in om te zien of er waarde zit. Koppel: je kiest de eerste twee, volgorde maakt niet uit. Trio: de eerste drie in de juiste volgorde. Fair = notering waarbij je quitte speelt.

{/if} + + + + {#each preds as r, i (r.horse.n)} + {@const t = plFinal.top[i]} + {@const place = placeP(i)} + {@const po = placeOdds[r.horse.n]} + {@const v = po && po > 1 ? betValue(place, po) : null} + + + + + + + + + + + + {/each} + +
#Paard1e2e3ePlaatsFairTotoWaarde
{r.horse.n}{r.horse.name}{pct(t.win, 0)}{pct(t.place2, 0)}{pct(t.place3, 0)}{pct(place, 0)}{oddsFmt(fairOdds(place))} 0} class:neg={v && v.edge <= 0}>{v ? signed(v.edge * 100, 0) + '%' : ''}
+
+
+
Koppel · 1–2 elke volgorde
+ {#each plFinal.koppel.slice(0, 6) as k (`${k.a}-${k.b}`)} +
{name(k.a)} + {name(k.b)}{pct(k.p)}{oddsFmt(fairOdds(k.p))}
+ {/each} +
+
+
Trio · 1–2–3 juiste volgorde
+ {#each plFinal.trio.slice(0, 6) as t (`${t.a}-${t.b}-${t.c}`)} +
{preds[t.a].horse.n} · {preds[t.b].horse.n} · {preds[t.c].horse.n}{pct(t.p)}{oddsFmt(fairOdds(t.p))}
+ {/each} +
+
+
+ +
+
Model vs. toto
+

Waarde & inzet

+ {#if help}

De toto is de mening van alle wedders samen en zit er meestal minder naast dan één model. Daarom wordt de eindkans gemengd: hoe hoger het vertrouwen in de toto, hoe sterker het model het oneens moet zijn voordat een paard waarde toont. Waarde is wat je per ingezette euro verwacht te winnen: +19% betekent €1,19 terug per €1. Inzet volgt uit Kelly: de wiskundig ideale inzet als deel van je bankroll, met een kwart als veilige fractie.

{/if} +
+ + +
+
+ + +
+ {#if bets.length === 0} +

Vul noteringen in bij de paarden (winnend hierboven, plaats hiernaast). Waarde = kans × notering − 1.

+ {:else} +
+
{toto.entered}/{preds.length}noteringen
+
{toto.complete ? signed((toto.overround - 1) * 100, 0) + '%' : '—'}toto-marge
+
{goodBets.length}met waarde
+
+ + + + {#each bets as b (`${b.type}-${b.r.horse.n}`)} + + + + + + + + + + {/each} + +
#PaardTypeTotoEindkansWaardeInzet
{b.r.horse.n}{b.r.horse.name}{b.type}{oddsFmt(b.odds)}{pct(b.p)} 0} class:neg={b.edge <= 0}>{signed(b.edge * 100, 0)}%{b.kelly > 0 ? euro(b.stake) : '—'}
+ {#if goodBets.length === 0} +

Geen enkele weddenschap heeft positieve waarde bij deze noteringen. Dat is een prima uitkomst: niet wedden is dan de beste zet.

+ {/if} + {/if} +
+
+ +
+
Per signaal · groen = pluspunt · oranje = minpunt
+

Waarom deze kansen

+ {#if help}

Elk paard wordt per signaal vergeleken met de rest van het veld: beter dan gemiddeld is groen, slechter is oranje. Het grote getal is hoeveel het signaal bijdraagt aan de score (na weging), het kleine getal eronder is de ruwe waarde. De scores bij elkaar geven de kans.

{/if} +
+ + + + + {#each featureKeys as k (k)}{/each} + + + + + {#each preds as r (r.horse.n)} + {@const f = r.features} + {@const h = r.horse} + + + + + + + + + + + + + {/each} + +
#Paard{featureLabel[k]}UitvalScoreKans
{h.n}{h.name}{sexLabel[h.sex]} · tr. {h.trainer}{signed(r.contributions.form)}{f.form.toFixed(2)} van 1{signed(r.contributions.trial)}{trialLabel(h)}{signed(r.contributions.record)}{h.record ? formatTime(h.record) : 'onbekend'}{signed(r.contributions.draw)}nr {h.n}{h.n > 8 ? ' · 2e rij' : ''}{signed(r.contributions.market)}notering {h.trial.odds.toFixed(1)}{signed(r.riskPenalty)}{pct(f.pFail, 0)} kans op D{signed(r.logit)}{pct(r.p)}
+
+
+ +
+ Begrippen voor wie nooit naar de draf kijkt +
+
    +
  • Draverij — de paarden trekken een karretje (sulky) met de rijder erop en moeten in draf blijven. Gaat een paard in galop, dan wordt het gediskwalificeerd: de D in de uitslagen.
  • +
  • Autostart — de paarden starten achter een rijdende auto met uitklapbare hekken, op volgorde van startnummer: 1 aan de binnenkant, 1 t/m 8 op de eerste rij, 9 en 10 op de tweede rij.
  • +
  • Kilometertijd — gemiddelde tijd per 1000 m, de standaardmaat voor snelheid. 1'13"8 is 1 minuut 13,8 seconden per km. Lager is sneller; één seconde is ruim tien meter.
  • +
  • Derbyproef — de kwalificatiekoers van 4 september over 2000 m, in twee heats. Heat 1 (open) werd gewonnen door Rolleman Flevo; heat 2 (alleen merries) door Rose Starlake en was 3,5 s/km trager.
  • +
  • Derby — de belangrijkste koers van het jaar voor driejarigen. Alle tien starters zijn drie jaar en lopen 2600 m; hun namen beginnen met een R omdat het geboortejaar 2023 de letter R kreeg.
  • +
  • Notering — de uitbetaling per ingezette euro als het paard wint: bij 2,50 krijg je €2,50 terug. Hoe lager, hoe meer de toto in het paard gelooft.
  • +
  • Merrie / hengst / ruin — vrouwelijk, mannelijk, gecastreerd. Merries hadden in de proef een eigen heat.
  • +
  • Rijder / trainer — wie op de sulky zit tijdens de koers, en wie het paard dagelijks traint. Tesselaar doet bij Rolleman Flevo allebei.
  • +
+
+
+ +
+ Hoe het model rekent +
+

logit = 0.55 · Σ gewicht × z(signaal) + risicostraf · ln(1 − P(uitval))  ·  kans = softmax(logit / T)

+
    +
  • Elk signaal wordt een z-score over het veld (hoeveel standaardafwijkingen boven of onder het gemiddelde), zodat seconden, punten en noteringen vergelijkbaar zijn.
  • +
  • Uitslagen — 1e = 1, 2e = 0,75, 3e = 0,6, 4e = 0,45, 5e = 0,35, 6e = 0,25, verder 0,1, D = 0; gewogen met halveringstijd.
  • +
  • Proef — minus de achterstand in seconden op de heatwinnaar, minus de merriehandicap voor heat 2.
  • +
  • Snelste tijd — minus de record-kilometertijd over 2600 m; onbekend = veldgemiddelde.
  • +
  • Startpositie — vaste curve: 2 en 3 het best, dan 1, aflopend naar 8; 9 en 10 negatief.
  • +
  • Wedmarkt — −ln(notering bij de proef).
  • +
  • Uitval — 3% + 10% per D + 3% per plek ≥ 7 in de laatste vijf; telt mee als ln(1 − kans).
  • +
  • Plaats & combinaties — Plackett–Luce, exact uitgerekend over alle 720 mogelijke top-3's; voor 2e en 3e plaats met pλ (Stern-correctie).
  • +
  • Eindkans — log-lineaire menging: ln p = (1 − v) · ln pmodel + v · ln ptoto, daarna genormaliseerd; ptoto = 1/notering gedeeld door de som (zo valt de toto-marge weg). Waarde en inzet gebruiken de eindkans.
  • +
  • De startpositie-curve en de merriehandicap zijn aannames, geen gemeten waarden. Daarom zijn ze instelbaar.
  • +
+ +
+
+ +
DERBY LENS · {race.name}SvelteKit
+
+
+ + diff --git a/static/derby-favicon.svg b/static/derby-favicon.svg new file mode 100644 index 0000000..35161e7 --- /dev/null +++ b/static/derby-favicon.svg @@ -0,0 +1,5 @@ + + + + + From 8ea36dd515c303212d4ba696674c473e101ecba3 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sun, 20 Sep 2026 02:23:30 +0000 Subject: [PATCH 4/5] add projects page --- src/routes/projects/+page.svelte | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/routes/projects/+page.svelte diff --git a/src/routes/projects/+page.svelte b/src/routes/projects/+page.svelte new file mode 100644 index 0000000..0fca7d0 --- /dev/null +++ b/src/routes/projects/+page.svelte @@ -0,0 +1,46 @@ + + +
+

Projects ({projects.length})

+ + +
From 7f04896ff83bdbca46e1c989f49290a958dd0b08 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sun, 20 Sep 2026 02:24:13 +0000 Subject: [PATCH 5/5] update blog index page ui --- src/routes/blog/+page.svelte | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte index 0284bd0..bea4401 100644 --- a/src/routes/blog/+page.svelte +++ b/src/routes/blog/+page.svelte @@ -46,18 +46,22 @@ {/if}
-{#each data.blogArticles as article (article.slug)} -
- {article.title} - {#if data.isAdmin && article.status === 'draft'} - draft - edit - {/if} -

{article.createdAt?.toDateString()}

-
-{/each} +
+ {#each data.blogArticles as article (article.slug)} + + + + {/each} +
{#if createNewArticlePopup}