"""BAN YARO — Hundefreundliche Orte""" import math from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from typing import Optional from database import db from auth import get_current_user router = APIRouter() TYPEN = {'restaurant', 'shop', 'freilauf', 'kotbeutel', 'tierarzt', 'hundeschule'} def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float: R = 6_371_000 p1 = math.radians(lat1) p2 = math.radians(lat2) dp = math.radians(lat2 - lat1) dl = math.radians(lon2 - lon1) a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2 return 2 * R * math.asin(math.sqrt(a)) # ------------------------------------------------------------------ # Schemas # ------------------------------------------------------------------ class PlaceCreate(BaseModel): name: str typ: str lat: float lon: float adresse: Optional[str] = None website: Optional[str] = None telefon: Optional[str] = None hund_rein: Optional[bool] = None leine_pflicht: Optional[bool] = None wasser_fuer_hunde: Optional[bool] = None class PlaceUpdate(BaseModel): name: Optional[str] = None typ: Optional[str] = None lat: Optional[float]= None lon: Optional[float]= None adresse: Optional[str] = None website: Optional[str] = None telefon: Optional[str] = None hund_rein: Optional[bool] = None leine_pflicht: Optional[bool] = None wasser_fuer_hunde: Optional[bool] = None def _row_to_dict(row) -> dict: d = dict(row) for k in ('hund_rein', 'leine_pflicht', 'wasser_fuer_hunde'): if d.get(k) is not None: d[k] = bool(d[k]) return d # ------------------------------------------------------------------ # GET /api/places — alle Orte (optional: Umkreis + Typ-Filter) # ------------------------------------------------------------------ @router.get("") async def list_places( lat: Optional[float] = None, lon: Optional[float] = None, radius: int = 5000, typ: Optional[str] = None, ): with db() as conn: q = "SELECT p.*, u.name AS user_name FROM places p LEFT JOIN users u ON u.id = p.user_id" params = [] if typ: q += " WHERE p.typ = ?" params.append(typ) q += " ORDER BY p.created_at DESC" rows = conn.execute(q, params).fetchall() result = [_row_to_dict(r) for r in rows] if lat is not None and lon is not None: result = [r for r in result if _haversine(lat, lon, r['lat'], r['lon']) <= radius] return result # ------------------------------------------------------------------ # POST /api/places — neuen Ort anlegen (Login erforderlich) # ------------------------------------------------------------------ @router.post("", status_code=201) async def create_place(data: PlaceCreate, user=Depends(get_current_user)): if data.typ not in TYPEN: raise HTTPException(400, f"Ungültiger Typ. Erlaubt: {', '.join(sorted(TYPEN))}") with db() as conn: cur = conn.execute(""" INSERT INTO places (user_id, name, typ, lat, lon, adresse, website, telefon, hund_rein, leine_pflicht, wasser_fuer_hunde) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( user['id'], data.name, data.typ, data.lat, data.lon, data.adresse, data.website, data.telefon, int(data.hund_rein) if data.hund_rein is not None else None, int(data.leine_pflicht) if data.leine_pflicht is not None else None, int(data.wasser_fuer_hunde) if data.wasser_fuer_hunde is not None else None, )) row = conn.execute( "SELECT p.*, u.name AS user_name FROM places p LEFT JOIN users u ON u.id = p.user_id WHERE p.id = ?", (cur.lastrowid,) ).fetchone() return _row_to_dict(row) # ------------------------------------------------------------------ # GET /api/places/{id} # ------------------------------------------------------------------ @router.get("/{place_id}") async def get_place(place_id: int): with db() as conn: row = conn.execute( "SELECT p.*, u.name AS user_name FROM places p LEFT JOIN users u ON u.id = p.user_id WHERE p.id = ?", (place_id,) ).fetchone() if not row: raise HTTPException(404, "Ort nicht gefunden.") return _row_to_dict(row) # ------------------------------------------------------------------ # PATCH /api/places/{id} — bearbeiten (nur eigene) # ------------------------------------------------------------------ @router.patch("/{place_id}") async def update_place(place_id: int, data: PlaceUpdate, user=Depends(get_current_user)): with db() as conn: row = conn.execute("SELECT * FROM places WHERE id = ?", (place_id,)).fetchone() if not row: raise HTTPException(404, "Ort nicht gefunden.") if row['user_id'] != user['id']: raise HTTPException(403, "Nicht berechtigt.") updates = data.model_dump(exclude_none=True) if not updates: return _row_to_dict(row) for key in ('hund_rein', 'leine_pflicht', 'wasser_fuer_hunde'): if key in updates: updates[key] = int(updates[key]) cols = ', '.join(f"{k} = ?" for k in updates) conn.execute(f"UPDATE places SET {cols} WHERE id = ?", [*updates.values(), place_id]) row = conn.execute( "SELECT p.*, u.name AS user_name FROM places p LEFT JOIN users u ON u.id = p.user_id WHERE p.id = ?", (place_id,) ).fetchone() return _row_to_dict(row) # ------------------------------------------------------------------ # DELETE /api/places/{id} # ------------------------------------------------------------------ @router.delete("/{place_id}", status_code=204) async def delete_place(place_id: int, user=Depends(get_current_user)): with db() as conn: row = conn.execute("SELECT * FROM places WHERE id = ?", (place_id,)).fetchone() if not row: raise HTTPException(404, "Ort nicht gefunden.") if row['user_id'] != user['id']: raise HTTPException(403, "Nicht berechtigt.") conn.execute("DELETE FROM places WHERE id = ?", (place_id,))