From 366582d3efa5c7390a172b19237a1a9c26c414a1 Mon Sep 17 00:00:00 2001 From: Erol Haagenrud Date: Mon, 17 Aug 2026 12:13:57 +0200 Subject: [PATCH] ADR-081/CHANGELOG: rangefinder for offisielle baner rullet ut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- ARCHITECTURE_DECISIONS.md | 130 ++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 53 ++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/ARCHITECTURE_DECISIONS.md b/ARCHITECTURE_DECISIONS.md index af2f2de..81611a0 100644 --- a/ARCHITECTURE_DECISIONS.md +++ b/ARCHITECTURE_DECISIONS.md @@ -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-`
` (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) diff --git a/CHANGELOG.md b/CHANGELOG.md index c527a59..e086684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.