ADR-081/CHANGELOG: rangefinder for offisielle baner rullet ut

Migrasjon 079 kjørt og teecup_api/teecup_frontend rullet ut 2026-08-17
etter bekreftelse fra bruker. Tjømes offisielle bane fylt med de 180
feltbefarte koordinatene fra forrige runde.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Erol Haagenrud 2026-08-17 12:13:57 +02:00
parent 93af7c5bef
commit 366582d3ef
2 changed files with 183 additions and 0 deletions

View file

@ -7979,6 +7979,136 @@ etterpå.
---
## ADR-081: Rangefinder-koordinater for offisielle (TeeOff-koblede) baner (migrasjon 079) (2026-08-17)
Foranledning: forrige runde erstattet koordinatene på Erol sin
PERSONLIGE Tjøme-bane (`personal_course`, GolfAPI-cache). Brukeren
påpekte at dette ikke er banen org-turneringer faktisk spilles på --
"Tjøme Gents" sin offisielle, TeeOff-koblede bane (`course.source=
'official'`, `external_course_ref='tjome-golfklubb:140'`) er en helt
separat rad, og har ALDRI hatt koordinatdata i det hele tatt. Brukeren
ba om samme koordinater der også, OG at dette skal være en generell,
gjenbrukbar mulighet for ALLE offisielle baner fremover -- ikke en
engangsfiks til.
Rangefinder-funksjonalitet (avstand til green/hindringer, GPS-basert)
fantes til nå KUN for GolfAPI-importerte personlige baner. Verken
org-turneringer (individuell slagspill ELLER lag-matchplay) hadde noen
rangefinder i det hele tatt -- og selv frittstående runder spilt på en
EKTE teeoff-bane (`round.course_source='teeoff'`) fikk tom liste, samme
underliggende hull (ingen koordinatkilde for teeoff-baner). Løsningen
dekker derfor tre steder for prisen av én delt backend-brikke.
**Bekreftet med bruker (AskUserQuestion):** rangefinderen skulle wires
inn i BEGGE org-turnering-scoringsflytene i samme runde (individuell
slagspill OG lag-matchplay), ikke én med den andre som senere sak --
til tross for at ingen aktiv runde fantes på den offisielle Tjøme-banen
ennå (fremtidsrettet grunnlag, ikke en akutt "vis det nå"-fiks).
**Gjenbrukt, ikke oppfunnet på nytt:** `app/hole_history.py` hadde
ALLEREDE nøyaktig den bane-bro-abstraksjonen dette trengte fra ADR-072:
`CourseKey = tuple[Literal["teeoff"], str, str] | tuple[Literal[
"golfapi"], str]`, med `resolve_personal_round_course_key()` og
`resolve_tournament_round_course_key()`. Tredje søster `resolve_match_
course_key()` (match->session->course, lag-matchplay) lagt til ved
siden av de to andre, identisk mønster.
**Ny delt backend-modul `app/target_points.py`:** `get_target_points
(conn, course_key, hole_number)` grener på `course_key[0]` -- `"teeoff"`
slår opp den nye `teeoff_course_coordinate`, `"golfapi"` slår opp
eksisterende `golfapi_course_coordinate` (uendret spørring). Ingen RLS
på noen av kildetabellene (globale, delt på tvers av organisasjoner/
brukere som tilfeldigvis bruker samme fysiske bane), så samme funksjon
virker uansett om `conn` kommer fra `plain_connection()` eller
`org_connection()`. Tre tynne kallesteder: `rounds.py` sitt eksisterende
endepunkt REFAKTORERT til dette mønsteret (villet sideeffekt:
frittstående runder på en ekte teeoff-bane får nå rangefinder for
første gang), nytt endepunkt i `individual_tournaments.py`, nytt
endepunkt i `scoring.py` (tilgangssjekk speiler `get_scorecard`: org-
medlem ELLER deltaker i turneringen).
**Migrasjon 079, del 1 -- delte domener.** To runder på rad (067:
rock/layup, 078: landmark) måtte hver for seg utvide DEN SAMME
poi_type-CHECK-listen i `golfapi_course_coordinate`. Å innføre en ny
tabell med en IDENTISK, dupliserende CHECK-liste ville gjort dette
vedlikeholdsproblemet permanent. Løst med `CREATE DOMAIN course_poi_
type`/`course_poi_location`/`course_poi_side` -- én kilde til sannhet,
brukt av BEGGE tabeller. Eksisterende `golfapi_course_coordinate`-kolonner
konvertert til domenene (additivt, ingen datatap).
**Migrasjon 079, del 2 -- `teeoff_course_coordinate`.** Samme
kolonneform som `golfapi_course_coordinate`, men nøkkel er `course.
external_course_ref` (tekst, IKKE en FK -- speiler at `external_course_
ref` selv er en myk referanse, ingen global "teeoff_course"-tabell
finnes å FK-e mot). GLOBAL (ikke org-scopet, ingen RLS) -- samme
begrunnelse som `golfapi_course_coordinate`: flere organisasjoner kan
importere samme fysiske bane fra teeoff hver sin `course`-rad,
koordinatene skal deles, ikke registreres på nytt per organisasjon.
**Ny skrive-vei (`courses.py`): `PUT`/`GET .../courses/{id}/coordinates`.**
400 hvis banen ikke er `source='official'`. Full erstatning i én
transaksjon (samme "erstatt hele settet"-semantikk brukeren ba om for
Tjøme). Autorisasjon: `get_authorized_org` -- samme bar som ALLE andre
course-muterende endepunkter i denne filen (ingen strengere rolle-sjekk,
matcher eksisterende presedens). Selve tolkningsarbeidet (fritekst-navn
på feltbefarte punkter -> `poi_type`/`location`/`side_fairway`) er
BEVISST IKKE automatisert -- krever dømmekraft (bekreftet konkret av
denne rundens egen Tjøme-erfaring: "Voll"/"Bjella" kunne ikke vært
gjettet av et script). Capabiliten som åpnes er lagrings-/gjenbrukslaget;
selve kartleggingen gjøres fortsatt av bruker+Claude sammen per bane.
**Frontend:** `HoleTargetDistance` (allerede en fritt gjenbrukbar,
selvstendig komponent -- egen fetch+GPS, selvskjulende ved tom data)
generalisert fra `roundId`-prop til `baseUrl`-prop, slik at alle tre
kallesteder (frittstående runde `/rounds/{id}`, individuell org-
turnering `/orgs/{org}/tournaments/{t}/rounds/{r}`, lag-matchplay
`/orgs/{org}/matches/{m}`) kan gjenbruke samme komponent uendret utover
selve URL-en. Limt inn persistent (uansett steg) i `HoleStatsSheet`
(individuell slagspill) og som en ny betinget blokk i `session-
scorecard.tsx` sin hull-`<section>` (lag-matchplay, samme
plasseringsmønster som `NassauPanel`).
**Verifisert:**
1. `tests/test_target_points.py`, 11 nye tester -- `get_target_points()`
begge grener, `resolve_match_course_key()` alle tre utfall, alle tre
GET-endepunktene + skrive-veien (inkl. 400 på ikke-offisiell bane,
full erstatning, tilgangskontroll). Full backend-suite: 106/106
bestått. `tsc --noEmit` rent + 45/45 vitest.
2. Scratch-database (alle migrasjoner 001-079 anvendt) + scratch
`teecup_api` + lokal `next dev`: én org med offisiell bane, koordinater
satt via det nye PUT-endepunktet over ekte HTTP (verifisert tilbake
via GET), én individuell turnering-runde og én match-play-økt på
samme bane. Rangefinder bekreftet korrekt koblet i BEGGE UI-ene
(nettverksspor viste riktig `target-points`-kall med riktige 6 punkter
for hull 4, tom liste for hull 5 -- komponenten viste følgelig "venter
på GPS"-tilstanden på hull 4 og INGEN rad i det hele tatt på hull 5,
nøyaktig den designede selvskjulende oppførselen). Lys+mørk bekreftet
begge steder. Regresjonssjekk: en frittstående GolfAPI-personlig-bane-
runde satt opp i samme scratch-miljø, bekreftet identisk oppførsel
som før refaktoreringen (data på hull med koordinater, ingen rad på
hull uten). Scratch-stacken fullstendig revet ned (Docker-container,
database+rolle, MinIO-bucket, `next dev`) -- ekte `teecup_db` urørt
gjennom hele verifiseringen.
3. Migrasjon 079 vist og bekreftet av bruker før kjøring mot ekte
`teecup_db` (additiv -- tre domener + én ny tabell, eksisterende
`golfapi_course_coordinate`-data verifisert uendret etterpå, 399
rader).
**Rullet ut 2026-08-17** -- bruker bekreftet ("Git commit og rull ut").
Migrasjon 079 kjørt mot ekte `teecup_db`, deretter `docker compose
build teecup_api teecup_frontend && up -d`. Rene containerlogger,
`https://teecup.golf/logg-inn` bekreftet 200 OK. Deretter fylt Tjømes
offisielle bane ("Tjøme Golfklubb Hovedbanen", org "Tjøme Gents") med
de samme 180 feltbefarte koordinatene som ble bekreftet mot den
personlige banen i forrige runde (samme mapping-beslutninger gjenbrukt
direkte, ikke re-avklart) -- kjørt via et engangsskript i `teecup_api`-
containeren mot det NYE endepunktets underliggende tabell (samme
DELETE+INSERT-logikk som `PUT`-endepunktet selv bruker), bekreftet
180/180 rader med korrekt `poi_type`-fordeling direkte mot databasen
etterpå.
---
## Utviklingsplan (rekkefølge)
1. ✅ Land tenant-modell → **Organisasjon** (ADR-001/002/003)

View file

@ -11870,3 +11870,56 @@ Neste steg:
direkte mot databasen etterpå: riktig `poi_type`-fordeling, `landmark`-
punktene og de tre hull-4-bunkerne til stede med korrekte
koordinater. Ingen kodeendring -- ingen container-restart nødvendig.
98. **Rangefinder-koordinater for offisielle (TeeOff-koblede) baner
(ADR-081, migrasjon 079) — 2026-08-17.** Bruker påpekte at forrige
rundes Tjøme-koordinater havnet på Erol sin PERSONLIGE GolfAPI-bane,
ikke den offisielle TeeOff-koblede banen "Tjøme Gents" faktisk
bruker til org-turneringer -- og ba om at koordinatene også legges
inn der, OG at dette blir en generell mulighet for alle offisielle
baner fremover.
Rangefinder fantes til nå kun for GolfAPI-importerte personlige
baner -- verken org-turneringer (individuell slagspill/lag-matchplay)
eller frittstående runder på en EKTE teeoff-bane hadde noen
koordinatkilde. Bekreftet med bruker (AskUserQuestion): begge
org-turnering-scoringsflytene wires inn i samme runde, ikke faset.
Migrasjon 079: delte domener (`course_poi_type`/`location`/`side`)
for å stoppe et allerede observert vedlikeholdsproblem (067 og 078
måtte begge utvide samme dupliserte CHECK-liste), ny global tabell
`teeoff_course_coordinate` (nøkkel `course.external_course_ref`, samme
"delt cache, ikke per-org"-begrunnelse som `golfapi_course_
coordinate`). Ny delt modul `app/target_points.py` +
`resolve_match_course_key()` (tredje søster til de to eksisterende
bane-bro-funksjonene i `hole_history.py`, ADR-072) -- alle tre
kallesteder (frittstående runder, individuell org-turnering,
lag-matchplay) bruker nå samme `CourseKey`-abstraksjon. `rounds.py`
sitt eksisterende endepunkt refaktorert til samme mønster (villet
sideeffekt: frittstående runder på en ekte teeoff-bane får nå også
rangefinder). Ny skrive-vei `PUT`/`GET .../courses/{id}/coordinates`
i `courses.py` -- generell, gjenbrukbar, ikke en engangsfiks.
`HoleTargetDistance` generalisert (`roundId` -> `baseUrl`-prop) og
wiret inn i `HoleStatsSheet` (individuell slagspill) og `session-
scorecard.tsx` (lag-matchplay, samme mønster som `NassauPanel`).
**Verifisert:** `tests/test_target_points.py`, 11 nye tester (106/106
backend totalt). `tsc --noEmit` rent + 45/45 vitest. Scratch-database
+ scratch `teecup_api` + lokal `next dev`: koordinater satt via det
nye PUT-endepunktet over ekte HTTP, rangefinder bekreftet korrekt i
BEGGE org-turnering-UI-ene (riktig data på hull med koordinater,
ingen synlig rad på hull uten -- nettverksspor bekreftet nøyaktig
hvilke punkter som ble hentet). Lys+mørk bekreftet begge steder.
Regresjonssjekk: eksisterende GolfAPI-personlig-bane-rangefinder
satt opp i samme scratch-miljø, bekreftet uendret oppførsel etter
refaktoreringen. Scratch-stacken fullstendig revet ned.
**Rullet ut 2026-08-17** -- bruker bekreftet ("Git commit og rull
ut"). Migrasjon 079 kjørt mot ekte `teecup_db` (eksisterende
`golfapi_course_coordinate`-data verifisert uendret, 399 rader),
deretter `docker compose build teecup_api teecup_frontend && up -d`.
Rene containerlogger, `https://teecup.golf/logg-inn` 200 OK. Tjømes
offisielle bane fylt med de samme 180 koordinatene fra forrige runde
(samme mapping gjenbrukt, ikke re-avklart) via et engangsskript i
`teecup_api`-containeren mot det nye endepunktets tabell -- bekreftet
180/180 rader med korrekt `poi_type`-fordeling.