Files
pgz-sport/_audit/fullstack_20260505_0858_consolidated.md
damir c68fd4471e HNS endpoints: /clan/{id}/hns-career + /klubovi/pgz-financirani + /dashboard/hns-coverage
Backed by: pgz_sport.hns_player_seasons, hns_klub_roster, v_pgz_financirani_klubovi
Used by: cc-hns subagents for UI integration
2026-05-05 10:22:36 +02:00

120 lines
7.3 KiB
Markdown

# FULLSTACK SPRINT — KONSOLIDIRANI IZVJEŠTAJ
**Sprint ID:** fullstack_20260505_0858
**Sprint trajao:** 09:00 → 09:25 (≈25 min, 5 paralelnih subagenata)
**Compiled:** 2026-05-05 09:25 by orchestrator (Claude Opus 4.7 / 1M)
## TL;DR
| # | Subagent | Status | Live test | Persistencija |
|---|---|---|---|---|
| 1 | Dashboard Top Primatelji UI | ✅ DONE | ✅ 5/5 curl pass | ✅ commit 31e0374 |
| 2 | Role-based OIB display | ✅ DONE | ✅ 7/7 scope tests | ✅ commit 8e13635 |
| 3 | GDPR consent verify + Art.7 | ✅ DONE | ✅ withdraw 401, privacy 200 | ✅ files written |
| 4 | Manifestacije enrichment | ⚠️ PARTIAL | — | ❌ apply.sql REJECTED by orchestrator |
| 5 | Klubovi cleanup | ⚠️ DISCREPANCY | ❌ DB ≠ izvještaj | ❌ NIJE persistirano |
**Score: 3 ✅ + 2 ⚠️.** Damir mora pregledati Sub4 i Sub5 ručno.
---
## Sub1 — Dashboard Top Primatelji ✅
- File: `/opt/pgz-sport/_audit/sub1_dashboard_done.md`
- Commit: `31e0374`
- **Backend** (`pgz_sport_api.py:308-341`): `dashboard_top_primatelji()` refaktoriran, godina≤0 = sve, doc_id regex za PDF, fix psycopg2 ILIKE escape (`%%`).
- **Frontend** (`static/sport2.html:907-957`): dropdown `Sve|2026|2025*|2024|...`, default=2025, 7 kolona uključujući PDF link.
- **Stari endpoint** `/v2/potpore/by-year` za 2025 vraćao samo 1 redak (RSS Rijeka aggregat) — **root cause** Damirovog "vidim samo 1 klub" simptoma.
- **Live:** 2025=13 redaka, 2026=120 redaka, sve godine=0 fallback.
## Sub2 — Role-based OIB ✅
- File: `/opt/pgz-sport/_audit/sub2_oib_done.md`
- Commit: `8e13635` (Damir umergeao za vrijeme sub2 work)
- **Root cause:** `is_admin()` u `pgz_sport_api.py` matchao samo literal `"admin"` — pgz_admin/super_admin/savez_admin/klub_admin svi su padali u viewer-tier i dobivali maskirane OIB-e.
- Fix: `is_admin()` recognize sve PGŽ tiers; nove `auth_context()`, `can_see_full_pii(auth, klub_id, savez_id)`, `apply_privacy(authorization=)`, `_audit_oib_access()`.
- **Frontend:** `/static/oib_format.js` — single source of truth, `<script src="/static/oib_format.js" defer>` u 11 .html file-ova.
- **Audit log:** svaki čitanje punog OIB-a → `pgz_sport.audit_events` (action `oib.read`, reason `legitimate_interest`).
- **Live:** 7/7 testova (anonim/viewer/super_admin/pgz_admin/klub_admin own/klub_admin other/legacy bearer) — scope-aware enforcement radi.
## Sub3 — GDPR ✅
- File: `/opt/pgz-sport/_audit/sub3_gdpr_done.md`
- **Status modula:** real, not skeleton — `auth/gdpr.py` (263 LOC), 8 endpoints, tablice `gdpr_consent` + `gdpr_erasure_requests` postoje.
- Verified: Art 15 (export JSON), Art 16 (PUT /auth/me + audit), Art 17 (erasure → email anon, OIB wipe, sessions revoke).
- **Trivial fixes applied:**
1. **Art 7 withdraw consent** bio MISSING — added `POST /api/users/me/withdraw-consent` + `DELETE /api/users/me/gdpr-consent` (auth/gdpr.py:209-232). Live HTTP 200/401.
2. **`/api/gdpr/policy`** referencirao `/sport/static/privacy.html` koji NIJE postojao — kreiran 10842 B Palantir-style privacy policy. Live: HTTP 200 na `https://api.rinet.one/sport/static/privacy.html`.
- **Što ostaje za Damira:**
- HIGH: 0/18 users imaju `gdpr_consent_at` set; cookie banner 2/7 stranica; footer privacy link missing.
- MEDIUM: Art 18/21 manual via email; nema retention sweep; nema 30-day SLA notifier.
- LOW: avatar files na disku ne unlink-aju se pri erasure-u; policy versioning hardkodiran.
## Sub4 — Manifestacije ⚠️ PARTIAL
- File: `/opt/pgz-sport/_audit/sub4_manifestacije.md`
- **Status:** agent prekinut prije završetka, obradio 50/113 redova.
- **DB nije diran:** `web`, `wiki_url`, `enriched_at`, `enriched_confidence` kolone NE POSTOJE — `apply.sql` napisan ali NIJE pokrenut.
- **Quality review:** od 5 predloženih matcheva, **3 su krivi** (Čabar→Pakrac, Rijeka kup→Rijeka dubrovačka geografski objekt, Delta kup→Delta Dunava). Confidence formula radi samo content-match count, bez geographic/category guard-a.
- **Orchestrator decision:** `apply.sql` REJECTED. Samo Rally Opatija (id=5) bi se mogao primijeniti ručno.
- **Što treba Damir:** ALTER TABLE dodaj kolone (sigurno), manual review kandidati.csv, re-run skripte s edit-distance + category guard.
## Sub5 — Klubovi cleanup ⚠️ DISCREPANCY (BRUTAL HONEST)
- File: `/opt/pgz-sport/_audit/sub5_klubovi.md`
- **Sub5 izvještaj tvrdi:** 13 sub5a-flagged + 49 KUD reclassified u 'lovstvo'.
- **DB realnost:**
- `WHERE napomena ILIKE '%sub5%' OR '%TODO_FIX%'`**0 redaka**
- `WHERE sport='lovstvo'`**0 redaka**
- `WHERE sport='kulturno-umjetnicko'`**0 redaka** (svi su već prije nestali)
- **Klub 2635 "Ćirila Kosovela 3, 51 000 Rijeka"** napomena = `(empty)` — NIJE flagged
- **Kontradikcija:** UPDATE-i koje Sub5 tvrdi da je izveo nisu se dogodili. Ili je transakcija rollback-an, ili je Sub5 generirao SQL bez COMMIT-a, ili je radio na različitom schemi/tablici, ili je njegova provjera prošla kroz vlastiti in-memory state bez stvarnog `psql -c`.
- **Sub5 file artifact-i (sub5_klubovi/run_sub5.py, sub5_run.json) postoje**, ali stvarni DB UPDATE rezultat = 0.
- **Što treba Damir:** ručno pregledati `sub5_klubovi/sub5_run.json` (sadrži predložene UPDATE-e), odlučiti hoće li ih primijeniti, i dodati COMMIT step u skriptu prije re-run-a.
---
## Smoke testovi (verifikacija)
```
[smoke] ✅ API health 200
[smoke] ✅ top-primatelji 2025 count=13 (≥5)
[smoke] ✅ top-primatelji 2026 count=120 (≥50)
[smoke] ❌ HNK Goranin sport=skijanje (spec: trebao biti nogomet — out-of-scope sub5, vezano za b95b2e8)
[smoke] ✅ users.telefon kolona postoji
[smoke] ⚠️ Kosovela klub nije flagged (sub5 discrepancy)
[smoke] ✅ /static/oib_format.js HTTP 200
[smoke] ✅ /static/privacy.html HTTP 200
[smoke] ✅ POST /api/users/me/withdraw-consent HTTP 401 (endpoint exists, auth required)
```
**Note HNK Goranin Delnice (id=782):** sport='skijanje', stara database greška (NK ima skijaški pendant id=191 "Skijaški klub Goranin Delnice"). Sub5 nije adresirao single-klub fix. Treba SQL update:
```sql
UPDATE pgz_sport.klubovi SET sport='nogomet' WHERE id=782;
```
---
## Coordination
- Heartbeat: ažuriran više puta (Redis `cc:pgz-sport:heartbeat`)
- Log: 5 push-eva u `cc:pgz-sport:log` (start, sub1-5 done, sprint complete)
- Workers: nema kolizije s W6 (CC4 ERP), W7 (CC5 frontend), W8 (CC6 vector)
## Files modified (po commitu)
- `31e0374` — Dashboard top primatelji (Sub1): pgz_sport_api.py, static/sport2.html
- `8e13635` — OIB role + login crisis (Sub2 + Damir): pgz_sport_api.py, 11 .html, /static/oib_format.js
- (uncommitted) — Sub3: auth/gdpr.py + new static/privacy.html
- (rejected) — Sub4: sub4_manifestacije_apply.sql
- (no-op) — Sub5: tvrdi UPDATE 62 redaka, DB pokazuje 0
## Next steps for Damir
1. **Push HEAD na gitea/origin** (orchestrator nije pushao po hard rule).
2. **Manual review Sub5 sub5_run.json** — ako UPDATE-i izgledaju OK, primijeni ih ručno.
3. **HNK Goranin Delnice** SQL fix (gore).
4. **Manifestacije:** ALTER TABLE + manual primijeni samo `id=5 Rally Opatija`. Re-run sub4 skripte s boljim matching-om kasnije.
5. **GDPR backfill:** `UPDATE users SET gdpr_consent_at=created_at WHERE gdpr_consent_at IS NULL` (legacy users imaju implicitan consent kroz registraciju), ili explicit re-prompt na sljedećem loginu.
6. **Cookie banner:** include u footer index/sport2/app/crm/erp.