Dashboard UI: davatelj dropdown + dynamic years + KORISNIK truncate + PDF link

This commit is contained in:
2026-05-05 13:43:30 +02:00
parent 16b980e842
commit c6a5ec62aa
10 changed files with 606 additions and 72 deletions
+131 -42
View File
@@ -405,66 +405,76 @@ def api_kpi():
@app.get("/api/dashboard/top-primatelji")
def dashboard_top_primatelji(godina: int = 2025, limit: int = 50):
"""Top primatelji javnih potreba — svi klubovi sa primljenim potporama.
godina<=0 znači sve godine. Napomena 'doc_id=N' joinira pgz_sport.dokumenti za PDF link."""
def dashboard_top_primatelji(godina: int = 2025, davatelj: str = None, limit: int = 100):
"""Top primatelji javnih potreba s davatelj filter + PDF link na godišnjak."""
where = []
params = []
if godina and godina > 0:
where_god = "WHERE pn.godina = %s"
params = (godina, limit)
else:
where_god = "WHERE TRUE"
params = (limit,)
where.append("pn.godina = %s")
params.append(godina)
if davatelj and davatelj != 'all':
where.append("pn.davatelj = %s")
params.append(davatelj)
where_sql = "WHERE " + " AND ".join(where) if where else ""
rows = fetch(f"""
WITH pn_e AS (
SELECT
pn.id,
pn.naziv_kluba,
pn.klub_id,
pn.iznos,
pn.napomena,
pn.godina,
NULLIF((regexp_match(COALESCE(pn.napomena, ''), 'doc_id=(\\d+)'))[1], '')::int AS doc_id
FROM pgz_sport.potpore_nositelji pn
{where_god}
)
SELECT
pn.id,
pn.naziv_kluba,
pn.klub_id,
pn.iznos,
COALESCE(LEFT(pn.napomena, 60), '') AS napomena_short,
pn.napomena,
pn.godina,
COALESCE(pn.davatelj, 'RSS (Riječki sportski savez)') AS davatelj,
COALESCE(pn.vrsta, 'Javne potrebe') AS vrsta,
COALESCE(k.sport, 'n/a') AS sport,
COALESCE(s.naziv, '') AS savez_naziv,
COALESCE(k.razina, '') AS razina,
COALESCE(k.grad, '') AS grad,
CASE
WHEN pn.napomena ILIKE '%%županijski%%' OR pn.napomena ILIKE '%%PGZ%%' OR pn.napomena ILIKE '%%PGŽ%%' THEN 'Županijski sportski savez PGŽ'
WHEN pn.napomena ILIKE '%%riječki%%' OR pn.napomena ILIKE '%%RSS%%' THEN 'Riječki sportski savez'
WHEN pn.napomena ILIKE '%%grad rijeka%%' THEN 'Grad Rijeka'
ELSE 'Riječki sportski savez'
END AS davatelj_naziv,
CASE
WHEN pn.napomena ILIKE '%%JPS%%' OR pn.napomena ILIKE '%%javn%%' THEN 'Javne potrebe u sportu'
WHEN pn.napomena ILIKE '%%manifestacij%%' THEN 'Manifestacija'
WHEN pn.napomena ILIKE '%%objekt%%' THEN 'Sportski objekti'
ELSE 'Javne potrebe'
END AS vrsta,
COALESCE(d.pdf_url, d.url, d.izvor_url) AS pdf_url,
d.title AS doc_title
FROM pn_e pn
d.id AS doc_id,
COALESCE(d.pdf_url, d.izvor_url, '/sport/api/v2/dokumenti/godisnjak/' || pn.godina::text) AS pdf_url,
COALESCE(d.title, 'Godišnjak ' || pn.godina::text) AS doc_title
FROM pgz_sport.potpore_nositelji pn
LEFT JOIN pgz_sport.klubovi k ON k.id = pn.klub_id
LEFT JOIN pgz_sport.savezi s ON s.id = k.savez_id
LEFT JOIN pgz_sport.dokumenti d ON d.id = pn.doc_id
ORDER BY pn.iznos DESC NULLS LAST
LEFT JOIN pgz_sport.dokumenti d ON d.vrsta='godisnjak' AND d.godina = pn.godina
{where_sql}
ORDER BY pn.iznos DESC NULLS LAST, pn.naziv_kluba
LIMIT %s
""", params)
""", tuple(params) + (limit,))
# Stats summary
stats = fetch(f"""
SELECT
count(*) AS total_records,
sum(iznos)::numeric(12,2) AS total_amount,
count(DISTINCT naziv_kluba) AS unique_klubova,
count(DISTINCT davatelj) AS unique_davatelji
FROM pgz_sport.potpore_nositelji pn
{where_sql}
""", tuple(params))
# Available years (always)
years = fetch("""
SELECT godina, count(*) AS broj, sum(iznos)::numeric(12,2) AS suma
FROM pgz_sport.potpore_nositelji
GROUP BY godina ORDER BY godina DESC
""")
# Available davatelji (always)
davatelji = fetch("""
SELECT DISTINCT COALESCE(davatelj, 'Nepoznato') AS davatelj
FROM pgz_sport.potpore_nositelji ORDER BY 1
""")
return {
"godina": godina,
"count": len(rows),
"davatelj": davatelj or "all",
"rows": rows,
"ukupno": sum((r.get("iznos") or 0) for r in rows),
"summary": stats[0] if stats else {},
"available_years": years,
"available_davatelji": [d["davatelj"] for d in davatelji],
}
@@ -1813,6 +1823,8 @@ def serve_crm():
@app.get("/crm-v2/")
@app.get("/crm_v2")
@app.get("/crm_v2/")
@app.get("/crm/v2")
@app.get("/crm")
def serve_crm_v2():
p = HTML_DIR / "crm_v2.html"
if p.exists():
@@ -2386,6 +2398,83 @@ def dokument_detail(doc_id: int):
if not rows: return {"error": "not_found"}
return rows[0]
@app.get("/favicon.ico")
def serve_favicon():
from fastapi.responses import FileResponse
return FileResponse("/opt/pgz-sport/static/favicon.ico", media_type="image/x-icon")
@app.get("/api/v2/klubovi/financirani")
def klubovi_financirani(sport: str = None, davatelj: str = None, godina: int = None, limit: int = 1000):
"""Klubovi koji su primili novac od PGŽ/RSS/Grad Rijeka. davatelj: pgz|rss|grad_rijeka|any"""
where = []
params = []
if sport:
where.append("k.sport = %s")
params.append(sport)
if davatelj == 'pgz':
where.append("k.prima_pgz = true")
elif davatelj == 'rss':
where.append("k.prima_rss = true")
elif davatelj == 'grad_rijeka':
where.append("k.prima_grad_rijeka = true")
elif davatelj == 'any':
where.append("(k.prima_pgz OR k.prima_rss OR k.prima_grad_rijeka)")
where_sql = "WHERE " + " AND ".join(where) if where else ""
rows = fetch(f"""
SELECT k.id, k.naziv, k.sport, k.razina, k.oib, k.grad, k.adresa,
k.prima_pgz, k.prima_rss, k.prima_grad_rijeka, k.u_godisnjaku,
k.broj_potpora, k.ukupno_potpora,
(SELECT count(*) FROM pgz_sport.clanovi WHERE klub_id = k.id) AS sportasa
FROM pgz_sport.v_klubovi_financiranje k
{where_sql}
ORDER BY k.ukupno_potpora DESC NULLS LAST, k.naziv
LIMIT %s
""", tuple(params) + (limit,))
return {"count": len(rows), "rows": rows, "filter": {"sport": sport, "davatelj": davatelj}}
@app.get("/api/v2/sportasi/filtered")
def sportasi_filtered(sport: str = None, klub_id: int = None, kategorija: str = None,
godina_rod_od: int = None, godina_rod_do: int = None,
q: str = None, limit: int = 500):
"""Sportaši s range filter za godinu rođenja + kategorije."""
where = ["c.aktivan = true"]
params = []
if sport:
where.append("(c.sport = %s OR k.sport = %s)")
params.extend([sport, sport])
if klub_id:
where.append("c.klub_id = %s")
params.append(klub_id)
if kategorija:
where.append("(c.kategorija = %s OR EXISTS (SELECT 1 FROM pgz_sport.clan_kategorije ck WHERE ck.clan_id = c.id AND ck.kategorija = %s))")
params.extend([kategorija, kategorija])
if godina_rod_od:
where.append("(EXTRACT(YEAR FROM c.datum_rodenja) >= %s OR c.godina_rodenja >= %s)")
params.extend([godina_rod_od, godina_rod_od])
if godina_rod_do:
where.append("(EXTRACT(YEAR FROM c.datum_rodenja) <= %s OR c.godina_rodenja <= %s)")
params.extend([godina_rod_do, godina_rod_do])
if q:
where.append("(c.ime ILIKE %s OR c.prezime ILIKE %s)")
params.extend([f"%{q}%", f"%{q}%"])
where_sql = "WHERE " + " AND ".join(where)
rows = fetch(f"""
SELECT c.id, c.ime, c.prezime, c.spol, c.datum_rodenja, c.godina_rodenja,
c.kategorija, c.pozicija, c.sport, c.klub_id, k.naziv AS klub_naziv,
c.hns_igrac_id, c.source, c.source_url
FROM pgz_sport.clanovi c
LEFT JOIN pgz_sport.klubovi k ON k.id = c.klub_id
{where_sql}
ORDER BY c.prezime, c.ime
LIMIT %s
""", tuple(params) + (limit,))
return {"count": len(rows), "rows": rows}
@app.get("/")
def root(request: Request):
host = request.headers.get("host", "")