Feature: Tierarzt-Bewertungen — Sterne-Rating pro Praxis mit Detail-Modal (SW by-v700)

This commit is contained in:
rene 2026-05-04 21:02:49 +02:00
parent c5030024b0
commit 40de0f38aa
5 changed files with 461 additions and 5 deletions

View file

@ -27,6 +27,14 @@ class TierarztCreate(BaseModel):
osm_id: Optional[str] = None
class BewertungCreate(BaseModel):
gesamt: int
wartezeit: Optional[int] = None
freundlichkeit: Optional[int] = None
kompetenz: Optional[int] = None
text: Optional[str] = None
class TierarztUpdate(BaseModel):
name: Optional[str] = None
strasse: Optional[str] = None
@ -220,3 +228,109 @@ async def update_tierarzt(tierarzt_id: int, data: TierarztUpdate,
)
row = conn.execute("SELECT * FROM tieraerzte WHERE id=?", (tierarzt_id,)).fetchone()
return dict(row)
# ------------------------------------------------------------------
# BEWERTUNGEN
# ------------------------------------------------------------------
def _refresh_vet_rating(conn, tierarzt_id: int):
"""Aktualisiert avg_rating und anz_bewertungen in tieraerzte."""
row = conn.execute(
"""SELECT COUNT(*) AS n, AVG(CAST(gesamt AS REAL)) AS avg
FROM tierarzt_bewertungen WHERE tierarzt_id=?""",
(tierarzt_id,)
).fetchone()
n = row["n"] or 0
avg = row["avg"] or 0.0
conn.execute(
"UPDATE tieraerzte SET avg_rating=?, anz_bewertungen=? WHERE id=?",
(round(avg, 1), n, tierarzt_id)
)
@router.post("/{tierarzt_id}/bewertung", status_code=201)
async def create_bewertung(tierarzt_id: int, data: BewertungCreate,
user=Depends(get_current_user)):
"""Bewertung abgeben (1×pro User+Tierarzt, UPSERT)."""
if not (1 <= data.gesamt <= 5):
raise HTTPException(400, "Gesamtbewertung muss zwischen 1 und 5 liegen.")
for field in ("wartezeit", "freundlichkeit", "kompetenz"):
val = getattr(data, field)
if val is not None and not (1 <= val <= 5):
raise HTTPException(400, f"{field} muss zwischen 1 und 5 liegen.")
text = (data.text or "").strip()[:500] or None
with db() as conn:
vet = conn.execute("SELECT id FROM tieraerzte WHERE id=?", (tierarzt_id,)).fetchone()
if not vet:
raise HTTPException(404, "Tierarzt nicht gefunden.")
conn.execute(
"""INSERT INTO tierarzt_bewertungen
(tierarzt_id, user_id, gesamt, wartezeit, freundlichkeit, kompetenz, text)
VALUES (?,?,?,?,?,?,?)
ON CONFLICT(tierarzt_id, user_id) DO UPDATE SET
gesamt=excluded.gesamt,
wartezeit=excluded.wartezeit,
freundlichkeit=excluded.freundlichkeit,
kompetenz=excluded.kompetenz,
text=excluded.text,
created_at=datetime('now')""",
(tierarzt_id, user["id"], data.gesamt, data.wartezeit,
data.freundlichkeit, data.kompetenz, text)
)
_refresh_vet_rating(conn, tierarzt_id)
row = conn.execute(
"SELECT * FROM tieraerzte WHERE id=?", (tierarzt_id,)
).fetchone()
return dict(row)
@router.get("/{tierarzt_id}/bewertungen")
async def list_bewertungen(tierarzt_id: int):
"""Alle Bewertungen für einen Tierarzt (public). Gibt Zusammenfassung + letzte 5 Texte."""
with db() as conn:
vet = conn.execute(
"SELECT id, avg_rating, anz_bewertungen FROM tieraerzte WHERE id=?",
(tierarzt_id,)
).fetchone()
if not vet:
raise HTTPException(404, "Tierarzt nicht gefunden.")
# Stern-Verteilung
verteilung = {}
for star in range(1, 6):
r = conn.execute(
"SELECT COUNT(*) AS n FROM tierarzt_bewertungen WHERE tierarzt_id=? AND gesamt=?",
(tierarzt_id, star)
).fetchone()
verteilung[str(star)] = r["n"]
# Letzte 5 Kommentare
kommentare = conn.execute(
"""SELECT gesamt, wartezeit, freundlichkeit, kompetenz, text, created_at
FROM tierarzt_bewertungen
WHERE tierarzt_id=? AND text IS NOT NULL AND text != ''
ORDER BY created_at DESC LIMIT 5""",
(tierarzt_id,)
).fetchall()
return {
"avg_rating": vet["avg_rating"] or 0,
"anz_bewertungen": vet["anz_bewertungen"] or 0,
"verteilung": verteilung,
"kommentare": [dict(k) for k in kommentare],
}
@router.get("/{tierarzt_id}/meine-bewertung")
async def get_meine_bewertung(tierarzt_id: int, user=Depends(get_current_user)):
"""Eigene Bewertung für einen Tierarzt (oder null)."""
with db() as conn:
row = conn.execute(
"SELECT * FROM tierarzt_bewertungen WHERE tierarzt_id=? AND user_id=?",
(tierarzt_id, user["id"])
).fetchone()
return dict(row) if row else None