perf: batch scheduling reconciliation and add widget

This commit is contained in:
2026-07-29 14:16:29 +02:00
parent 60b01e5a16
commit b9f557e95b
7 changed files with 290 additions and 32 deletions

View File

@@ -7,7 +7,8 @@ from datetime import datetime, timedelta, timezone
from functools import lru_cache
from typing import Any, cast
from sqlalchemy.orm import Session, object_session
from sqlalchemy import func, or_
from sqlalchemy.orm import Session, object_session, selectinload
from govoplan_core.core.calendar import (
CalendarCapabilityError,
@@ -1303,11 +1304,34 @@ def create_scheduling_request(
return request, {}
def list_scheduling_requests(session: Session, *, tenant_id: str, status: str | None = None) -> list[SchedulingRequest]:
query = session.query(SchedulingRequest).filter(SchedulingRequest.tenant_id == tenant_id, SchedulingRequest.deleted_at.is_(None))
def list_scheduling_requests(
session: Session,
*,
tenant_id: str,
status: str | None = None,
limit: int = 100,
) -> list[SchedulingRequest]:
query = (
session.query(SchedulingRequest)
.options(
selectinload(SchedulingRequest.slots),
selectinload(SchedulingRequest.participants),
)
.filter(
SchedulingRequest.tenant_id == tenant_id,
SchedulingRequest.deleted_at.is_(None),
)
)
if status:
query = query.filter(SchedulingRequest.status == status)
return query.order_by(SchedulingRequest.created_at.desc(), SchedulingRequest.title.asc()).all()
return (
query.order_by(
SchedulingRequest.created_at.desc(),
SchedulingRequest.title.asc(),
)
.limit(max(1, min(limit, 200)))
.all()
)
def _actor_ids(values: tuple[str, ...]) -> tuple[str, ...]:
@@ -1369,12 +1393,53 @@ def list_visible_scheduling_requests(
actor_ids: tuple[str, ...],
can_manage: bool = False,
status: str | None = None,
limit: int = 100,
) -> list[SchedulingRequest]:
return [
request
for request in list_scheduling_requests(session, tenant_id=tenant_id, status=status)
if scheduling_request_is_visible(request, actor_ids=actor_ids, can_manage=can_manage)
]
query = (
session.query(SchedulingRequest)
.options(
selectinload(SchedulingRequest.slots),
selectinload(SchedulingRequest.participants),
)
.filter(
SchedulingRequest.tenant_id == tenant_id,
SchedulingRequest.deleted_at.is_(None),
)
)
if status:
query = query.filter(SchedulingRequest.status == status)
if not can_manage:
ids = _actor_ids(actor_ids)
email_ids = tuple(value.casefold() for value in ids if "@" in value)
participant_exists = (
session.query(SchedulingParticipant.id)
.filter(
SchedulingParticipant.tenant_id == tenant_id,
SchedulingParticipant.request_id == SchedulingRequest.id,
SchedulingParticipant.deleted_at.is_(None),
or_(
SchedulingParticipant.respondent_id.in_(ids or ("",)),
func.lower(SchedulingParticipant.email).in_(
email_ids or ("",)
),
),
)
.exists()
)
query = query.filter(
or_(
SchedulingRequest.organizer_user_id.in_(ids or ("",)),
participant_exists,
)
)
return (
query.order_by(
SchedulingRequest.created_at.desc(),
SchedulingRequest.title.asc(),
)
.limit(max(1, min(limit, 200)))
.all()
)
def get_scheduling_request(session: Session, *, tenant_id: str, request_id: str) -> SchedulingRequest: