import logging from datetime import datetime, timedelta from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.cron import CronTrigger from sqlalchemy.orm import Session from app.db.session import SessionLocal from app.models.task import Task, TaskStatus from app.models.user import User from app.services.whatsapp_service import send_task_reminder, send_task_overdue logger = logging.getLogger(__name__) class SchedulerService: def __init__(self): self.scheduler = BackgroundScheduler() self.scheduler.start() self._setup_jobs() def _setup_jobs(self): self.scheduler.add_job( func=self.send_reminders, trigger=CronTrigger(hour=9, minute=0), # Daily at 9 AM id='daily_reminders', name='Send daily task reminders', replace_existing=True ) self.scheduler.add_job( func=self.check_overdue_tasks, trigger=CronTrigger(hour=18, minute=0), # Daily at 6 PM id='overdue_check', name='Check for overdue tasks', replace_existing=True ) def send_reminders(self): logger.info("Starting daily reminder job") db: Session = SessionLocal() try: # Get tasks scheduled for today that haven't been reminded today = datetime.utcnow().date() tomorrow = today + timedelta(days=1) tasks = db.query(Task).join(User).filter( Task.status.in_([TaskStatus.PENDING, TaskStatus.IN_PROGRESS]), Task.scheduled_at >= datetime.combine(today, datetime.min.time()), Task.scheduled_at < datetime.combine(tomorrow, datetime.min.time()), Task.whatsapp_reminder_sent == False, User.whatsapp_number.isnot(None) ).all() sent_count = 0 for task in tasks: if send_task_reminder(task.owner.whatsapp_number, task): task.whatsapp_reminder_sent = True sent_count += 1 db.commit() logger.info(f"Sent {sent_count} reminders") except Exception as e: logger.error(f"Error in reminder job: {str(e)}") db.rollback() finally: db.close() def check_overdue_tasks(self): logger.info("Starting overdue task check") db: Session = SessionLocal() try: now = datetime.utcnow() overdue_tasks = db.query(Task).join(User).filter( Task.status.in_([TaskStatus.PENDING, TaskStatus.IN_PROGRESS]), Task.due_date < now, User.whatsapp_number.isnot(None) ).all() sent_count = 0 for task in overdue_tasks: if send_task_overdue(task.owner.whatsapp_number, task): sent_count += 1 logger.info(f"Sent {sent_count} overdue notifications") except Exception as e: logger.error(f"Error in overdue check job: {str(e)}") finally: db.close() def schedule_task_reminder(self, task_id: int, remind_at: datetime): job_id = f"task_reminder_{task_id}" self.scheduler.add_job( func=self._send_individual_reminder, trigger='date', run_date=remind_at, args=[task_id], id=job_id, name=f'Reminder for task {task_id}', replace_existing=True ) def _send_individual_reminder(self, task_id: int): db: Session = SessionLocal() try: task = db.query(Task).join(User).filter( Task.id == task_id, User.whatsapp_number.isnot(None) ).first() if task and task.status in [TaskStatus.PENDING, TaskStatus.IN_PROGRESS]: send_task_reminder(task.owner.whatsapp_number, task) task.whatsapp_reminder_sent = True db.commit() except Exception as e: logger.error(f"Error sending individual reminder for task {task_id}: {str(e)}") finally: db.close() def shutdown(self): self.scheduler.shutdown() scheduler_service = SchedulerService()