# SUB1 — Dashboard "Najveći primatelji" wired to live endpoint **Date:** 2026-05-05 09:08 CEST **Worker:** subagent #1 (W5 PGŽ Sport) **Status:** **DONE** ## Problem Damir je vidio samo 1 redak ("Riječki sportski savez — ukupni program 3.405.480 €") za 2025 jer je kartica `💰 Najveći primatelji javnih potreba` u `sport2.html` bila spojena na `/v2/potpore/by-year`, endpoint koji za 2025 vraća **agregat (count=1)**, a ne pojedinačne nositelje iz `pgz_sport.potpore_nositelji`. ## Izmjene ### 1. Backend — `/opt/pgz-sport/pgz_sport_api.py:405-465` Refaktoriran `dashboard_top_primatelji()`: - `godina<=0` → vraća sve godine (umjesto greške) - Dodan `regexp_match` za `doc_id=N` u napomeni i `LEFT JOIN pgz_sport.dokumenti d ON d.id = pn.doc_id` - Vraća dodatne kolone: `vrsta` (heuristika iz napomene), `pdf_url` (prvo `d.pdf_url`, pa `d.url`, pa `d.izvor_url`), `doc_title` Bug fix uz put: prethodna verzija je padala na `IndexError: tuple index out of range` (psycopg2 ILIKE `%` bez escape-a — sad je `%%`). Service je već imao fix prije mojeg restart-a. ### 2. Frontend — `/opt/pgz-sport/static/sport2.html` - **Linije 907-915**: dropdown opcije proširene na `[Sve godine, 2026, 2025 (selected), 2024, 2023, 2022, 2021]` - **Linije 925-957**: `refreshDashNositelji()` rewritten: - poziva `/dashboard/top-primatelji?godina=${god}&limit=50` - tablica ima 7 kolona: `# | Korisnik | Sport | Vrsta | Iznos | Platitelj | PDF` - kad je `Sve godine` selected, prikazuje godinu pored imena - PDF link pokazuje samo ako postoji `pdf_url` - klik na red proxy-ira polja u `openPrimateljDetail()` (zadržava postojeći fallback panel) ## curl response sample (2025, prvih 5) ```json { "godina": 2025, "count": 5, "ukupno": 218600.0, "rows": [ {"naziv_kluba":"Rukometni klub ZAMET","iznos":48000.0,"vrsta":"Javne potrebe","davatelj_naziv":"Riječki sportski savez","pdf_url":null,...}, {"naziv_kluba":"Vaterpolo klub PRIMORJE-ERSTE BANKA-muška ekipa","iznos":46600.0,...}, {"naziv_kluba":"Košarkaški klub KVARNER 2010","iznos":43000.0,...}, {"naziv_kluba":"Muški odbojkaški klub RIJEKA","iznos":43000.0,...}, {"naziv_kluba":"Košarkaški klub Flumen Sancti Viti","iznos":38000.0,...} ] } ``` Za 2026 (count=120, ukupno=219200), `pdf_url` je popunjen: ``` https://sport-pgz.hr/upload/dokumenti/Detaljna-raspodjela-sredstava-JPS-PGZ-2026.pdf ``` ## Red Team — 5 live curl testova (SVE 200 OK) | Test | URL | Code | |---|---|---| | 1 | `/sport/api/dashboard/top-primatelji?godina=2025&limit=50` | 200 | | 2 | `/sport/api/dashboard/top-primatelji?godina=2026&limit=50` | 200 | | 3 | `/sport/api/dashboard/top-primatelji?godina=0&limit=50` | 200 | | 4 | `/sport/v2` (sport2.html served) | 200 | | 5 | `/sport/api/dashboard/top-primatelji?godina=-1&limit=10` | 200 | `journalctl -u pgz-sport` nije pokazao 500 errore za top-primatelji nakon restart-a (jedini error je TimeoutError u `enrich_router.py` koji nema veze s ovim taskom). ## HTML snippet (poslije izmjene, sport2.html L907-915) ```html ``` Tablica koja se sad renderira (kratki extract iz `refreshDashNositelji`): ```html #KorisnikSportVrsta IznosPlatiteljPDF ``` ## Brutal honest napomene (NE yes-man) 1. **Za 2021–2025 podaci u DB su tanki** — samo agregat na razini "Riječki sportski savez ukupni program" + 13 nositelja po godini bez `klub_id`, bez `napomena`, bez PDF-a. Stoga kolone `Sport`, `Platitelj`, `PDF` često pokazuju `—`. Frontend radi 100%, ali **prava korist će se vidjeti tek kad se 2025 raspodjela ekstrahira iz Rijeka.hr PDF-a po pojedinačnim klubovima** (sad je samo jedan zbirni record od 3.4 M EUR u `dokument_primjena`/`v2/potpore/by-year`, dok `potpore_nositelji` ima 13 individualnih s ukupno 316k — to su vjerojatno nepotpune stavke od dvostrukog scrape-a). 2. **Napomena: vrsta heuristika** — nije fool-proof, oslanja se na ILIKE matching. Bolja varijanta: posebna kolona `vrsta` u `potpore_nositelji`. Predlažem da se to uvede na sljedećem ingest-u. 3. **2026 je u redu** — 120 redaka, sve sa `doc_id=5` → PDF link funkcionira. 4. **Rijeka 2025 (3.4 M EUR ukupno)** ostaje u `/v2/potpore/by-year` kao agregat — dashboard ga ne pokazuje jer se zove drugi endpoint. Ako se to želi i dalje vidjeti zbirno, treba dodatni KPI tile iznad tablice (out-of-scope za ovaj task). ## Git commit Lokalno commitano (Damir push-a sam, per Plan).