refactor: simplify poll transition orchestration

This commit is contained in:
2026-07-21 12:01:24 +02:00
parent 83d04d2f33
commit f839545605

View File

@@ -423,6 +423,93 @@ def _transition_for_idempotency_key(
) )
def _transition_decision_option(
poll: Poll,
*,
action: str,
option_id: str | None,
option_key: str | None,
) -> PollOption | None:
if action == "decide":
return _option_by_id_or_key(poll, option_id=option_id, option_key=option_key)
if option_id is not None or option_key is not None:
raise PollError("Only a decide transition accepts a poll option")
return None
def _replayed_transition(
poll: Poll,
*,
existing: PollLifecycleTransition | None,
action: str,
decision_option: PollOption | None,
) -> PollTransitionResult | None:
if existing is None:
return None
requested_option_id = decision_option.id if decision_option is not None else None
if existing.action != action or existing.decision_option_id != requested_option_id:
raise PollError("Idempotency key was already used for a different poll transition")
return PollTransitionResult(poll=poll, transition=existing, replayed=True)
def _clear_expired_close_for_open(poll: Poll, *, action: str, now: datetime) -> datetime | None:
if action != "open" or poll.closes_at is None:
return None
normalized_closes_at = response_datetime(poll.closes_at)
if normalized_closes_at is None or normalized_closes_at > now:
return None
poll.closes_at = None
return normalized_closes_at
def _apply_transition_state(
poll: Poll,
*,
action: str,
from_status: str,
to_status: str,
decision_option: PollOption | None,
now: datetime,
) -> None:
if action == "archive":
poll.archived_from_status = from_status
elif action == "unarchive":
poll.archived_from_status = None
elif action == "close":
poll.closed_at = now
elif action == "decide":
if decision_option is None:
raise PollError("A decide transition requires a poll option")
poll.decided_option_id = decision_option.id
poll.decided_at = now
if poll.closed_at is None:
poll.closed_at = now
poll.status = to_status
def _transition_audit_metadata(
*,
action: str,
from_status: str,
to_status: str,
used_legacy_unarchive_fallback: bool,
cleared_expired_closes_at: datetime | None,
) -> dict[str, Any]:
metadata: dict[str, Any] = {}
if action == "archive":
metadata["archived_from_status"] = from_status
elif action == "unarchive":
metadata["restored_status"] = to_status
if used_legacy_unarchive_fallback:
metadata["legacy_restore_fallback"] = {
"reason": "missing_archived_from_status",
"restored_status": to_status,
}
if cleared_expired_closes_at is not None:
metadata["cleared_expired_closes_at"] = cleared_expired_closes_at.isoformat()
return metadata
def transition_poll( def transition_poll(
session: Session, session: Session,
*, *,
@@ -440,22 +527,26 @@ def transition_poll(
poll = _lock_poll_for_transition(session, tenant_id=tenant_id, poll_id=poll_id) poll = _lock_poll_for_transition(session, tenant_id=tenant_id, poll_id=poll_id)
normalized_key = _normalize_idempotency_key(idempotency_key) normalized_key = _normalize_idempotency_key(idempotency_key)
decision_option = None decision_option = _transition_decision_option(
if action == "decide": poll,
decision_option = _option_by_id_or_key(poll, option_id=option_id, option_key=option_key) action=action,
elif option_id is not None or option_key is not None: option_id=option_id,
raise PollError("Only a decide transition accepts a poll option") option_key=option_key,
)
existing = _transition_for_idempotency_key( existing = _transition_for_idempotency_key(
session, session,
poll_id=poll.id, poll_id=poll.id,
idempotency_key=normalized_key, idempotency_key=normalized_key,
) )
if existing is not None: replayed = _replayed_transition(
requested_option_id = decision_option.id if decision_option is not None else None poll,
if existing.action != action or existing.decision_option_id != requested_option_id: existing=existing,
raise PollError("Idempotency key was already used for a different poll transition") action=action,
return PollTransitionResult(poll=poll, transition=existing, replayed=True) decision_option=decision_option,
)
if replayed is not None:
return replayed
try: try:
plan = transition_engine.plan( plan = transition_engine.plan(
@@ -469,40 +560,22 @@ def transition_poll(
previous_decision_option_id = poll.decided_option_id previous_decision_option_id = poll.decided_option_id
now = _now() now = _now()
used_legacy_unarchive_fallback = action == "unarchive" and poll.archived_from_status is None used_legacy_unarchive_fallback = action == "unarchive" and poll.archived_from_status is None
cleared_expired_closes_at: datetime | None = None cleared_expired_closes_at = _clear_expired_close_for_open(poll, action=action, now=now)
if action == "open" and poll.closes_at is not None: _apply_transition_state(
normalized_closes_at = response_datetime(poll.closes_at) poll,
if normalized_closes_at is not None and normalized_closes_at <= now: action=action,
cleared_expired_closes_at = normalized_closes_at from_status=plan.from_status,
poll.closes_at = None to_status=plan.to_status,
decision_option=decision_option,
if action == "archive": now=now,
poll.archived_from_status = plan.from_status )
elif action == "unarchive": transition_metadata = _transition_audit_metadata(
poll.archived_from_status = None action=action,
elif action == "close": from_status=plan.from_status,
poll.closed_at = now to_status=plan.to_status,
elif action == "decide": used_legacy_unarchive_fallback=used_legacy_unarchive_fallback,
if decision_option is None: cleared_expired_closes_at=cleared_expired_closes_at,
raise PollError("A decide transition requires a poll option") )
poll.decided_option_id = decision_option.id
poll.decided_at = now
if poll.closed_at is None:
poll.closed_at = now
poll.status = plan.to_status
transition_metadata: dict[str, Any] = {}
if action == "archive":
transition_metadata["archived_from_status"] = plan.from_status
elif action == "unarchive":
transition_metadata["restored_status"] = plan.to_status
if used_legacy_unarchive_fallback:
transition_metadata["legacy_restore_fallback"] = {
"reason": "missing_archived_from_status",
"restored_status": plan.to_status,
}
if cleared_expired_closes_at is not None:
transition_metadata["cleared_expired_closes_at"] = cleared_expired_closes_at.isoformat()
transition = PollLifecycleTransition( transition = PollLifecycleTransition(
tenant_id=tenant_id, tenant_id=tenant_id,
poll_id=poll.id, poll_id=poll.id,