diff --git a/backend/routes/webcal.py b/backend/routes/webcal.py index 7815f77..087bba3 100644 --- a/backend/routes/webcal.py +++ b/backend/routes/webcal.py @@ -152,7 +152,7 @@ async def ical_feed(token: str): vevents = [] - # ── 1. Health-Erinnerungen ───────────────────────────────── + # ── 1. Health-Erinnerungen (Nächster Termin) ────────────── if dog_ids: ph = ','.join('?' * len(dog_ids)) health_rows = conn.execute( @@ -181,7 +181,6 @@ async def ical_feed(token: str): if e["intervall_tage"]: rrule = f"FREQ=DAILY;INTERVAL={e['intervall_tage']}" - # next-day als DTEND (All-Day-Event) dtend_date = (date.fromisoformat(e["naechstes"]) + timedelta(days=1)).isoformat() vevents.append(_vevent( uid = _uid('health', e["id"]), @@ -192,6 +191,98 @@ async def ical_feed(token: str): rrule = rrule, )) + # ── 1b. Tierarzt-Termine ─────────────────────────────────── + if dog_ids: + ph = ','.join('?' * len(dog_ids)) + tierarzt_rows = conn.execute( + f"""SELECT h.id, h.bezeichnung, h.datum, h.notiz, + h.tierarzt_name, h.dog_id + FROM health h + WHERE h.dog_id IN ({ph}) + AND h.typ = 'tierarzt' + AND h.datum >= date('now') + ORDER BY h.datum""", + dog_ids + ).fetchall() + + for e in tierarzt_rows: + dog_name = dog_map.get(e["dog_id"], "Hund") + desc_parts = [] + if e["tierarzt_name"]: + desc_parts.append(f"Praxis: {e['tierarzt_name']}") + if e["notiz"]: + desc_parts.append(e["notiz"]) + dtend_date = (date.fromisoformat(e["datum"]) + timedelta(days=1)).isoformat() + vevents.append(_vevent( + uid = _uid('tierarzt', e["id"]), + dtstart = f";VALUE=DATE:{_date_str(e['datum'])}", + dtend = f";VALUE=DATE:{_date_str(dtend_date)}", + summary = f"Tierarzt: {e['bezeichnung']} ({dog_name})", + description = '\n'.join(desc_parts), + )) + + # ── 1c. Medikamente mit Enddatum ────────────────────────── + if dog_ids: + ph = ','.join('?' * len(dog_ids)) + medi_rows = conn.execute( + f"""SELECT h.id, h.bezeichnung, h.bis_datum, h.notiz, + h.dosierung, h.dog_id + FROM health h + WHERE h.dog_id IN ({ph}) + AND h.typ = 'medikament' + AND h.bis_datum IS NOT NULL + AND h.bis_datum >= date('now') + ORDER BY h.bis_datum""", + dog_ids + ).fetchall() + + for e in medi_rows: + dog_name = dog_map.get(e["dog_id"], "Hund") + desc_parts = [] + if e["dosierung"]: + desc_parts.append(f"Dosierung: {e['dosierung']}") + if e["notiz"]: + desc_parts.append(e["notiz"]) + dtend_date = (date.fromisoformat(e["bis_datum"]) + timedelta(days=1)).isoformat() + vevents.append(_vevent( + uid = _uid('medi-end', e["id"]), + dtstart = f";VALUE=DATE:{_date_str(e['bis_datum'])}", + dtend = f";VALUE=DATE:{_date_str(dtend_date)}", + summary = f"Medikament Ende: {e['bezeichnung']} ({dog_name})", + description = '\n'.join(desc_parts), + )) + + # ── 1d. Hunde-Geburtstage (jährlich wiederkehrend) ──────── + if dog_ids: + ph = ','.join('?' * len(dog_ids)) + bday_rows = conn.execute( + f"""SELECT id, name, geburtstag + FROM dogs + WHERE id IN ({ph}) + AND geburtstag IS NOT NULL""", + dog_ids + ).fetchall() + + for d in bday_rows: + try: + bday = date.fromisoformat(d["geburtstag"]) + except ValueError: + continue + # Nächsten Geburtstag im laufenden oder nächsten Jahr + today = date.today() + next_bday = bday.replace(year=today.year) + if next_bday < today: + next_bday = bday.replace(year=today.year + 1) + dtend_date = (next_bday + timedelta(days=1)).isoformat() + age = next_bday.year - bday.year + vevents.append(_vevent( + uid = _uid('bday', d["id"]), + dtstart = f";VALUE=DATE:{_date_str(next_bday.isoformat())}", + dtend = f";VALUE=DATE:{_date_str(dtend_date)}", + summary = f"Geburtstag: {d['name']} ({age}. {'Geburtstag' if age == 1 else 'Geburtstag'})", + rrule = f"FREQ=YEARLY", + )) + # ── 2. Events (zukünftige) ───────────────────────────────── event_rows = conn.execute( """SELECT id, titel, datum, uhrzeit, ort_name, beschreibung