banyaro/backend/scheduler.py
rene 5518064be3 Feat: APScheduler — täglich Push für Health-Erinnerungen
- apscheduler==3.10.4 in requirements.txt
- scheduler.py: AsyncIOScheduler, täglich 08:00 Uhr (Europe/Berlin)
- Job prüft naechstes IN (heute, in 7 Tagen, gestern):
  heute → "Heute fällig", 7 Tage → Vorwarnung, gestern → Überfällig
- Nur Impfung, Entwurmung, Medikament
- misfire_grace_time=3600 (robust nach Container-Neustart)
- Scheduler start/stop im FastAPI lifespan
2026-04-13 20:49:49 +02:00

89 lines
3.1 KiB
Python

"""
BAN YARO — Hintergrund-Scheduler
Täglich: Gesundheits-Erinnerungen per Push versenden.
"""
import logging
from datetime import date, timedelta
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.cron import CronTrigger
from database import db
from routes.push import send_push_to_user
logger = logging.getLogger(__name__)
_scheduler = AsyncIOScheduler(timezone="Europe/Berlin")
def start():
_scheduler.add_job(
_job_health_reminders,
CronTrigger(hour=8, minute=0), # täglich 08:00 Uhr
id="health_reminders",
replace_existing=True,
misfire_grace_time=3600, # bis zu 1h Verzug ok (z.B. nach Neustart)
)
_scheduler.start()
logger.info("Scheduler gestartet — Health-Reminder täglich 08:00 Uhr.")
def stop():
_scheduler.shutdown(wait=False)
logger.info("Scheduler gestoppt.")
# ------------------------------------------------------------------
# JOB: Gesundheits-Erinnerungen
# ------------------------------------------------------------------
async def _job_health_reminders():
"""
Findet alle Health-Einträge mit `naechstes`-Datum:
- genau heute → sofortige Erinnerung
- in 7 Tagen → Vorwarnung
- gestern → Überfällig-Erinnerung (nur einmal, 1 Tag nach Fälligkeit)
Schickt jeweils eine Push-Notification an den Hundebesitzer.
"""
today = date.today()
in7 = today + timedelta(days=7)
yesterday = today - timedelta(days=1)
logger.info(f"Health-Reminder Job läuft für {today}")
with db() as conn:
# Alle fälligen Einträge der nächsten 7 Tage + gestrige (überfällig)
rows = conn.execute("""
SELECT h.id, h.typ, h.bezeichnung, h.naechstes,
d.user_id, d.name AS hund_name
FROM health h
JOIN dogs d ON d.id = h.dog_id
WHERE h.naechstes IN (?, ?, ?)
AND h.typ IN ('impfung', 'entwurmung', 'medikament')
""", (str(today), str(in7), str(yesterday))).fetchall()
sent_total = 0
for r in rows:
naechstes = date.fromisoformat(r["naechstes"])
delta = (naechstes - today).days
if delta == 7:
title = f"⏰ Erinnerung: {r['bezeichnung']}"
body = f"In 7 Tagen fällig für {r['hund_name']}."
elif delta == 0:
title = f"📅 Heute fällig: {r['bezeichnung']}"
body = f"Bitte heute erledigen — {r['hund_name']} wartet."
else: # delta == -1 → gestern überfällig
title = f"⚠️ Überfällig: {r['bezeichnung']}"
body = f"War gestern fällig für {r['hund_name']} — bitte bald erledigen."
sent = send_push_to_user(r["user_id"], {
"type": "health_reminder",
"title": title,
"body": body,
"data": {"page": "health"},
})
sent_total += sent
if sent:
logger.info(f"Reminder Push: user={r['user_id']} entry={r['id']} delta={delta}d")
logger.info(f"Health-Reminder Job fertig — {len(rows)} Einträge, {sent_total} Push gesendet.")