flip_stale_sessions flips L1WalkSession.status from 'active' to 'abandoned' for rows where last_step_at is older than 24h. Preserves the row for audit; removes it from the L1 dashboard's 'Resume in progress' widget. Runs hourly via APScheduler with max_instances=1 (Lesson 1). Uses the admin session factory (no RLS context at startup). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
50 lines
1.6 KiB
Python
50 lines
1.6 KiB
Python
"""Hourly cleanup job: flip stale active L1WalkSessions to 'abandoned'.
|
|
|
|
Sessions with status='active' and last_step_at older than 24h are considered
|
|
abandoned (L1 closed the browser, customer hung up, etc.). Flipping them
|
|
removes them from the "Resume in progress" widget while preserving the row
|
|
for audit/reporting.
|
|
|
|
Run via APScheduler interval job, max_instances=1 (Lesson 1).
|
|
"""
|
|
import logging
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from sqlalchemy import update
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.models.l1_walk_session import L1WalkSession
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def flip_stale_sessions(db: AsyncSession) -> int:
|
|
"""Flip active sessions to 'abandoned' if last_step_at < now - 24h.
|
|
|
|
Returns the number of sessions flipped.
|
|
"""
|
|
cutoff = datetime.now(timezone.utc) - timedelta(hours=24)
|
|
stmt = (
|
|
update(L1WalkSession)
|
|
.where(L1WalkSession.status == "active")
|
|
.where(L1WalkSession.last_step_at < cutoff)
|
|
.values(status="abandoned")
|
|
)
|
|
result = await db.execute(stmt)
|
|
await db.commit()
|
|
return result.rowcount or 0
|
|
|
|
|
|
async def run_cleanup_job(session_factory) -> None:
|
|
"""APScheduler entry point. Uses the admin session factory (no RLS context)."""
|
|
async with session_factory() as db:
|
|
try:
|
|
count = await flip_stale_sessions(db)
|
|
if count > 0:
|
|
logger.info(
|
|
"l1_session_cleanup: flipped %d sessions to abandoned", count
|
|
)
|
|
except Exception:
|
|
logger.exception("l1_session_cleanup: error during run")
|