perf: bound and batch visible poll listings
This commit is contained in:
@@ -176,6 +176,7 @@ def _require_sensitive_poll_data_scope(principal: ApiPrincipal) -> None:
|
|||||||
def api_list_polls(
|
def api_list_polls(
|
||||||
status_filter: str | None = Query(default=None, alias="status"),
|
status_filter: str | None = Query(default=None, alias="status"),
|
||||||
kind: str | None = None,
|
kind: str | None = None,
|
||||||
|
limit: int = 100,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
principal: ApiPrincipal = Depends(get_api_principal),
|
principal: ApiPrincipal = Depends(get_api_principal),
|
||||||
) -> PollListResponse:
|
) -> PollListResponse:
|
||||||
@@ -187,6 +188,7 @@ def api_list_polls(
|
|||||||
can_manage=_can_manage_polls(principal),
|
can_manage=_can_manage_polls(principal),
|
||||||
status=status_filter,
|
status=status_filter,
|
||||||
kind=kind,
|
kind=kind,
|
||||||
|
limit=limit,
|
||||||
)
|
)
|
||||||
return PollListResponse(polls=[_poll_response(poll) for poll in polls])
|
return PollListResponse(polls=[_poll_response(poll) for poll in polls])
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from typing import Any, Callable
|
|||||||
|
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session, selectinload
|
||||||
|
|
||||||
from govoplan_core.db.base import utcnow
|
from govoplan_core.db.base import utcnow
|
||||||
from govoplan_poll.backend.db.models import Poll, PollInvitation, PollLifecycleTransition, PollOption, PollResponse
|
from govoplan_poll.backend.db.models import Poll, PollInvitation, PollLifecycleTransition, PollOption, PollResponse
|
||||||
@@ -366,13 +366,28 @@ def create_poll(
|
|||||||
return poll
|
return poll
|
||||||
|
|
||||||
|
|
||||||
def list_polls(session: Session, *, tenant_id: str, status: str | None = None, kind: str | None = None) -> list[Poll]:
|
def list_polls(
|
||||||
query = session.query(Poll).filter(Poll.tenant_id == tenant_id, Poll.deleted_at.is_(None))
|
session: Session,
|
||||||
|
*,
|
||||||
|
tenant_id: str,
|
||||||
|
status: str | None = None,
|
||||||
|
kind: str | None = None,
|
||||||
|
limit: int = 100,
|
||||||
|
) -> list[Poll]:
|
||||||
|
query = (
|
||||||
|
session.query(Poll)
|
||||||
|
.options(selectinload(Poll.options))
|
||||||
|
.filter(Poll.tenant_id == tenant_id, Poll.deleted_at.is_(None))
|
||||||
|
)
|
||||||
if status:
|
if status:
|
||||||
query = query.filter(Poll.status == status)
|
query = query.filter(Poll.status == status)
|
||||||
if kind:
|
if kind:
|
||||||
query = query.filter(Poll.kind == kind)
|
query = query.filter(Poll.kind == kind)
|
||||||
return query.order_by(Poll.created_at.desc(), Poll.title.asc()).all()
|
return (
|
||||||
|
query.order_by(Poll.created_at.desc(), Poll.title.asc())
|
||||||
|
.limit(max(1, min(limit, 200)))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_poll(session: Session, *, tenant_id: str, poll_id: str) -> Poll:
|
def get_poll(session: Session, *, tenant_id: str, poll_id: str) -> Poll:
|
||||||
@@ -432,12 +447,45 @@ def list_visible_polls(
|
|||||||
can_manage: bool = False,
|
can_manage: bool = False,
|
||||||
status: str | None = None,
|
status: str | None = None,
|
||||||
kind: str | None = None,
|
kind: str | None = None,
|
||||||
|
limit: int = 100,
|
||||||
) -> list[Poll]:
|
) -> list[Poll]:
|
||||||
return [
|
query = (
|
||||||
poll
|
session.query(Poll)
|
||||||
for poll in list_polls(session, tenant_id=tenant_id, status=status, kind=kind)
|
.options(selectinload(Poll.options))
|
||||||
if poll_is_visible(session, poll=poll, actor_ids=actor_ids, can_manage=can_manage)
|
.filter(Poll.tenant_id == tenant_id, Poll.deleted_at.is_(None))
|
||||||
]
|
)
|
||||||
|
if status:
|
||||||
|
query = query.filter(Poll.status == status)
|
||||||
|
if kind:
|
||||||
|
query = query.filter(Poll.kind == kind)
|
||||||
|
if not can_manage:
|
||||||
|
ids = _actor_ids(actor_ids)
|
||||||
|
invitation_exists = (
|
||||||
|
session.query(PollInvitation.id)
|
||||||
|
.filter(
|
||||||
|
PollInvitation.tenant_id == tenant_id,
|
||||||
|
PollInvitation.poll_id == Poll.id,
|
||||||
|
PollInvitation.respondent_id.in_(ids or ("",)),
|
||||||
|
PollInvitation.revoked_at.is_(None),
|
||||||
|
or_(
|
||||||
|
PollInvitation.expires_at.is_(None),
|
||||||
|
PollInvitation.expires_at > _now(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.exists()
|
||||||
|
)
|
||||||
|
query = query.filter(
|
||||||
|
or_(
|
||||||
|
Poll.created_by_user_id.in_(ids or ("",)),
|
||||||
|
Poll.visibility.in_(("tenant", "public")),
|
||||||
|
invitation_exists,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
query.order_by(Poll.created_at.desc(), Poll.title.asc())
|
||||||
|
.limit(max(1, min(limit, 200)))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_visible_poll(
|
def get_visible_poll(
|
||||||
|
|||||||
Reference in New Issue
Block a user