Medisync / utils /reminders.py
anique-1's picture
Deploy MediSync AI Backend with APK to Hugging Face
903c699
Raw
History Blame Contribute Delete
9.26 kB
import asyncio
from datetime import datetime, timedelta
from db import db
from utils.sms_utils import send_medicine_reminder_sms
from utils.email_utils import send_medicine_reminder_email
REMINDER_INTERVAL_SECONDS = 60 # Check every minute
REMINDER_WINDOW_MINUTES = 5 # Remind if medicine time is within next 5 minutes
# Global set to track sent reminders for today, reset daily
sent_reminders_today = set()
async def run_reminder_check():
print("run_reminder_check started")
try:
from zoneinfo import ZoneInfo
tz = ZoneInfo("Asia/Karachi")
except ImportError:
import pytz
tz = pytz.timezone("Asia/Karachi")
now = datetime.now(tz)
today_str = now.strftime("%Y-%m-%d")
print(f"[reminder_worker] Running at {now.isoformat()} (today: {today_str})")
# Reset sent reminders if it's a new day
if not hasattr(run_reminder_check, 'last_reset_day') or run_reminder_check.last_reset_day != today_str:
print("[reminder_worker] New day detected, resetting sent_reminders_today.")
sent_reminders_today.clear()
run_reminder_check.last_reset_day = today_str
users = await db.users.find({}).to_list(length=1000)
print(f"[reminder_worker] Found {len(users)} users.")
for user in users:
medicines = await db.medicines.find({"user_id": str(user["_id"]), "status": "active"}).to_list(length=6)
print(f"[reminder_worker] User {user.get('email', user.get('_id'))} has {len(medicines)} active medicines.")
for med in medicines:
history = med.get("history", [])
taken_times = set()
sent_times = set()
for h in history:
if h.get("status") == "taken":
taken_times.add(h.get("time"))
elif h.get("status") == "reminder_sent":
sent_times.add(h.get("time"))
for t in med.get("times", []):
med_time_str = f"{today_str} {t}"
med_dt = None
parse_attempts = [
("%Y-%m-%d %I:%M %p", "12-hour"),
("%Y-%m-%d %H:%M", "24-hour"),
]
for fmt, label in parse_attempts:
try:
med_dt = datetime.strptime(med_time_str, fmt)
med_dt = med_dt.replace(tzinfo=tz)
break
except Exception:
continue
if not med_dt:
print(f"[reminder_worker] Failed to parse med_time_str '{med_time_str}' with both 12-hour and 24-hour formats.")
continue
delta_seconds = (med_dt - now).total_seconds()
print(f"[reminder_worker] Checking medicine '{med.get('name', '')}' for user {user.get('email', user.get('_id'))}: scheduled at {med_dt.isoformat()}, now {now.isoformat()}, delta_seconds={delta_seconds}")
# Send reminder 5 minutes before to 2 minutes after scheduled time
if -300 <= delta_seconds <= 120:
iso_time = med_dt.isoformat()
# Only send reminder if not already taken and not already sent for this specific time
if iso_time not in taken_times and iso_time not in sent_times:
print(f"[reminder_worker] Attempting to send reminder for {med.get('name', '')} at {t} to {user.get('phone_number', '')} and {user.get('email', '')}")
sms_sent = False
email_sent = False
try:
send_medicine_reminder_sms(
user["phone_number"],
med["name"],
med.get("dosage", ""),
t
)
print("[reminder_worker] SMS reminder sent successfully.")
sms_sent = True
except Exception as e:
print(f"[reminder_worker] SMS reminder failed: {e}")
try:
await send_medicine_reminder_email(
user["email"],
user["full_name"],
med["name"],
med.get("dosage", ""),
t
)
print("[reminder_worker] Email reminder sent successfully.")
email_sent = True
except Exception as e:
print(f"[reminder_worker] Email reminder failed: {e}")
if sms_sent or email_sent:
# Update history to mark as reminder_sent
history.append({"time": iso_time, "status": "reminder_sent", "sent_at": now.isoformat()})
await db.medicines.update_one({"_id": med["_id"]}, {"$set": {"history": history}})
else:
print(f"[reminder_worker] Reminder already sent or dose already taken for {med.get('name', '')} at {t}.")
else:
print(f"[reminder_worker] Not in reminder window for {med.get('name', '')} at {t}.")
async def reminder_worker():
print("reminder_worker started")
while True:
await run_reminder_check()
await asyncio.sleep(REMINDER_INTERVAL_SECONDS)
async def run_missed_doses_check():
from bson import ObjectId
print("run_missed_doses_check started")
try:
from zoneinfo import ZoneInfo
tz = ZoneInfo("Asia/Karachi")
except ImportError:
import pytz
tz = pytz.timezone("Asia/Karachi")
now = datetime.now(tz)
users = await db.users.find({}).to_list(length=1000)
for user in users:
medicines = await db.medicines.find({"user_id": str(user["_id"]), "status": "active"}).to_list(length=100)
for med in medicines:
history = med.get("history", [])
taken_times = set()
absent_times = set()
for h in history:
if h.get("status") == "taken":
taken_times.add(h.get("time"))
elif h.get("status") == "absent":
absent_times.add(h.get("time"))
for t in med.get("times", []):
today_str = now.strftime("%Y-%m-%d")
med_time_str = f"{today_str} {t}"
try:
med_time = datetime.strptime(med_time_str, "%Y-%m-%d %I:%M %p")
med_time = med_time.replace(tzinfo=tz)
except Exception:
continue
# If more than 15 min have passed and not marked as taken or absent
if now > med_time + timedelta(minutes=15):
iso_time = med_time.isoformat()
if iso_time not in taken_times and iso_time not in absent_times:
history.append({"time": iso_time, "status": "absent"})
await db.medicines.update_one({"_id": med["_id"]}, {"$set": {"history": history}})
# Send missed notification only once
try:
from utils.sms_utils import send_medicine_missed_sms
print(f"Sending SMS missed-dose notification to {user.get('phone_number', '')} for {med.get('name', '')} at {t}")
send_medicine_missed_sms(
user.get("phone_number", ""),
user.get("full_name", ""),
med.get("name", ""),
med.get("dosage", ""),
t
)
print("SMS missed-dose notification sent successfully.")
except Exception as e:
print(f"SMS missed-dose notification failed: {e}")
try:
from utils.email_utils import send_medicine_missed_email
print(f"Sending EMAIL missed-dose notification to {user.get('email', '')} for {med.get('name', '')} at {t}")
await send_medicine_missed_email(
user.get("email", ""),
user.get("full_name", ""),
med.get("name", ""),
med.get("dosage", ""),
t
)
print("Email missed-dose notification sent successfully.")
except Exception as e:
print(f"Email missed-dose notification failed: {e}")
async def mark_missed_doses_worker():
print("mark_missed_doses_worker started")
while True:
await run_missed_doses_check()
await asyncio.sleep(60)