Refactor campaign delivery decision paths

This commit is contained in:
2026-07-29 17:06:15 +02:00
parent 5240749ae1
commit dd9592a192
9 changed files with 1103 additions and 593 deletions

View File

@@ -2712,8 +2712,7 @@ def _campaign_jobs_page_response(
cursor: str | None = None,
changed_job_ids: set[str] | None = None,
) -> CampaignJobsResponse:
total_unfiltered = int(session.query(func.count(CampaignJob.id)).filter(*base_filters).scalar() or 0)
total = int(session.query(func.count(CampaignJob.id)).filter(*filtered).scalar() or 0)
total_unfiltered, total = _campaign_jobs_page_counts(session, base_filters=base_filters, filtered=filtered)
pages = (total + page_size - 1) // page_size if total else 0
fingerprint = _campaign_jobs_cursor_fingerprint(
campaign_id=campaign_id,
@@ -2728,47 +2727,26 @@ def _campaign_jobs_page_response(
sort_direction=sort_direction,
)
ordering = _campaign_jobs_ordering(sort_by, sort_direction)
ordered_query = session.query(CampaignJob).filter(*filtered).order_by(*ordering)
start_cursor: str | None = None
effective_offset = 0
if cursor:
if sort_by != "number" or sort_direction != "asc":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Campaign job cursors require number ascending order",
)
try:
cursor_values = decode_keyset_cursor(CAMPAIGN_JOBS_CURSOR_SCOPE, cursor, fingerprint=fingerprint)
if cursor_values is None:
raise KeysetCursorError("Invalid pagination cursor")
page_query = session.query(CampaignJob).filter(*filtered).filter(_campaign_jobs_cursor_condition(cursor_values))
except KeysetCursorError as exc:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
start_cursor = cursor
else:
effective_offset = (page - 1) * page_size
page_query = session.query(CampaignJob).filter(*filtered)
if effective_offset > 0 and sort_by == "number" and sort_direction == "asc":
previous_row = ordered_query.offset(effective_offset - 1).limit(1).first()
if previous_row is not None:
start_cursor = _campaign_jobs_cursor_for_row(previous_row, fingerprint=fingerprint)
rows_plus_one = (
page_query
.order_by(*ordering)
.offset(effective_offset)
.limit(page_size + 1)
.all()
rows_plus_one, start_cursor = _campaign_jobs_page_rows(
session,
filtered=filtered,
ordering=ordering,
page=page,
page_size=page_size,
sort_by=sort_by,
sort_direction=sort_direction,
cursor=cursor,
fingerprint=fingerprint,
)
jobs = rows_plus_one[:page_size]
next_cursor = (
_campaign_jobs_cursor_for_row(jobs[-1], fingerprint=fingerprint)
if changed_job_ids is None
and sort_by == "number"
and sort_direction == "asc"
and len(rows_plus_one) > page_size
and jobs
else None
next_cursor = _campaign_jobs_next_cursor(
jobs,
rows_plus_one=rows_plus_one,
page_size=page_size,
sort_by=sort_by,
sort_direction=sort_direction,
changed_job_ids=changed_job_ids,
fingerprint=fingerprint,
)
if changed_job_ids is not None:
jobs = [job for job in jobs if job.id in changed_job_ids]
@@ -2787,6 +2765,110 @@ def _campaign_jobs_page_response(
)
def _campaign_jobs_page_counts(
session: Session,
*,
base_filters: list[object],
filtered: list[object],
) -> tuple[int, int]:
total_unfiltered = int(session.query(func.count(CampaignJob.id)).filter(*base_filters).scalar() or 0)
total = int(session.query(func.count(CampaignJob.id)).filter(*filtered).scalar() or 0)
return total_unfiltered, total
def _campaign_jobs_page_rows(
session: Session,
*,
filtered: list[object],
ordering: list[object],
page: int,
page_size: int,
sort_by: str,
sort_direction: str,
cursor: str | None,
fingerprint: str,
) -> tuple[list[CampaignJob], str | None]:
if cursor:
page_query = _campaign_jobs_query_after_cursor(
session,
filtered=filtered,
cursor=cursor,
fingerprint=fingerprint,
sort_by=sort_by,
sort_direction=sort_direction,
)
return page_query.order_by(*ordering).limit(page_size + 1).all(), cursor
effective_offset = (page - 1) * page_size
page_query = session.query(CampaignJob).filter(*filtered)
start_cursor = _campaign_jobs_offset_cursor(
page_query,
ordering=ordering,
effective_offset=effective_offset,
sort_by=sort_by,
sort_direction=sort_direction,
fingerprint=fingerprint,
)
rows = page_query.order_by(*ordering).offset(effective_offset).limit(page_size + 1).all()
return rows, start_cursor
def _campaign_jobs_query_after_cursor(
session: Session,
*,
filtered: list[object],
cursor: str,
fingerprint: str,
sort_by: str,
sort_direction: str,
):
if sort_by != "number" or sort_direction != "asc":
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Campaign job cursors require number ascending order",
)
try:
cursor_values = decode_keyset_cursor(CAMPAIGN_JOBS_CURSOR_SCOPE, cursor, fingerprint=fingerprint)
if cursor_values is None:
raise KeysetCursorError("Invalid pagination cursor")
except KeysetCursorError as exc:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
return session.query(CampaignJob).filter(*filtered).filter(_campaign_jobs_cursor_condition(cursor_values))
def _campaign_jobs_offset_cursor(
query,
*,
ordering: list[object],
effective_offset: int,
sort_by: str,
sort_direction: str,
fingerprint: str,
) -> str | None:
if effective_offset <= 0 or sort_by != "number" or sort_direction != "asc":
return None
previous_row = query.order_by(*ordering).offset(effective_offset - 1).limit(1).first()
if previous_row is None:
return None
return _campaign_jobs_cursor_for_row(previous_row, fingerprint=fingerprint)
def _campaign_jobs_next_cursor(
jobs: list[CampaignJob],
*,
rows_plus_one: list[CampaignJob],
page_size: int,
sort_by: str,
sort_direction: str,
changed_job_ids: set[str] | None,
fingerprint: str,
) -> str | None:
cursor_supported = sort_by == "number" and sort_direction == "asc"
if changed_job_ids is not None or not cursor_supported or len(rows_plus_one) <= page_size or not jobs:
return None
return _campaign_jobs_cursor_for_row(jobs[-1], fingerprint=fingerprint)
def _campaign_jobs_cursor_fingerprint(
*,
campaign_id: str,