Feature: Gassi-Hundefotos bei Teilnehmern + Fotos nach dem Treffen (SW by-v878)
This commit is contained in:
parent
b6a644ac3a
commit
44ba51cd38
8 changed files with 230 additions and 20 deletions
|
|
@ -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,))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue