""" daemon.py - Background polling loop for scheduled tasks Override poll_callback() with your domain-specific logic. """ import os import time import logging from datetime import datetime import core.postgres as postgres import core.notifications as notifications logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", 60)) def check_medication_reminders(): """Check for medications due now and send notifications.""" try: meds = postgres.select("medications", where={"active": True}) now = datetime.now() current_time = now.strftime("%H:%M") today = now.strftime("%Y-%m-%d") for med in meds: times = med.get("times", []) if current_time not in times: continue logs = postgres.select( "med_logs", where={"medication_id": med["id"]}, ) already_taken = any( log.get("action") == "taken" and log.get("scheduled_time", "").startswith(today) for log in logs ) if already_taken: continue user_settings = notifications.getNotificationSettings(med["user_uuid"]) if user_settings: msg = f"Time to take {med['name']} ({med['dosage']} {med['unit']})" notifications._sendToEnabledChannels(user_settings, msg) except Exception as e: logger.error(f"Error checking medication reminders: {e}") def check_routine_reminders(): """Check for scheduled routines due now and send notifications.""" try: now = datetime.now() current_time = now.strftime("%H:%M") current_day = now.strftime("%a").lower() schedules = postgres.select("routine_schedules", where={"remind": True}) for schedule in schedules: if current_time != schedule.get("time"): continue days = schedule.get("days", []) if current_day not in days: continue routine = postgres.select_one("routines", {"id": schedule["routine_id"]}) if not routine: continue user_settings = notifications.getNotificationSettings(routine["user_uuid"]) if user_settings: msg = f"Time to start your routine: {routine['name']}" notifications._sendToEnabledChannels(user_settings, msg) except Exception as e: logger.error(f"Error checking routine reminders: {e}") def check_refills(): """Check for medications running low on refills.""" try: meds = postgres.select("medications") for med in meds: qty = med.get("quantity_remaining") if qty is not None and qty <= 7: user_settings = notifications.getNotificationSettings(med["user_uuid"]) if user_settings: msg = f"Low on {med['name']}: only {qty} doses remaining. Time to refill!" notifications._sendToEnabledChannels(user_settings, msg) except Exception as e: logger.error(f"Error checking refills: {e}") def poll_callback(): """Called every POLL_INTERVAL seconds.""" check_medication_reminders() check_routine_reminders() check_refills() def daemon_loop(): logger.info("Scheduler daemon starting") while True: try: poll_callback() except Exception as e: logger.error(f"Poll callback error: {e}") time.sleep(POLL_INTERVAL) if __name__ == "__main__": daemon_loop()