|
| 1 | +import asyncio |
1 | 2 | import logging |
| 3 | +from collections import defaultdict |
2 | 4 | from datetime import datetime, timedelta, timezone |
3 | 5 | from enum import Enum |
4 | 6 | from typing import Annotated, Any, Literal, Optional |
5 | 7 |
|
6 | 8 | from fastapi import APIRouter, Depends, HTTPException |
7 | 9 | from fastapi_pagination import Page |
8 | 10 | from fastapi_pagination.ext.databases import apaginate |
9 | | -from pydantic import BaseModel |
| 11 | +from pydantic import BaseModel, Field |
10 | 12 | from redis.exceptions import LockError |
11 | 13 |
|
12 | 14 | import reflector.auth as auth |
13 | 15 | from reflector.db import get_database |
14 | 16 | from reflector.db.calendar_events import calendar_events_controller |
15 | 17 | from reflector.db.meetings import meetings_controller |
| 18 | +from reflector.db.rooms import Room as DbRoom |
16 | 19 | from reflector.db.rooms import rooms_controller |
17 | 20 | from reflector.redis_cache import RedisAsyncLock |
18 | 21 | from reflector.schemas.platform import Platform |
@@ -195,6 +198,69 @@ async def rooms_list( |
195 | 198 | return paginated |
196 | 199 |
|
197 | 200 |
|
| 201 | +class BulkStatusRequest(BaseModel): |
| 202 | + room_names: list[str] = Field(max_length=100) |
| 203 | + |
| 204 | + |
| 205 | +class RoomMeetingStatus(BaseModel): |
| 206 | + active_meetings: list[Meeting] |
| 207 | + upcoming_events: list[CalendarEventResponse] |
| 208 | + |
| 209 | + |
| 210 | +@router.post("/rooms/meetings/bulk-status", response_model=dict[str, RoomMeetingStatus]) |
| 211 | +async def rooms_bulk_meeting_status( |
| 212 | + request: BulkStatusRequest, |
| 213 | + user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)], |
| 214 | +): |
| 215 | + user_id = user["sub"] if user else None |
| 216 | + |
| 217 | + all_rooms = await rooms_controller.get_by_names(request.room_names) |
| 218 | + # Filter to rooms the user can see (owned or shared), matching rooms_list behavior |
| 219 | + rooms = [ |
| 220 | + r |
| 221 | + for r in all_rooms |
| 222 | + if r.is_shared or (user_id is not None and r.user_id == user_id) |
| 223 | + ] |
| 224 | + room_by_id: dict[str, DbRoom] = {r.id: r for r in rooms} |
| 225 | + room_ids = list(room_by_id.keys()) |
| 226 | + |
| 227 | + current_time = datetime.now(timezone.utc) |
| 228 | + active_meetings, upcoming_events = await asyncio.gather( |
| 229 | + meetings_controller.get_all_active_for_rooms(room_ids, current_time), |
| 230 | + calendar_events_controller.get_upcoming_for_rooms(room_ids), |
| 231 | + ) |
| 232 | + |
| 233 | + # Group by room name |
| 234 | + active_by_room: dict[str, list[Meeting]] = defaultdict(list) |
| 235 | + for m in active_meetings: |
| 236 | + room = room_by_id.get(m.room_id) |
| 237 | + if not room: |
| 238 | + continue |
| 239 | + m.platform = room.platform |
| 240 | + if user_id != room.user_id and m.platform == "whereby": |
| 241 | + m.host_room_url = "" |
| 242 | + active_by_room[room.name].append(m) |
| 243 | + |
| 244 | + upcoming_by_room: dict[str, list[CalendarEventResponse]] = defaultdict(list) |
| 245 | + for e in upcoming_events: |
| 246 | + room = room_by_id.get(e.room_id) |
| 247 | + if not room: |
| 248 | + continue |
| 249 | + if user_id != room.user_id: |
| 250 | + e.description = None |
| 251 | + e.attendees = None |
| 252 | + upcoming_by_room[room.name].append(e) |
| 253 | + |
| 254 | + result: dict[str, RoomMeetingStatus] = {} |
| 255 | + for name in request.room_names: |
| 256 | + result[name] = RoomMeetingStatus( |
| 257 | + active_meetings=active_by_room.get(name, []), |
| 258 | + upcoming_events=upcoming_by_room.get(name, []), |
| 259 | + ) |
| 260 | + |
| 261 | + return result |
| 262 | + |
| 263 | + |
198 | 264 | @router.get("/rooms/{room_id}", response_model=RoomDetails) |
199 | 265 | async def rooms_get( |
200 | 266 | room_id: str, |
|
0 commit comments