feat: /api/v2/analiza/* endpoints - sport analytics backend
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv('/opt/rinet-gpu/.env.master')
|
||||
# auto-added by patch_scrapers_with_dotenv.sh
|
||||
"""
|
||||
Objekti Router — sportski objekti PGZ
|
||||
Created: 2026-05-09 v4.0-final pre-K3s
|
||||
Endpoints: GET /api/v2/objekti/list (paginated), /api/v2/objekti/{id}
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from typing import Optional, List
|
||||
import os, json
|
||||
from psycopg2.extras import RealDictCursor
|
||||
import psycopg2
|
||||
|
||||
router = APIRouter(prefix="/api/v2/objekti", tags=["objekti"])
|
||||
|
||||
def get_conn():
|
||||
return psycopg2.connect(
|
||||
host=os.getenv("DB_HOST", "10.10.0.2"),
|
||||
port=int(os.getenv("DB_PORT", "6432")),
|
||||
dbname=os.getenv("DB_NAME", "rinet_v3"),
|
||||
user=os.getenv("DB_USER", "rinet"),
|
||||
password=os.environ["DB_PASSWORD"]
|
||||
)
|
||||
|
||||
@router.get("/list")
|
||||
def list_objekti(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
grad: Optional[str] = None,
|
||||
tip: Optional[str] = None,
|
||||
region: Optional[str] = None,
|
||||
search: Optional[str] = None,
|
||||
):
|
||||
"""List sportski objekti sa filtriranjem"""
|
||||
where = ["aktivan = true"]
|
||||
params = []
|
||||
|
||||
if grad:
|
||||
where.append("grad ILIKE %s")
|
||||
params.append(f"%{grad}%")
|
||||
if tip:
|
||||
where.append("tip = %s")
|
||||
params.append(tip)
|
||||
if region:
|
||||
where.append("region ILIKE %s")
|
||||
params.append(f"%{region}%")
|
||||
if search:
|
||||
where.append("fts @@ plainto_tsquery(\'simple\', %s)")
|
||||
params.append(search)
|
||||
|
||||
where_sql = " AND ".join(where) if where else "true"
|
||||
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
# Count
|
||||
cur.execute(f"SELECT COUNT(*) FROM pgz_sport.objekti WHERE {where_sql}", params)
|
||||
total = cur.fetchone()["count"]
|
||||
|
||||
# Data
|
||||
cur.execute(f"""
|
||||
SELECT id, naziv, tip, vlasnik, upravitelj, adresa, grad, region,
|
||||
kapacitet, godina_izgradnje, geo_lat, geo_lng, koristi_se_za,
|
||||
email, telefon, web, aktivan, pristupacan_invalidi
|
||||
FROM pgz_sport.objekti
|
||||
WHERE {where_sql}
|
||||
ORDER BY grad, naziv
|
||||
LIMIT %s OFFSET %s
|
||||
""", params + [limit, skip])
|
||||
|
||||
items = cur.fetchall()
|
||||
|
||||
return {
|
||||
"items": items,
|
||||
"total": total,
|
||||
"skip": skip,
|
||||
"limit": limit
|
||||
}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.get("/{objekt_id}")
|
||||
def get_objekt(objekt_id: int):
|
||||
"""Detail jednog objekta"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("SELECT * FROM pgz_sport.objekti WHERE id = %s", (objekt_id,))
|
||||
r = cur.fetchone()
|
||||
if not r:
|
||||
raise HTTPException(status_code=404, detail="Objekt not found")
|
||||
return r
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.get("/stats/by-tip")
|
||||
def stats_by_tip():
|
||||
"""Stats: count objekata po tip"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("""
|
||||
SELECT tip, COUNT(*) as count
|
||||
FROM pgz_sport.objekti
|
||||
WHERE aktivan = true
|
||||
GROUP BY tip
|
||||
ORDER BY 2 DESC
|
||||
""")
|
||||
return {"items": cur.fetchall()}
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@router.get("/stats/by-region")
|
||||
def stats_by_region():
|
||||
"""Stats: count objekata po region"""
|
||||
conn = get_conn()
|
||||
try:
|
||||
with conn.cursor(cursor_factory=RealDictCursor) as cur:
|
||||
cur.execute("""
|
||||
SELECT region, COUNT(*) as count
|
||||
FROM pgz_sport.objekti
|
||||
WHERE aktivan = true AND region IS NOT NULL
|
||||
GROUP BY region
|
||||
ORDER BY 2 DESC
|
||||
""")
|
||||
return {"items": cur.fetchall()}
|
||||
finally:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user