Feature: Aktive Erinnerungen, Versicherung, Verhaltensprotokoll, Hundefreundliche Orte (SW by-v874)
This commit is contained in:
parent
83034c0db0
commit
b818f85f36
11 changed files with 589 additions and 14 deletions
|
|
@ -569,3 +569,190 @@ async def terminvorschlaege(dog_id: int, user=Depends(get_current_user)):
|
|||
})
|
||||
|
||||
return vorschlaege
|
||||
|
||||
|
||||
# ==================================================================
|
||||
# VERSICHERUNGS-VERWALTUNG
|
||||
# ==================================================================
|
||||
|
||||
class InsuranceCreate(BaseModel):
|
||||
anbieter: str
|
||||
police_nr: Optional[str] = None
|
||||
jahresbeitrag: Optional[float] = None
|
||||
kontakt: Optional[str] = None
|
||||
ablaufdatum: Optional[str] = None
|
||||
notizen: Optional[str] = None
|
||||
|
||||
class InsuranceUpdate(BaseModel):
|
||||
anbieter: Optional[str] = None
|
||||
police_nr: Optional[str] = None
|
||||
jahresbeitrag: Optional[float] = None
|
||||
kontakt: Optional[str] = None
|
||||
ablaufdatum: Optional[str] = None
|
||||
notizen: Optional[str] = None
|
||||
|
||||
|
||||
@router.get("/{dog_id}/insurance")
|
||||
async def get_insurance(dog_id: int, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone() or (_ for _ in ()).throw(HTTPException(404))
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM dog_insurance WHERE dog_id=? ORDER BY created_at DESC", (dog_id,)
|
||||
).fetchall()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
@router.post("/{dog_id}/insurance", status_code=201)
|
||||
async def create_insurance(dog_id: int, data: InsuranceCreate, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO dog_insurance (dog_id, anbieter, police_nr, jahresbeitrag, kontakt, ablaufdatum, notizen)
|
||||
VALUES (?,?,?,?,?,?,?)""",
|
||||
(dog_id, data.anbieter, data.police_nr, data.jahresbeitrag, data.kontakt, data.ablaufdatum, data.notizen)
|
||||
)
|
||||
row = conn.execute("SELECT * FROM dog_insurance WHERE id=?", (cur.lastrowid,)).fetchone()
|
||||
return dict(row)
|
||||
|
||||
|
||||
@router.patch("/{dog_id}/insurance/{ins_id}")
|
||||
async def update_insurance(dog_id: int, ins_id: int, data: InsuranceUpdate, user=Depends(get_current_user)):
|
||||
fields = {k: v for k, v in data.model_dump().items() if v is not None}
|
||||
if not fields:
|
||||
raise HTTPException(400, "Keine Änderungen.")
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
set_clause = ", ".join(f"{k}=?" for k in fields)
|
||||
conn.execute(f"UPDATE dog_insurance SET {set_clause} WHERE id=? AND dog_id=?",
|
||||
list(fields.values()) + [ins_id, dog_id])
|
||||
row = conn.execute("SELECT * FROM dog_insurance WHERE id=?", (ins_id,)).fetchone()
|
||||
return dict(row) if row else {}
|
||||
|
||||
|
||||
@router.delete("/{dog_id}/insurance/{ins_id}", status_code=204)
|
||||
async def delete_insurance(dog_id: int, ins_id: int, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
conn.execute("DELETE FROM dog_insurance WHERE id=? AND dog_id=?", (ins_id, dog_id))
|
||||
|
||||
|
||||
# ==================================================================
|
||||
# VERHALTENS-PROTOKOLL
|
||||
# ==================================================================
|
||||
|
||||
BEHAVIOR_KATEGORIEN = {
|
||||
"angst": {"label": "Angst / Panik", "icon": "smiley-nervous"},
|
||||
"aggression": {"label": "Aggression", "icon": "warning"},
|
||||
"ueberreaktion":{"label": "Überreaktion", "icon": "lightning"},
|
||||
"ressource": {"label": "Ressourcenverteidigung", "icon": "lock"},
|
||||
"separation": {"label": "Trennungsangst", "icon": "house"},
|
||||
"leine": {"label": "Leinenprobleme", "icon": "path"},
|
||||
"sozial": {"label": "Sozialkompetenz", "icon": "users"},
|
||||
"sonstiges": {"label": "Sonstiges", "icon": "note"},
|
||||
}
|
||||
|
||||
BEHAVIOR_TRIGGER = [
|
||||
"fremde_hunde", "fremde_menschen", "kinder", "laerm_feuerwerk",
|
||||
"laerm_gewitter", "auto_fahrrad", "tierarzt", "allein_zuhause",
|
||||
"andere_tiere", "besucher_zuhause", "sonstiges"
|
||||
]
|
||||
|
||||
TRIGGER_LABELS = {
|
||||
"fremde_hunde": "Fremde Hunde", "fremde_menschen": "Fremde Menschen",
|
||||
"kinder": "Kinder", "laerm_feuerwerk": "Feuerwerk/Knaller",
|
||||
"laerm_gewitter": "Gewitter", "auto_fahrrad": "Autos/Fahrräder",
|
||||
"tierarzt": "Tierarztbesuch", "allein_zuhause": "Allein zuhause",
|
||||
"andere_tiere": "Andere Tiere", "besucher_zuhause": "Besucher",
|
||||
"sonstiges": "Sonstiges"
|
||||
}
|
||||
|
||||
|
||||
class BehaviorCreate(BaseModel):
|
||||
datum: str
|
||||
uhrzeit: Optional[str] = None
|
||||
kategorie: str
|
||||
intensitaet: int = 3
|
||||
trigger: Optional[str] = None
|
||||
notiz: Optional[str] = None
|
||||
|
||||
|
||||
@router.get("/{dog_id}/behavior")
|
||||
async def get_behavior(dog_id: int, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM behavior_log WHERE dog_id=? ORDER BY datum DESC, uhrzeit DESC LIMIT 100",
|
||||
(dog_id,)
|
||||
).fetchall()
|
||||
return {
|
||||
"entries": [dict(r) for r in rows],
|
||||
"kategorien": BEHAVIOR_KATEGORIEN,
|
||||
"trigger_labels": TRIGGER_LABELS,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{dog_id}/behavior", status_code=201)
|
||||
async def create_behavior(dog_id: int, data: BehaviorCreate, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
if data.kategorie not in BEHAVIOR_KATEGORIEN:
|
||||
raise HTTPException(400, "Unbekannte Kategorie.")
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO behavior_log (dog_id, datum, uhrzeit, kategorie, intensitaet, trigger, notiz)
|
||||
VALUES (?,?,?,?,?,?,?)""",
|
||||
(dog_id, data.datum, data.uhrzeit, data.kategorie,
|
||||
max(1, min(5, data.intensitaet)), data.trigger, data.notiz)
|
||||
)
|
||||
row = conn.execute("SELECT * FROM behavior_log WHERE id=?", (cur.lastrowid,)).fetchone()
|
||||
return dict(row)
|
||||
|
||||
|
||||
@router.delete("/{dog_id}/behavior/{entry_id}", status_code=204)
|
||||
async def delete_behavior(dog_id: int, entry_id: int, user=Depends(get_current_user)):
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
conn.execute("DELETE FROM behavior_log WHERE id=? AND dog_id=?", (entry_id, dog_id))
|
||||
|
||||
|
||||
@router.get("/{dog_id}/reminders")
|
||||
async def get_upcoming_reminders(dog_id: int, user=Depends(get_current_user)):
|
||||
"""Bevorstehende Erinnerungen der nächsten 30 Tage + überfällige."""
|
||||
from datetime import timedelta
|
||||
today = date.today()
|
||||
in30 = today + timedelta(days=30)
|
||||
with db() as conn:
|
||||
dog = conn.execute("SELECT id FROM dogs WHERE id=? AND user_id=?", (dog_id, user["id"])).fetchone()
|
||||
if not dog:
|
||||
raise HTTPException(404)
|
||||
rows = conn.execute(
|
||||
"""SELECT id, typ, bezeichnung, naechstes
|
||||
FROM health
|
||||
WHERE dog_id=? AND naechstes IS NOT NULL
|
||||
AND (erinnerung IS NULL OR erinnerung = 1)
|
||||
AND typ IN ('impfung','entwurmung','medikament')
|
||||
ORDER BY naechstes ASC""",
|
||||
(dog_id,)
|
||||
).fetchall()
|
||||
result = []
|
||||
for r in rows:
|
||||
try:
|
||||
d = date.fromisoformat(r["naechstes"])
|
||||
except Exception:
|
||||
continue
|
||||
delta = (d - today).days
|
||||
if delta < -30 or delta > 30:
|
||||
continue
|
||||
result.append({**dict(r), "delta_tage": delta, "ueberfaellig": delta < 0})
|
||||
return result
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue