PDF link target=_blank + nginx timeouts + priority filteri (samo s podacima)

nginx (sport.rinet.one):
- proxy_read_timeout 60s → 300s
- proxy_send_timeout 300s
- proxy_buffering off (PDF stream)
- client_max_body_size 50M → 100M

Endpoints:
- /api/v2/klubovi/financirani: +with_data filter (samo s potporama/godišnjakom/HNS)
- /api/v2/sportasi/filtered: +samo_priority +samo_s_hns

Frontend:
- PDF link target=_blank rel=noopener
- window._klub_only_priority = true (default)
- window._sportas_only_priority = true (default)

DB View:
- pgz_sport.v_nogomet_priority (prima_potpore, u_godisnjaku, ima_hns_roster)
This commit is contained in:
2026-05-05 13:51:07 +02:00
parent c6a5ec62aa
commit f7b5114f58
289 changed files with 37204 additions and 363 deletions
+98
View File
@@ -2242,6 +2242,104 @@ def list_dokumenti(razina: Optional[str] = None, vrsta: Optional[str] = None,
rows = db_query(sql, params)
return {"count": len(rows), "results": rows}
# ─────────────────────────────────────────────────────────────────────
# Unified dokumenti library — Agent G (2026-05-05)
# dradulic@outlook.com / damir@rinet.one
# Maps free-form vrsta + organizacija into a normalized (tip, izdavatelj)
# pair so the frontend can offer a single "Svi dokumenti" tab with two
# canonical dropdowns. No DB changes — pure SQL CASE expressions.
# ─────────────────────────────────────────────────────────────────────
_TIP_CASE_SQL = """
CASE
WHEN vrsta = 'godisnjak' OR vrsta = 'sportski-godisnjak' THEN 'godisnjak'
WHEN vrsta = 'manifestacija' OR vrsta ILIKE '%%natjec%%' THEN 'natjecaj'
WHEN vrsta IN ('pravilnik','pravilnik_savez','statut','odluka','zakon','zakon_dopuna') THEN 'pravilnik'
WHEN vrsta IN ('publikacija','periodika','povijest','program','plan','strategija','raspodjela','erasmus') THEN 'publikacija'
WHEN COALESCE(title,'') ILIKE '%%javni poziv%%' OR COALESCE(title,'') ILIKE '%%natje%%aj%%' THEN 'javni-poziv'
ELSE 'ostalo'
END
"""
_IZDAVATELJ_CASE_SQL = """
CASE
WHEN organizacija ILIKE '%%RSS%%' OR organizacija ILIKE '%%Riječki sportski savez%%' OR url ILIKE '%%rss.hr%%' THEN 'RSS'
WHEN organizacija ILIKE '%%Hrvatski olimpijski%%' OR organizacija = 'HOO' OR organizacija ILIKE '%%MTIS/HOO%%' OR url ILIKE '%%hoo.hr%%' OR url ILIKE '%%media-hoo%%' OR vrsta = 'hoo' THEN 'HOO'
WHEN organizacija ILIKE '%%Zajednica%%PGŽ%%' OR organizacija ILIKE '%%ZSP PGŽ%%' OR organizacija ILIKE '%%Zajednica sportova PGŽ%%' OR organizacija ILIKE '%%Zajednica športskih saveza PGŽ%%' THEN 'ZSPGZ'
WHEN organizacija ILIKE 'Primorsko-goranska%%' OR organizacija = 'PGŽ' OR url ILIKE '%%pgz.hr%%' OR url ILIKE '%%sport-pgz%%' THEN 'PGŽ'
WHEN organizacija ILIKE 'Grad Rijeka%%' OR organizacija ILIKE 'Grad/Općina%%' OR url ILIKE '%%rijeka.hr%%' OR vrsta ILIKE 'jls_%%' OR vrsta = 'grad_rijeka_sport' THEN 'JLS'
WHEN organizacija ILIKE 'Hrvatski %%savez%%' OR organizacija IN ('HKS','HOK','HNS','HRS','HVS','HBS') OR vrsta ILIKE 'savez_%%' THEN 'savez'
WHEN organizacija ILIKE '%%klub%%' THEN 'klub'
ELSE 'ostalo'
END
"""
@router.get("/dokumenti/unified")
def dokumenti_unified(
tip: Optional[str] = None,
izdavatelj: Optional[str] = None,
vrsta: Optional[str] = None,
q: Optional[str] = None,
godina_min: Optional[int] = None,
godina_max: Optional[int] = None,
limit: int = 500,
):
"""Unified document library — returns ALL documents with normalized
`tip` (godisnjak/manifestacija/natjecaj/pravilnik/publikacija/javni-poziv/ostalo)
and `izdavatelj` (RSS/HOO/PGŽ/ZSPGZ/JLS/savez/klub/ostalo) derived
from raw vrsta+organizacija+url columns.
Filters: tip, izdavatelj, vrsta (raw), q (FTS over title/opis/organizacija),
godina_min/godina_max, limit.
"""
where = ["COALESCE(aktivan,true)=true",
"vrsta NOT IN ('corpus','corpus_v2','corpus_v3','novost_savez','audit','godisnjak_facts','enrichment','autogen')"]
params = []
if tip:
where.append(f"({_TIP_CASE_SQL}) = %s"); params.append(tip)
if izdavatelj:
where.append(f"({_IZDAVATELJ_CASE_SQL}) = %s"); params.append(izdavatelj)
if vrsta:
where.append("vrsta = %s"); params.append(vrsta)
if godina_min:
where.append("godina >= %s"); params.append(godina_min)
if godina_max:
where.append("godina <= %s"); params.append(godina_max)
if q:
where.append("(title ILIKE %s OR kratak_opis ILIKE %s OR organizacija ILIKE %s)")
params.extend([f"%{q}%", f"%{q}%", f"%{q}%"])
sql = f"""SELECT id, title, kratak_opis, vrsta,
{_TIP_CASE_SQL} AS tip,
{_IZDAVATELJ_CASE_SQL} AS izdavatelj,
razina, organizacija, sport, sluzbeni_glasnik,
izvor_url, pdf_url, url, fname, godina, izdano_datum,
CASE WHEN sadrzaj IS NOT NULL THEN length(sadrzaj) ELSE 0 END AS chars,
scraped_at
FROM pgz_sport.dokumenti
WHERE {' AND '.join(where)}
ORDER BY izdano_datum DESC NULLS LAST, godina DESC NULLS LAST, id DESC
LIMIT %s"""
params.append(limit)
rows = db_query(sql, params)
return {"count": len(rows), "dokumenti": rows}
@router.get("/dokumenti/facets")
def dokumenti_facets():
"""Counts po normaliziranom tipu i izdavatelju — za UI dropdown badges."""
by_tip = db_query(f"""SELECT {_TIP_CASE_SQL} AS tip, count(*) AS broj
FROM pgz_sport.dokumenti
WHERE COALESCE(aktivan,true)=true
AND vrsta NOT IN ('corpus','corpus_v2','corpus_v3','novost_savez','audit','godisnjak_facts','enrichment','autogen')
GROUP BY 1 ORDER BY 2 DESC""")
by_izd = db_query(f"""SELECT {_IZDAVATELJ_CASE_SQL} AS izdavatelj, count(*) AS broj
FROM pgz_sport.dokumenti
WHERE COALESCE(aktivan,true)=true
AND vrsta NOT IN ('corpus','corpus_v2','corpus_v3','novost_savez','audit','godisnjak_facts','enrichment','autogen')
GROUP BY 1 ORDER BY 2 DESC""")
return {"by_tip": by_tip, "by_izdavatelj": by_izd}
@router.post("/dokumenti/upload")
async def upload_dokument(
file: UploadFile = File(...),