Fix issues #6, #7, #11, #12, #13: med reminders, push notifications, auth persistence, scheduling conflicts
- Fix TIME object vs string comparison in scheduler preventing adaptive med reminders from ever firing (#12, #6) - Add frequency filtering to midnight schedule creation for every_n_days meds - Require start_date and interval_days for every_n_days medications - Add refresh token support (30-day) to API and bot for persistent sessions (#13) - Add "trusted device" checkbox to frontend login for long-lived sessions (#7) - Auto-refresh expired tokens in both bot (apiRequest) and frontend (api.ts) - Restore bot sessions from cache on restart using refresh tokens - Duration-aware routine scheduling conflict detection (#11) - Add conflict check when starting routine sessions against medication times - Add diagnostic logging to notification delivery channels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,9 +5,9 @@ Override poll_callback() with your domain-specific logic.
|
||||
"""
|
||||
|
||||
import os
|
||||
import time
|
||||
import time as time_module
|
||||
import logging
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from datetime import datetime, timezone, timedelta, time as time_type
|
||||
|
||||
import core.postgres as postgres
|
||||
import core.notifications as notifications
|
||||
@@ -249,6 +249,12 @@ def check_adaptive_medication_reminders():
|
||||
# Use base time
|
||||
check_time = sched.get("base_time")
|
||||
|
||||
# Normalize TIME objects to "HH:MM" strings for comparison
|
||||
if isinstance(check_time, time_type):
|
||||
check_time = check_time.strftime("%H:%M")
|
||||
elif check_time is not None:
|
||||
check_time = str(check_time)[:5]
|
||||
|
||||
if check_time != current_time:
|
||||
continue
|
||||
|
||||
@@ -367,6 +373,11 @@ def check_nagging():
|
||||
display_time = sched.get("adjusted_time")
|
||||
else:
|
||||
display_time = sched.get("base_time")
|
||||
# Normalize TIME objects for display
|
||||
if isinstance(display_time, time_type):
|
||||
display_time = display_time.strftime("%H:%M")
|
||||
elif display_time is not None:
|
||||
display_time = str(display_time)[:5]
|
||||
|
||||
# Send nag notification
|
||||
user_settings = notifications.getNotificationSettings(user_uuid)
|
||||
@@ -446,6 +457,39 @@ def _get_distinct_user_uuids():
|
||||
return uuids
|
||||
|
||||
|
||||
def _is_med_due_today(med, today):
|
||||
"""Check if a medication is due on the given date based on its frequency."""
|
||||
from datetime import date as date_type
|
||||
|
||||
freq = med.get("frequency", "daily")
|
||||
|
||||
if freq == "as_needed":
|
||||
return False
|
||||
|
||||
if freq == "specific_days":
|
||||
current_day = today.strftime("%a").lower()
|
||||
med_days = med.get("days_of_week", [])
|
||||
if current_day not in med_days:
|
||||
return False
|
||||
|
||||
if freq == "every_n_days":
|
||||
start = med.get("start_date")
|
||||
interval = med.get("interval_days")
|
||||
if start and interval:
|
||||
start_d = (
|
||||
start
|
||||
if isinstance(start, date_type)
|
||||
else datetime.strptime(str(start), "%Y-%m-%d").date()
|
||||
)
|
||||
days_since = (today - start_d).days
|
||||
if days_since < 0 or days_since % interval != 0:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _check_per_user_midnight_schedules():
|
||||
"""Create daily adaptive schedules for each user when it's midnight in
|
||||
their timezone (within the poll window)."""
|
||||
@@ -453,10 +497,13 @@ def _check_per_user_midnight_schedules():
|
||||
try:
|
||||
now = _user_now_for(user_uuid)
|
||||
if now.hour == 0 and now.minute < POLL_INTERVAL / 60:
|
||||
today = now.date()
|
||||
user_meds = postgres.select(
|
||||
"medications", where={"user_uuid": user_uuid, "active": True}
|
||||
)
|
||||
for med in user_meds:
|
||||
if not _is_med_due_today(med, today):
|
||||
continue
|
||||
times = med.get("times", [])
|
||||
if times:
|
||||
adaptive_meds.create_daily_schedule(
|
||||
@@ -499,7 +546,7 @@ def daemon_loop():
|
||||
poll_callback()
|
||||
except Exception as e:
|
||||
logger.error(f"Poll callback error: {e}")
|
||||
time.sleep(POLL_INTERVAL)
|
||||
time_module.sleep(POLL_INTERVAL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user