from __future__ import annotations from dataclasses import dataclass from datetime import datetime from typing import Optional from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from models.calendars import CalendarEntry @dataclass class VisibleEntries: """ Result of applying calendar visibility rules for a given period. """ user_entries: list[CalendarEntry] confirmed_entries: list[CalendarEntry] admin_other_entries: list[CalendarEntry] merged_entries: list[CalendarEntry] # sorted, deduped async def get_visible_entries_for_period( sess: AsyncSession, calendar_id: int, period_start: datetime, period_end: datetime, user: Optional[object], session_id: Optional[str], ) -> VisibleEntries: """ Visibility rules (same as your fixed month view): - Non-admin: - sees all *confirmed* entries in the period (any user) - sees all entries for current user/session in the period (any state) - Admin: - sees all confirmed + provisional + ordered entries in the period (all users) - sees pending only for current user/session """ user_id = user.id if user else None is_admin = bool(user and getattr(user, "is_admin", False)) # --- Entries for current user/session (any state, in period) --- user_entries: list[CalendarEntry] = [] if user_id or session_id: conditions_user = [ CalendarEntry.calendar_id == calendar_id, CalendarEntry.deleted_at.is_(None), CalendarEntry.start_at >= period_start, CalendarEntry.start_at < period_end, ] if user_id: conditions_user.append(CalendarEntry.user_id == user_id) elif session_id: conditions_user.append(CalendarEntry.session_id == session_id) result_user = await sess.execute(select(CalendarEntry).where(*conditions_user)) user_entries = result_user.scalars().all() # --- Confirmed entries for everyone in period --- result_conf = await sess.execute( select(CalendarEntry).where( CalendarEntry.calendar_id == calendar_id, CalendarEntry.state == "confirmed", CalendarEntry.deleted_at.is_(None), CalendarEntry.start_at >= period_start, CalendarEntry.start_at < period_end, ) ) confirmed_entries = result_conf.scalars().all() # --- For admins: ordered + provisional for everyone in period --- admin_other_entries: list[CalendarEntry] = [] if is_admin: result_admin = await sess.execute( select(CalendarEntry).where( CalendarEntry.calendar_id == calendar_id, CalendarEntry.state.in_(("ordered", "provisional")), CalendarEntry.deleted_at.is_(None), CalendarEntry.start_at >= period_start, CalendarEntry.start_at < period_end, ) ) admin_other_entries = result_admin.scalars().all() # --- Merge with de-duplication and keep chronological order --- entries_by_id: dict[int, CalendarEntry] = {} # Everyone's confirmed for e in confirmed_entries: entries_by_id[e.id] = e # Admin-only: everyone's ordered/provisional if is_admin: for e in admin_other_entries: entries_by_id[e.id] = e # Always include current user/session entries (includes their pending) for e in user_entries: entries_by_id[e.id] = e merged_entries = sorted( entries_by_id.values(), key=lambda e: e.start_at or period_start, ) return VisibleEntries( user_entries=user_entries, confirmed_entries=confirmed_entries, admin_other_entries=admin_other_entries, merged_entries=merged_entries, )