Fix medication reminders for already-taken meds
- Convert created_at from UTC to user's local timezone before comparing dates - Add scheduled_time check in adaptive reminders (was only checking if any dose was taken today) - Prevents duplicate reminders when user is in a different timezone than UTC
This commit is contained in:
@@ -26,6 +26,17 @@ def _user_now_for(user_uuid):
|
||||
return tz.user_now_for(user_uuid)
|
||||
|
||||
|
||||
def _utc_to_local_date(created_at, user_tz):
|
||||
"""Convert a DB created_at (naive UTC datetime) to a local date string YYYY-MM-DD."""
|
||||
if created_at is None:
|
||||
return ""
|
||||
if isinstance(created_at, datetime):
|
||||
if created_at.tzinfo is None:
|
||||
created_at = created_at.replace(tzinfo=timezone.utc)
|
||||
return created_at.astimezone(user_tz).date().isoformat()
|
||||
return str(created_at)[:10]
|
||||
|
||||
|
||||
def check_medication_reminders():
|
||||
"""Check for medications due now and send notifications."""
|
||||
try:
|
||||
@@ -47,6 +58,7 @@ def check_medication_reminders():
|
||||
current_day = now.strftime("%a").lower()
|
||||
today = now.date()
|
||||
today_str = today.isoformat()
|
||||
user_tz = tz.tz_for_user(user_uuid)
|
||||
|
||||
for med in user_med_list:
|
||||
freq = med.get("frequency", "daily")
|
||||
@@ -83,13 +95,13 @@ def check_medication_reminders():
|
||||
if current_time not in times:
|
||||
continue
|
||||
|
||||
# Already taken today? Check by created_at date
|
||||
# Already taken today? Check by created_at date in user's timezone
|
||||
logs = postgres.select(
|
||||
"med_logs", where={"medication_id": med["id"], "action": "taken"}
|
||||
)
|
||||
already_taken = any(
|
||||
log.get("scheduled_time") == current_time
|
||||
and str(log.get("created_at", ""))[:10] == today_str
|
||||
and _utc_to_local_date(log.get("created_at"), user_tz) == today_str
|
||||
for log in logs
|
||||
)
|
||||
if already_taken:
|
||||
@@ -134,7 +146,9 @@ def check_routine_reminders():
|
||||
if isinstance(start, date_type)
|
||||
else datetime.strptime(str(start), "%Y-%m-%d").date()
|
||||
)
|
||||
if (today - start_d).days < 0 or (today - start_d).days % interval != 0:
|
||||
if (today - start_d).days < 0 or (
|
||||
today - start_d
|
||||
).days % interval != 0:
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
@@ -213,6 +227,7 @@ def check_adaptive_medication_reminders():
|
||||
now = _user_now_for(user_uuid)
|
||||
current_time = now.strftime("%H:%M")
|
||||
today = now.date()
|
||||
user_tz = tz.tz_for_user(user_uuid)
|
||||
|
||||
# Check if adaptive timing is enabled
|
||||
settings = adaptive_meds.get_adaptive_settings(user_uuid)
|
||||
@@ -277,7 +292,7 @@ def check_adaptive_medication_reminders():
|
||||
if check_time != current_time:
|
||||
continue
|
||||
|
||||
# Check if already taken
|
||||
# Check if already taken for this specific time slot today
|
||||
logs = postgres.select(
|
||||
"med_logs",
|
||||
where={
|
||||
@@ -288,7 +303,9 @@ def check_adaptive_medication_reminders():
|
||||
)
|
||||
|
||||
already_taken = any(
|
||||
str(log.get("created_at", ""))[:10] == today.isoformat()
|
||||
log.get("scheduled_time") == check_time
|
||||
and _utc_to_local_date(log.get("created_at"), user_tz)
|
||||
== today.isoformat()
|
||||
for log in logs
|
||||
)
|
||||
|
||||
@@ -352,7 +369,9 @@ def check_nagging():
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not query medication_schedules for {med_id}: {e}")
|
||||
logger.warning(
|
||||
f"Could not query medication_schedules for {med_id}: {e}"
|
||||
)
|
||||
# Table may not exist yet
|
||||
continue
|
||||
|
||||
@@ -360,7 +379,9 @@ def check_nagging():
|
||||
if not schedules:
|
||||
if not _is_med_due_today(med, today):
|
||||
continue
|
||||
logger.info(f"No schedules found for medication {med_id}, attempting to create")
|
||||
logger.info(
|
||||
f"No schedules found for medication {med_id}, attempting to create"
|
||||
)
|
||||
times = med.get("times", [])
|
||||
if times:
|
||||
try:
|
||||
@@ -531,9 +552,7 @@ def _check_per_user_midnight_schedules():
|
||||
continue
|
||||
times = med.get("times", [])
|
||||
if times:
|
||||
adaptive_meds.create_daily_schedule(
|
||||
user_uuid, med["id"], times
|
||||
)
|
||||
adaptive_meds.create_daily_schedule(user_uuid, med["id"], times)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Could not create adaptive schedules for user {user_uuid}: {e}"
|
||||
@@ -543,6 +562,7 @@ def _check_per_user_midnight_schedules():
|
||||
def check_task_reminders():
|
||||
"""Check one-off tasks for advance and at-time reminders."""
|
||||
from datetime import timedelta
|
||||
|
||||
try:
|
||||
tasks = postgres.select("tasks", where={"status": "pending"})
|
||||
if not tasks:
|
||||
@@ -575,15 +595,24 @@ def check_task_reminders():
|
||||
# Advance reminder
|
||||
if reminder_min > 0 and not task.get("advance_notified"):
|
||||
adv_dt = sched_dt - timedelta(minutes=reminder_min)
|
||||
if adv_dt.date() == current_date and adv_dt.strftime("%H:%M") == current_hhmm:
|
||||
if (
|
||||
adv_dt.date() == current_date
|
||||
and adv_dt.strftime("%H:%M") == current_hhmm
|
||||
):
|
||||
if user_settings is None:
|
||||
user_settings = notifications.getNotificationSettings(user_uuid)
|
||||
user_settings = notifications.getNotificationSettings(
|
||||
user_uuid
|
||||
)
|
||||
if user_settings:
|
||||
msg = f"⏰ In {reminder_min} min: {task['title']}"
|
||||
if task.get("description"):
|
||||
msg += f" — {task['description']}"
|
||||
notifications._sendToEnabledChannels(user_settings, msg, user_uuid=user_uuid)
|
||||
postgres.update("tasks", {"advance_notified": True}, {"id": task["id"]})
|
||||
notifications._sendToEnabledChannels(
|
||||
user_settings, msg, user_uuid=user_uuid
|
||||
)
|
||||
postgres.update(
|
||||
"tasks", {"advance_notified": True}, {"id": task["id"]}
|
||||
)
|
||||
|
||||
# At-time reminder
|
||||
if sched_date == current_date and sched_hhmm == current_hhmm:
|
||||
@@ -593,10 +622,15 @@ def check_task_reminders():
|
||||
msg = f"📋 Now: {task['title']}"
|
||||
if task.get("description"):
|
||||
msg += f"\n{task['description']}"
|
||||
notifications._sendToEnabledChannels(user_settings, msg, user_uuid=user_uuid)
|
||||
notifications._sendToEnabledChannels(
|
||||
user_settings, msg, user_uuid=user_uuid
|
||||
)
|
||||
postgres.update(
|
||||
"tasks",
|
||||
{"status": "notified", "updated_at": datetime.utcnow().isoformat()},
|
||||
{
|
||||
"status": "notified",
|
||||
"updated_at": datetime.utcnow().isoformat(),
|
||||
},
|
||||
{"id": task["id"]},
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user