spotters: activity (per-country score) and seen (collection) tables for cars, planes, trains, wildlife
All checks were successful
Deploy production / deploy (push) Successful in 1m7s

This commit is contained in:
2026-10-25 13:09:02 +00:00
parent 85c6f89e36
commit f86c8ef294
6 changed files with 26384 additions and 4 deletions

View File

@@ -0,0 +1,106 @@
CREATE TABLE "cars"."activity" (
"model_id" integer,
"area" text,
"score" real NOT NULL,
CONSTRAINT "activity_pkey" PRIMARY KEY("area","model_id")
);
--> statement-breakpoint
CREATE TABLE "cars"."seen" (
"model_id" integer PRIMARY KEY,
"seen_on" date,
"note" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "planes"."activity" (
"type_id" integer,
"area" text,
"score" real NOT NULL,
CONSTRAINT "activity_pkey" PRIMARY KEY("area","type_id")
);
--> statement-breakpoint
CREATE TABLE "planes"."seen" (
"type_id" integer PRIMARY KEY,
"seen_on" date,
"note" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "wildlife"."activity" (
"species_id" integer,
"area" text,
"score" real NOT NULL,
CONSTRAINT "activity_pkey" PRIMARY KEY("area","species_id")
);
--> statement-breakpoint
CREATE TABLE "wildlife"."seen" (
"species_id" integer PRIMARY KEY,
"seen_on" date,
"note" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "trains"."activity" (
"type_id" integer,
"area" text,
"score" real NOT NULL,
CONSTRAINT "activity_pkey" PRIMARY KEY("area","type_id")
);
--> statement-breakpoint
CREATE TABLE "trains"."seen" (
"type_id" integer PRIMARY KEY,
"seen_on" date,
"note" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE INDEX "activity_area_score_idx" ON "cars"."activity" ("area","score");--> statement-breakpoint
CREATE INDEX "activity_area_score_idx" ON "planes"."activity" ("area","score");--> statement-breakpoint
CREATE INDEX "activity_area_score_idx" ON "wildlife"."activity" ("area","score");--> statement-breakpoint
CREATE INDEX "activity_area_score_idx" ON "trains"."activity" ("area","score");--> statement-breakpoint
ALTER TABLE "cars"."activity" ADD CONSTRAINT "activity_model_id_models_id_fkey" FOREIGN KEY ("model_id") REFERENCES "cars"."models"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "cars"."seen" ADD CONSTRAINT "seen_model_id_models_id_fkey" FOREIGN KEY ("model_id") REFERENCES "cars"."models"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "planes"."activity" ADD CONSTRAINT "activity_type_id_types_id_fkey" FOREIGN KEY ("type_id") REFERENCES "planes"."types"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "planes"."seen" ADD CONSTRAINT "seen_type_id_types_id_fkey" FOREIGN KEY ("type_id") REFERENCES "planes"."types"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "wildlife"."activity" ADD CONSTRAINT "activity_species_id_species_id_fkey" FOREIGN KEY ("species_id") REFERENCES "wildlife"."species"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "wildlife"."seen" ADD CONSTRAINT "seen_species_id_species_id_fkey" FOREIGN KEY ("species_id") REFERENCES "wildlife"."species"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "trains"."activity" ADD CONSTRAINT "activity_type_id_types_id_fkey" FOREIGN KEY ("type_id") REFERENCES "trains"."types"("id") ON DELETE CASCADE;--> statement-breakpoint
ALTER TABLE "trains"."seen" ADD CONSTRAINT "seen_type_id_types_id_fkey" FOREIGN KEY ("type_id") REFERENCES "trains"."types"("id") ON DELETE CASCADE;--> statement-breakpoint
-- Hand-written: updated_at triggers, grants, GUI registration. activity is filled by
-- scripts/spotters-activity.ts; seen is written by the owner from the pokedex pages.
DO $$
DECLARE s text;
BEGIN
FOREACH s IN ARRAY ARRAY['cars','planes','trains','wildlife'] LOOP
EXECUTE format('CREATE TRIGGER touch_updated_at BEFORE UPDATE ON %I.seen FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at()', s);
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN
EXECUTE format('GRANT SELECT, INSERT, UPDATE, DELETE ON %I.activity, %I.seen TO personal_app', s, s);
END IF;
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_ro') THEN
EXECUTE format('GRANT SELECT ON %I.activity, %I.seen TO personal_ro', s, s);
END IF;
END LOOP;
END $$;
--> statement-breakpoint
DO $$
BEGIN
IF to_regclass('meta.tables') IS NOT NULL THEN
INSERT INTO meta.tables (schema_name, table_name, slug, display_name, kind, editable, has_done, sort_order)
VALUES ('cars','activity','cars-activity','Car activity by country','core',false,false,1000),
('cars','seen','cars-seen','Cars seen','core',false,false,1000),
('planes','activity','planes-activity','Plane activity by country','core',false,false,1000),
('planes','seen','planes-seen','Planes seen','core',false,false,1000),
('trains','activity','trains-activity','Train activity by country','core',false,false,1000),
('trains','seen','trains-seen','Trains seen','core',false,false,1000),
('wildlife','activity','wildlife-activity','Wildlife activity by country','core',false,false,1000),
('wildlife','seen','wildlife-seen','Wildlife seen','core',false,false,1000)
ON CONFLICT DO NOTHING;
IF has_function_privilege('meta.sync(integer)', 'EXECUTE') THEN
PERFORM meta.sync();
END IF;
END IF;
END $$;