146 lines
8 KiB
MySQL
146 lines
8 KiB
MySQL
|
|
-- =====================================================================
|
||
|
|
-- TeeCup — migrasjon 065
|
||
|
|
-- GolfAPI.io som tredje banekilde (ADR-064) -- baner utenfor TeeOffs
|
||
|
|
-- dekning (f.eks. Tjøme Golfklubb, bekreftet IKKE i TeeOff 2026-08-12).
|
||
|
|
-- =====================================================================
|
||
|
|
-- GolfAPI-kall koster ekte, begrenset budsjett (kjøpt 20 kall, ~2/bane)
|
||
|
|
-- -- ULIKT TeeOffs frie, offentlige API. Derfor: ETT delt, globalt
|
||
|
|
-- cache-lag (golfapi_course + underliggende hull/tee/koordinat-tabeller)
|
||
|
|
-- som ALDRI hentes på nytt for samme fysiske bane (GolfAPIs avtale
|
||
|
|
-- tillater eksplisitt permanent caching). Både den org-scopede
|
||
|
|
-- turneringsmodellen (course/hole/tee/tee_rating) OG den globale
|
||
|
|
-- personal_course-katalogen (migrasjon 020) kopierer INN fra denne
|
||
|
|
-- cachen ved eksplisitt import -- selve cachen eies ikke av noen av de
|
||
|
|
-- to, den er en tredje, uavhengig katalog.
|
||
|
|
--
|
||
|
|
-- Koordinatene (grønn front/midt/bak, hindringer, tee-punkter) kopieres
|
||
|
|
-- ALDRI inn per import -- de er ikke en del av HCP-beregningen (ingen
|
||
|
|
-- reproduserbarhets-hensyn som par/rating har via ADR-007/ADR-019), så
|
||
|
|
-- rangefinder-oppslag leser alltid direkte fra golfapi_course_coordinate
|
||
|
|
-- via banens lagrede GolfAPI-courseID. Én fysisk bane = ett
|
||
|
|
-- koordinatsett, uansett hvor mange ganger den importeres.
|
||
|
|
-- =====================================================================
|
||
|
|
|
||
|
|
\set ON_ERROR_STOP on
|
||
|
|
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
-- Delt GolfAPI-cache (global, ingen organization_id/owner -- én rad per
|
||
|
|
-- fysisk bane som NOEN i TeeCup noensinne har importert)
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
|
||
|
|
-- GolfAPIs courseID brukes direkte som PK (globalt unik hos dem, ingen
|
||
|
|
-- grunn til en egen surrogate-nøkkel).
|
||
|
|
CREATE TABLE golfapi_course (
|
||
|
|
course_id text PRIMARY KEY,
|
||
|
|
club_id text NOT NULL,
|
||
|
|
club_name text NOT NULL,
|
||
|
|
course_name text NOT NULL,
|
||
|
|
city text,
|
||
|
|
country text,
|
||
|
|
num_holes smallint NOT NULL CHECK (num_holes BETWEEN 1 AND 18),
|
||
|
|
-- Alltid hentet med measureUnit=m (golfapi_client.py) -- ingen yard-data
|
||
|
|
-- lagres, unngår enhets-fellen research allerede advarte om.
|
||
|
|
has_gps boolean NOT NULL DEFAULT false,
|
||
|
|
num_coordinates integer NOT NULL DEFAULT 0,
|
||
|
|
golfapi_updated_at timestamptz, -- deres timestampUpdated, til evt. fremtidig re-sync-vurdering
|
||
|
|
fetched_at timestamptz NOT NULL DEFAULT now()
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Par/SI per hull. Kun menns verdier er kanonisk brukt av kopier-inn-
|
||
|
|
-- stegene under (TeeCups eksisterende hole/personal_course_hole har
|
||
|
|
-- ingen kjønnsdelt par -- samme forenkling som TeeOff-importen, ADR-019).
|
||
|
|
-- Kvinners verdier caches likevel, i tilfelle en fremtidig utvidelse.
|
||
|
|
CREATE TABLE golfapi_course_hole (
|
||
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
golfapi_course_id text NOT NULL REFERENCES golfapi_course(course_id) ON DELETE CASCADE,
|
||
|
|
hole_number smallint NOT NULL CHECK (hole_number BETWEEN 1 AND 18),
|
||
|
|
par_men smallint NOT NULL CHECK (par_men BETWEEN 3 AND 6),
|
||
|
|
index_men smallint NOT NULL CHECK (index_men BETWEEN 1 AND 18),
|
||
|
|
par_women smallint CHECK (par_women BETWEEN 3 AND 6),
|
||
|
|
index_women smallint CHECK (index_women BETWEEN 1 AND 18),
|
||
|
|
UNIQUE (golfapi_course_id, hole_number),
|
||
|
|
UNIQUE (golfapi_course_id, index_men)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE TABLE golfapi_course_tee (
|
||
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
golfapi_course_id text NOT NULL REFERENCES golfapi_course(course_id) ON DELETE CASCADE,
|
||
|
|
golfapi_tee_id text NOT NULL,
|
||
|
|
name text NOT NULL,
|
||
|
|
color_hex text,
|
||
|
|
-- Meter per hull, indeks 0 = hull 1 ... indeks 17 = hull 18.
|
||
|
|
lengths_meters smallint[] NOT NULL,
|
||
|
|
course_rating_men numeric(4,1) NOT NULL,
|
||
|
|
slope_men smallint NOT NULL CHECK (slope_men BETWEEN 55 AND 155),
|
||
|
|
course_rating_women numeric(4,1),
|
||
|
|
slope_women smallint CHECK (slope_women BETWEEN 55 AND 155),
|
||
|
|
course_rating_men_front9 numeric(4,1),
|
||
|
|
course_rating_men_back9 numeric(4,1),
|
||
|
|
course_rating_women_front9 numeric(4,1),
|
||
|
|
course_rating_women_back9 numeric(4,1),
|
||
|
|
UNIQUE (golfapi_course_id, golfapi_tee_id),
|
||
|
|
CHECK (array_length(lengths_meters, 1) BETWEEN 1 AND 18)
|
||
|
|
);
|
||
|
|
|
||
|
|
-- Rangefinder-datalaget: grønn front/midt/bak, hindringer, tee-punkter.
|
||
|
|
-- poi_type/green_location/side_fairway holdes som tekst+CHECK (ikke
|
||
|
|
-- egne enum-typer) -- samme "kan utvides uten ALTER TYPE"-begrunnelse
|
||
|
|
-- som 001 allerede bruker for `format`.
|
||
|
|
CREATE TABLE golfapi_course_coordinate (
|
||
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
|
|
golfapi_course_id text NOT NULL REFERENCES golfapi_course(course_id) ON DELETE CASCADE,
|
||
|
|
hole_number smallint NOT NULL CHECK (hole_number BETWEEN 1 AND 18),
|
||
|
|
poi_type text NOT NULL CHECK (poi_type IN (
|
||
|
|
'green', 'green_bunker', 'fairway_bunker', 'water',
|
||
|
|
'trees', 'marker_100', 'marker_150', 'marker_200',
|
||
|
|
'dogleg', 'road', 'tee_front', 'tee_back'
|
||
|
|
)),
|
||
|
|
-- GolfAPIs `location`-felt er generisk (front/midt/bak for POI-et),
|
||
|
|
-- ikke kun for 'green' -- f.eks. hadde Tjømes bunkere også front/bak.
|
||
|
|
-- Ikke navngitt green_location -- ville feilaktig antydet at kun
|
||
|
|
-- grønn-punkter har denne verdien.
|
||
|
|
location text CHECK (location IN ('front', 'middle', 'back')),
|
||
|
|
side_fairway text CHECK (side_fairway IN ('left', 'center', 'right')),
|
||
|
|
latitude double precision NOT NULL CHECK (latitude BETWEEN -90 AND 90),
|
||
|
|
longitude double precision NOT NULL CHECK (longitude BETWEEN -180 AND 180)
|
||
|
|
);
|
||
|
|
|
||
|
|
CREATE INDEX ON golfapi_course_hole (golfapi_course_id);
|
||
|
|
CREATE INDEX ON golfapi_course_tee (golfapi_course_id);
|
||
|
|
CREATE INDEX ON golfapi_course_coordinate (golfapi_course_id, hole_number);
|
||
|
|
|
||
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON golfapi_course TO teecup_app;
|
||
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON golfapi_course_hole TO teecup_app;
|
||
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON golfapi_course_tee TO teecup_app;
|
||
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON golfapi_course_coordinate TO teecup_app;
|
||
|
|
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
-- Kopier-inn-mål 1: org-scopet turneringsbane (course), ny kilde
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
-- Samme mønster som ADR-019s 'official' (teeoff) -- men 'international'
|
||
|
|
-- betyr GolfAPI, IKKE live-oppslag (Beslutning i ADR-064: GolfAPI-kall
|
||
|
|
-- koster budsjett, teeoffs gjør ikke -- derfor kopier-inn her også).
|
||
|
|
ALTER TYPE course_source ADD VALUE 'international';
|
||
|
|
|
||
|
|
-- CHECK-constraint kan ikke endres direkte -- droppes og gjenopprettes
|
||
|
|
-- med samme betingelse utvidet symmetrisk. Navnet (course_check) er
|
||
|
|
-- Postgres sitt auto-genererte navn for det opprinnelige, unavngitte
|
||
|
|
-- table-level CHECK-et i 001 -- bekreftet mot ekte teecup_db, ikke gjettet.
|
||
|
|
ALTER TABLE course DROP CONSTRAINT course_check;
|
||
|
|
ALTER TABLE course ADD CONSTRAINT course_check
|
||
|
|
CHECK (source NOT IN ('official', 'international') OR external_course_ref IS NOT NULL);
|
||
|
|
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
-- Kopier-inn-mål 2: global personal_course-katalog, ny provenance-kolonne
|
||
|
|
-- ---------------------------------------------------------------------
|
||
|
|
-- round.course_source trenger INGEN endring -- en GolfAPI-importert bane
|
||
|
|
-- blir en helt vanlig personal_course-rad (course_source='custom' i
|
||
|
|
-- round, uendret), kun med denne kolonnen satt for å (a) hindre
|
||
|
|
-- dupliserte importer, (b) la rangefinder-oppslag finne koordinatene.
|
||
|
|
ALTER TABLE personal_course
|
||
|
|
ADD COLUMN external_golfapi_course_id text REFERENCES golfapi_course(course_id);
|
||
|
|
|
||
|
|
CREATE UNIQUE INDEX personal_course_golfapi_ref_unique
|
||
|
|
ON personal_course (external_golfapi_course_id)
|
||
|
|
WHERE external_golfapi_course_id IS NOT NULL;
|