Add upcoming_entries_for_container to CalendarService

New paginated query for upcoming confirmed entries across all calendars
belonging to a container (page). Used by the events page summary view.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
giles
2026-02-22 22:28:18 +00:00
parent 7316dc6eac
commit 6e438dbfdc
3 changed files with 37 additions and 0 deletions

View File

@@ -129,6 +129,11 @@ class CalendarService(Protocol):
self, session: AsyncSession, content_type: str, content_id: int, self, session: AsyncSession, content_type: str, content_id: int,
) -> set[int]: ... ) -> set[int]: ...
async def upcoming_entries_for_container(
self, session: AsyncSession, container_type: str, container_id: int,
*, page: int = 1, per_page: int = 20,
) -> tuple[list[CalendarEntryDTO], bool]: ...
async def visible_entries_for_period( async def visible_entries_for_period(
self, session: AsyncSession, calendar_id: int, self, session: AsyncSession, calendar_id: int,
period_start: datetime, period_end: datetime, period_start: datetime, period_end: datetime,

View File

@@ -239,6 +239,35 @@ class SqlCalendarService:
merged = sorted(entries_by_id.values(), key=lambda e: e.start_at or period_start) merged = sorted(entries_by_id.values(), key=lambda e: e.start_at or period_start)
return [_entry_to_dto(e) for e in merged] return [_entry_to_dto(e) for e in merged]
async def upcoming_entries_for_container(
self, session: AsyncSession, container_type: str, container_id: int,
*, page: int = 1, per_page: int = 20,
) -> tuple[list[CalendarEntryDTO], bool]:
"""Upcoming confirmed entries across all calendars for a container."""
cal_ids = select(Calendar.id).where(
Calendar.container_type == container_type,
Calendar.container_id == container_id,
Calendar.deleted_at.is_(None),
).scalar_subquery()
offset = (page - 1) * per_page
result = await session.execute(
select(CalendarEntry)
.where(
CalendarEntry.calendar_id.in_(cal_ids),
CalendarEntry.state == "confirmed",
CalendarEntry.deleted_at.is_(None),
CalendarEntry.start_at >= func.now(),
)
.order_by(CalendarEntry.start_at.asc())
.limit(per_page)
.offset(offset)
.options(selectinload(CalendarEntry.calendar))
)
entries = result.scalars().all()
has_more = len(entries) == per_page
return [_entry_to_dto(e) for e in entries], has_more
async def associated_entries( async def associated_entries(
self, session: AsyncSession, content_type: str, content_id: int, page: int, self, session: AsyncSession, content_type: str, content_id: int, page: int,
) -> tuple[list[CalendarEntryDTO], bool]: ) -> tuple[list[CalendarEntryDTO], bool]:

View File

@@ -140,6 +140,9 @@ class StubCalendarService:
) -> int: ) -> int:
return 0 return 0
async def upcoming_entries_for_container(self, session, container_type, container_id, *, page=1, per_page=20):
return [], False
async def entry_ids_for_content(self, session, content_type, content_id): async def entry_ids_for_content(self, session, content_type, content_id):
return set() return set()