Feature: Gassi-Hundefotos bei Teilnehmern + Fotos nach dem Treffen (SW by-v878)

This commit is contained in:
rene 2026-05-12 17:04:43 +02:00
parent b6a644ac3a
commit 44ba51cd38
8 changed files with 230 additions and 20 deletions

View file

@ -1,15 +1,17 @@
"""BAN YARO — Gassi-Treffen"""
import math
import math, os, uuid
import httpx
from datetime import date
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from pydantic import BaseModel
from typing import Optional, List
from database import db
from auth import get_current_user
from routes.push import send_push_to_user
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
router = APIRouter()
@ -371,9 +373,34 @@ async def get_walk(walk_id: int):
GROUP BY wp.user_id
""", (walk_id,)).fetchall()
# Hunde-Details (Foto + Rasse) pro Teilnehmer
dog_rows = conn.execute("""
SELECT wpd.user_id, d.name AS dog_name, d.foto_url, d.rasse
FROM walk_participant_dogs wpd
JOIN dogs d ON d.id = wpd.dog_id
WHERE wpd.walk_id = ?
""", (walk_id,)).fetchall()
# Walk-Fotos
photos = conn.execute(
"SELECT id, user_id, url, created_at FROM walk_photos WHERE walk_id=? ORDER BY created_at",
(walk_id,)
).fetchall()
from collections import defaultdict
dogs_by_user = defaultdict(list)
for r in dog_rows:
dogs_by_user[r['user_id']].append({
'name': r['dog_name'], 'foto_url': r['foto_url'], 'rasse': r['rasse']
})
result = dict(walk)
result['teilnehmer'] = [dict(p) for p in participants]
result['teilnehmer'] = [
{**dict(p), 'hunde_liste': dogs_by_user.get(p['user_id'], [])}
for p in participants
]
result['teilnehmer_count'] = len(result['teilnehmer'])
result['photos'] = [dict(p) for p in photos]
return result
@ -508,3 +535,82 @@ async def leave_walk(walk_id: int, user=Depends(get_current_user)):
conn.execute("UPDATE walks SET status = 'offen' WHERE id = ?", (walk_id,))
return {"status": "left", "teilnehmer_count": count}
# ------------------------------------------------------------------
# POST /api/walks/{id}/photos — Foto nach dem Treffen hochladen
# GET /api/walks/{id}/photos — Fotos eines Treffens abrufen
# ------------------------------------------------------------------
@router.post("/{walk_id}/photos", status_code=201)
async def upload_walk_photo(
walk_id: int,
file: UploadFile = File(...),
user=Depends(get_current_user)
):
with db() as conn:
walk = conn.execute("SELECT * FROM walks WHERE id=?", (walk_id,)).fetchone()
if not walk:
raise HTTPException(404, "Treffen nicht gefunden.")
# Nur Teilnehmer oder Veranstalter dürfen Fotos hochladen
is_participant = conn.execute(
"SELECT 1 FROM walk_participants WHERE walk_id=? AND user_id=?",
(walk_id, user['id'])
).fetchone()
if not is_participant and walk['user_id'] != user['id']:
raise HTTPException(403, "Nur Teilnehmer können Fotos hochladen.")
import io
from PIL import Image, ImageOps
try:
import pillow_heif; pillow_heif.register_heif_opener()
except ImportError:
pass
raw = await file.read()
try:
img = Image.open(io.BytesIO(raw))
img = ImageOps.exif_transpose(img).convert("RGB")
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=88)
raw = buf.getvalue()
except Exception:
pass
filename = f"walk_{walk_id}_{uuid.uuid4().hex[:8]}.jpg"
path = os.path.join(MEDIA_DIR, "walks", filename)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "wb") as f:
f.write(raw)
url = f"/media/walks/{filename}"
with db() as conn:
cur = conn.execute(
"INSERT INTO walk_photos (walk_id, user_id, url) VALUES (?,?,?)",
(walk_id, user['id'], url)
)
row = conn.execute("SELECT * FROM walk_photos WHERE id=?", (cur.lastrowid,)).fetchone()
return dict(row)
@router.get("/{walk_id}/photos")
async def get_walk_photos(walk_id: int):
with db() as conn:
rows = conn.execute(
"SELECT * FROM walk_photos WHERE walk_id=? ORDER BY created_at",
(walk_id,)
).fetchall()
return [dict(r) for r in rows]
@router.delete("/{walk_id}/photos/{photo_id}", status_code=204)
async def delete_walk_photo(walk_id: int, photo_id: int, user=Depends(get_current_user)):
with db() as conn:
photo = conn.execute(
"SELECT * FROM walk_photos WHERE id=? AND walk_id=?", (photo_id, walk_id)
).fetchone()
if not photo:
raise HTTPException(404)
walk = conn.execute("SELECT user_id FROM walks WHERE id=?", (walk_id,)).fetchone()
if photo['user_id'] != user['id'] and walk['user_id'] != user['id']:
raise HTTPException(403)
conn.execute("DELETE FROM walk_photos WHERE id=?", (photo_id,))