| 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 |
| REMINDER_WINDOW_MINUTES = 5 |
|
|
| |
| 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})") |
| |
| |
| 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}") |
| |
| if -300 <= delta_seconds <= 120: |
| iso_time = med_dt.isoformat() |
| |
| 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: |
| |
| 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 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}}) |
| |
| 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) |
|
|
|
|