"""Background task: auto-mark overdue pending reservations as no_show.""" import asyncio import logging from datetime import datetime, timezone, timedelta logger = logging.getLogger(__name__) # Grace period after reserved_for before a pending reservation becomes no_show NO_SHOW_GRACE_MINUTES = 30 # How often the checker runs CHECK_INTERVAL_SECONDS = 300 # 5 minutes async def _run_no_show_checker(): # Import here to avoid circular imports at module load time from database import SessionLocal from models.reservation import Reservation while True: await asyncio.sleep(CHECK_INTERVAL_SECONDS) try: db = SessionLocal() try: cutoff = datetime.now(timezone.utc) - timedelta(minutes=NO_SHOW_GRACE_MINUTES) overdue = ( db.query(Reservation) .filter( Reservation.status == "pending", Reservation.reserved_for < cutoff, ) .all() ) if overdue: for r in overdue: r.status = "no_show" r.updated_at = datetime.now(timezone.utc) db.commit() logger.info("no_show_checker: marked %d reservation(s) as no_show", len(overdue)) finally: db.close() except Exception: logger.exception("no_show_checker: unexpected error") async def start_reservation_tasks() -> asyncio.Task: task = asyncio.create_task(_run_no_show_checker()) return task