2:start/pause/resume/cancel, SSE live progress stream, per-recipient monitor, CSV export.""" 69:def _ser(c: Campaign) -> dict: 102:async def _get_own(db: AsyncSession, user: User, cid: int) -> Campaign: 109:async def _validate_refs(db: AsyncSession, user: User, data: CampaignIn): 187:async def list_campaigns(status: str = Query(""), 206:async def create_campaign(payload: CampaignIn, db: AsyncSession = Depends(get_db), 222:async def get_campaign(cid: int, user: User = Depends(get_current_user), 228:async def patch_campaign(cid: int, payload: CampaignIn, 250:async def delete_campaign(cid: int, db: AsyncSession = Depends(get_db), 263:async def clear_all_campaigns(db: AsyncSession = Depends(get_db), 286:async def preview_campaign(cid: int, sample_size: int = Query(0), 375:async def start(cid: int, db: AsyncSession = Depends(get_db), 407:async def pause(cid: int, db: AsyncSession = Depends(get_db), 419:async def resume(cid: int, db: AsyncSession = Depends(get_db), 433:async def cancel(cid: int, db: AsyncSession = Depends(get_db), 445:# Monitor: progress, SSE stream, recipient table 448:async def _progress_payload(db: AsyncSession, c: Campaign) -> dict: 475:async def get_progress(cid: int, user: User = Depends(get_current_user), 481:@router.get("/{cid}/stream") 482:async def campaign_stream(cid: int, request: Request, 485: """SSE live progress stream. Manual implementation (no sse-starlette): a Redis 493: async def event_gen(): 540: return StreamingResponse(event_gen(), media_type="text/event-stream", 547:async def campaign_recipients(cid: int, status: str = Query(""), 564: async def names(): 597:async def export_campaign(cid: int, db: AsyncSession = Depends(get_db), 605: def generate(): 635:async def wizard_options(db: AsyncSession = Depends(get_db), 652: async def valid_count(list_id: int) -> int: 679:async def validate_step(payload: dict, db: AsyncSession = Depends(get_db),