Sportski objekti: API + Leaflet map page + address enrichment
DB: pgz_sport.sportski_objekti (103 objekti, 103 s geo, 60 s adresom, 31 tip) API: - /api/v2/sportski-objekti (filter: tip, grad, sport, q) - /api/v2/sportski-objekti/meta (tipovi, gradovi, sportovi, ukupno) Frontend: - /static/objekti.html — Leaflet (OpenStreetMap) interactive map - 3 dropdown filter (tip, grad, sport) + search - Side panel s listom + map markers s ikonama (🏟️⚽🏊⛵🎿🎳⛸️🎯🥌🏃) - Popup: naziv, tip, kapacitet, adresa, upravitelj, izgradeno, sportovi, web link, Google Maps link - /objekti, /sport/objekti, /sport/api/v2/sportski-objekti routes Sidebar app.html: +Sportski objekti link Background: scripts/objekti_enrich_address.py (Nominatim reverse-geocode 60 objekata bez adrese)
This commit is contained in:
@@ -2835,6 +2835,56 @@ def auth_me_v2_alias(authorization: str = Header(None)):
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/api/v2/sportski-objekti")
|
||||
def sportski_objekti_v2_list(tip: str = None, grad: str = None, sport: str = None, q: str = None, limit: int = 500):
|
||||
"""Sportski objekti PGŽ s filterima."""
|
||||
where = ["aktivan = true"]
|
||||
params = []
|
||||
if tip:
|
||||
where.append("tip = %s"); params.append(tip)
|
||||
if grad:
|
||||
where.append("grad = %s"); params.append(grad)
|
||||
if sport:
|
||||
where.append("%s = ANY(sportovi)"); params.append(sport)
|
||||
if q:
|
||||
where.append("(naziv ILIKE %s OR adresa ILIKE %s OR upravitelj ILIKE %s)")
|
||||
params.extend([f"%{q}%"]*3)
|
||||
|
||||
rows = fetch(f"""
|
||||
SELECT id, naziv, tip, grad, adresa, lat, lng, upravitelj, kapacitet,
|
||||
sportovi, izgradeno, obnovljeno_god, "veličina" AS velicina, natkrita,
|
||||
napomena, web
|
||||
FROM pgz_sport.sportski_objekti
|
||||
WHERE {' AND '.join(where)}
|
||||
ORDER BY grad, naziv
|
||||
LIMIT %s
|
||||
""", tuple(params) + (limit,))
|
||||
return {"count": len(rows), "rows": rows}
|
||||
|
||||
|
||||
@app.get("/api/v2/sportski-objekti/meta")
|
||||
def sportski_objekti_meta():
|
||||
"""Dropdown options za filter."""
|
||||
tipovi = fetch("SELECT tip, count(*) AS broj FROM pgz_sport.sportski_objekti WHERE aktivan = true AND tip IS NOT NULL GROUP BY tip ORDER BY broj DESC")
|
||||
gradovi = fetch("SELECT grad, count(*) AS broj FROM pgz_sport.sportski_objekti WHERE aktivan = true AND grad IS NOT NULL GROUP BY grad ORDER BY broj DESC")
|
||||
sportovi = fetch("SELECT DISTINCT unnest(sportovi) AS sport, count(*) AS broj FROM pgz_sport.sportski_objekti WHERE aktivan = true AND sportovi IS NOT NULL GROUP BY sport ORDER BY broj DESC LIMIT 50")
|
||||
return {
|
||||
"tipovi": tipovi,
|
||||
"gradovi": gradovi,
|
||||
"sportovi": sportovi,
|
||||
"ukupno": (fetch("SELECT count(*) AS n FROM pgz_sport.sportski_objekti WHERE aktivan = true")[0])["n"]
|
||||
}
|
||||
|
||||
|
||||
@app.get("/objekti")
|
||||
@app.get("/objekti/")
|
||||
@app.get("/sport/objekti")
|
||||
@app.get("/sport/objekti/")
|
||||
def serve_objekti():
|
||||
from fastapi.responses import FileResponse
|
||||
return FileResponse("/opt/pgz-sport/static/objekti.html")
|
||||
|
||||
@app.get("/")
|
||||
def root(request: Request):
|
||||
host = request.headers.get("host", "")
|
||||
|
||||
Reference in New Issue
Block a user