Feature: Hundeernährungs-Feature — Kalorien-Rechner, Futter-Guide, Giftliste, KI-Berater (SW by-v698)
This commit is contained in:
parent
b1d9fb4f54
commit
6e4bf25581
7 changed files with 838 additions and 8 deletions
145
backend/routes/ernaehrung.py
Normal file
145
backend/routes/ernaehrung.py
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
"""BAN YARO — Ernährungs-Routes"""
|
||||
|
||||
import logging
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from database import db
|
||||
from auth import get_current_user
|
||||
import ki as ki_module
|
||||
|
||||
router = APIRouter()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Schemas
|
||||
# ------------------------------------------------------------------
|
||||
class FutterProfilUpdate(BaseModel):
|
||||
futter_typ: Optional[str] = None # trocken|nass|barf|mix
|
||||
marke: Optional[str] = None
|
||||
kcal_tag: Optional[int] = None
|
||||
portionen: Optional[int] = None
|
||||
notizen: Optional[str] = None
|
||||
|
||||
|
||||
class KiBeratungRequest(BaseModel):
|
||||
frage: str
|
||||
dog_name: Optional[str] = None
|
||||
rasse: Optional[str] = None
|
||||
alter: Optional[str] = None
|
||||
gewicht: Optional[float] = None
|
||||
aktiv: Optional[bool] = None
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Hilfsfunktion: Zugriffsprüfung
|
||||
# ------------------------------------------------------------------
|
||||
def _check_dog_access(conn, dog_id: int, user_id: int):
|
||||
row = conn.execute(
|
||||
"SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user_id)
|
||||
).fetchone()
|
||||
if not row:
|
||||
raise HTTPException(404, "Hund nicht gefunden.")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# GET /dogs/{dog_id}/ernaehrung
|
||||
# ------------------------------------------------------------------
|
||||
@router.get("/{dog_id}/ernaehrung")
|
||||
async def get_ernaehrung(dog_id: int, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
_check_dog_access(conn, dog_id, user["id"])
|
||||
row = conn.execute(
|
||||
"SELECT * FROM futter_profil WHERE dog_id=?", (dog_id,)
|
||||
).fetchone()
|
||||
if not row:
|
||||
return {}
|
||||
return dict(row)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# PUT /dogs/{dog_id}/ernaehrung
|
||||
# ------------------------------------------------------------------
|
||||
@router.put("/{dog_id}/ernaehrung")
|
||||
async def put_ernaehrung(dog_id: int, body: FutterProfilUpdate,
|
||||
user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
_check_dog_access(conn, dog_id, user["id"])
|
||||
existing = conn.execute(
|
||||
"SELECT id FROM futter_profil WHERE dog_id=?", (dog_id,)
|
||||
).fetchone()
|
||||
if existing:
|
||||
conn.execute("""
|
||||
UPDATE futter_profil
|
||||
SET futter_typ=COALESCE(?, futter_typ),
|
||||
marke=COALESCE(?, marke),
|
||||
kcal_tag=COALESCE(?, kcal_tag),
|
||||
portionen=COALESCE(?, portionen),
|
||||
notizen=COALESCE(?, notizen),
|
||||
updated_at=datetime('now')
|
||||
WHERE dog_id=?
|
||||
""", (body.futter_typ, body.marke, body.kcal_tag,
|
||||
body.portionen, body.notizen, dog_id))
|
||||
else:
|
||||
conn.execute("""
|
||||
INSERT INTO futter_profil
|
||||
(dog_id, futter_typ, marke, kcal_tag, portionen, notizen)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", (dog_id, body.futter_typ, body.marke, body.kcal_tag,
|
||||
body.portionen or 2, body.notizen))
|
||||
row = conn.execute(
|
||||
"SELECT * FROM futter_profil WHERE dog_id=?", (dog_id,)
|
||||
).fetchone()
|
||||
return dict(row)
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /dogs/{dog_id}/ernaehrung/ki-beratung
|
||||
# ------------------------------------------------------------------
|
||||
@router.post("/{dog_id}/ernaehrung/ki-beratung")
|
||||
async def ki_ernaehrung(dog_id: int, body: KiBeratungRequest,
|
||||
request: Request,
|
||||
user=Depends(get_current_user)):
|
||||
if not body.frage or len(body.frage.strip()) < 3:
|
||||
raise HTTPException(400, "Bitte stelle eine Frage.")
|
||||
if len(body.frage) > 800:
|
||||
raise HTTPException(400, "Frage zu lang (max. 800 Zeichen).")
|
||||
|
||||
with db() as conn:
|
||||
_check_dog_access(conn, dog_id, user["id"])
|
||||
|
||||
dog_name = body.dog_name or "unbekannt"
|
||||
rasse = body.rasse or "unbekannt"
|
||||
alter = body.alter or "unbekannt"
|
||||
gewicht = f"{body.gewicht} kg" if body.gewicht else "unbekannt"
|
||||
aktiv_str = "aktiv" if body.aktiv else "normal aktiv"
|
||||
|
||||
system = (
|
||||
"Du bist Ernährungsberater für Hunde. "
|
||||
"Antworte immer auf Deutsch, kurz und praktisch. "
|
||||
"Keine unnötigen Füllsätze. "
|
||||
"Weise bei ernsthaften Gesundheitsfragen immer auf den Tierarzt hin. "
|
||||
"Stelle keine medizinischen Diagnosen."
|
||||
)
|
||||
|
||||
prompt = (
|
||||
f"Hund: {dog_name}, Rasse: {rasse}, Alter: {alter}, "
|
||||
f"Gewicht: {gewicht}, Aktivität: {aktiv_str}.\n\n"
|
||||
f"Frage: {body.frage.strip()}\n\n"
|
||||
"Antworte konkret und praktisch, maximal 200 Wörter."
|
||||
)
|
||||
|
||||
try:
|
||||
antwort = await ki_module.complete(
|
||||
prompt=prompt,
|
||||
system=system,
|
||||
max_tokens=500,
|
||||
requires_premium=False,
|
||||
user_id=user["id"],
|
||||
)
|
||||
return {"antwort": antwort}
|
||||
except ki_module.KIUnavailableError as e:
|
||||
raise HTTPException(503, str(e))
|
||||
except Exception:
|
||||
raise HTTPException(500, "KI momentan nicht verfügbar.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue