Spaces:
Paused
Paused
| from fastapi import APIRouter, Depends, HTTPException | |
| from sqlalchemy.ext.asyncio import AsyncSession | |
| from sqlalchemy import select, delete | |
| from typing import List, Dict, Any, Optional | |
| from datetime import datetime, timedelta | |
| from ..core.dependencies import get_current_active_user | |
| from ..db.database import get_db | |
| from ..db.models import Event, User | |
| from pydantic import BaseModel | |
| router = APIRouter() | |
| class RecurringEventCreate(BaseModel): | |
| title: str | |
| description: str | |
| start_time: datetime | |
| end_time: datetime | |
| recurrence_pattern: str | |
| recurrence_end_date: Optional[datetime] = None | |
| attendees: List[str] = [] | |
| reminder_minutes: int = 30 | |
| class RecurringEventUpdate(BaseModel): | |
| title: Optional[str] = None | |
| description: Optional[str] = None | |
| start_time: Optional[datetime] = None | |
| end_time: Optional[datetime] = None | |
| attendees: Optional[List[str]] = None | |
| reminder_minutes: Optional[int] = None | |
| async def create_recurring_event( | |
| event_data: RecurringEventCreate, | |
| current_user: User = Depends(get_current_active_user), | |
| db: AsyncSession = Depends(get_db) | |
| ) -> List[Dict[str, Any]]: | |
| """Create a new recurring event""" | |
| if event_data.recurrence_pattern not in ["daily", "weekly", "monthly", "yearly"]: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Invalid recurrence pattern. Must be one of: daily, weekly, monthly, yearly" | |
| ) | |
| if event_data.start_time >= event_data.end_time: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="End time must be after start time" | |
| ) | |
| events = [] | |
| current_start = event_data.start_time | |
| current_end = event_data.end_time | |
| duration = event_data.end_time - event_data.start_time | |
| sequence_number = 0 | |
| while True: | |
| if event_data.recurrence_end_date and current_start > event_data.recurrence_end_date: | |
| break | |
| event = Event( | |
| user_id=current_user.id, | |
| title=event_data.title, | |
| description=event_data.description, | |
| start_time=current_start, | |
| end_time=current_end, | |
| attendees=event_data.attendees, | |
| reminder_minutes=event_data.reminder_minutes, | |
| is_recurring=True, | |
| recurrence_pattern=event_data.recurrence_pattern, | |
| sequence_number=sequence_number, | |
| status="scheduled" | |
| ) | |
| db.add(event) | |
| events.append(event) | |
| # Calculate next occurrence | |
| sequence_number += 1 | |
| if event_data.recurrence_pattern == "daily": | |
| current_start += timedelta(days=1) | |
| elif event_data.recurrence_pattern == "weekly": | |
| current_start += timedelta(weeks=1) | |
| elif event_data.recurrence_pattern == "monthly": | |
| # Add one month (approximately) | |
| if current_start.month == 12: | |
| current_start = current_start.replace(year=current_start.year + 1, month=1) | |
| else: | |
| current_start = current_start.replace(month=current_start.month + 1) | |
| elif event_data.recurrence_pattern == "yearly": | |
| current_start = current_start.replace(year=current_start.year + 1) | |
| current_end = current_start + duration | |
| await db.commit() | |
| # Refresh all events to get their IDs | |
| for event in events: | |
| await db.refresh(event) | |
| return events | |
| async def update_recurring_event( | |
| event_id: int, | |
| event_update: RecurringEventUpdate, | |
| update_future: bool = True, | |
| current_user: User = Depends(get_current_active_user), | |
| db: AsyncSession = Depends(get_db) | |
| ) -> List[Dict[str, Any]]: | |
| """Update a recurring event and optionally its future occurrences""" | |
| update_data = event_update.dict(exclude_unset=True) | |
| if not update_data: | |
| raise HTTPException(status_code=400, detail="No update data provided") | |
| # Get the original event | |
| stmt = select(Event).where( | |
| Event.id == event_id, | |
| Event.user_id == current_user.id | |
| ) | |
| result = await db.execute(stmt) | |
| event = result.scalar_one_or_none() | |
| if not event: | |
| raise HTTPException( | |
| status_code=404, | |
| detail="Event not found or you don't have permission to update it" | |
| ) | |
| updated_events = [event] | |
| # Update future occurrences if requested | |
| if update_future and event.is_recurring: | |
| future_stmt = select(Event).where( | |
| Event.recurrence_group == event.recurrence_group, | |
| Event.sequence_number > event.sequence_number, | |
| Event.user_id == current_user.id | |
| ) | |
| future_result = await db.execute(future_stmt) | |
| future_events = future_result.scalars().all() | |
| for future_event in future_events: | |
| for field, value in update_data.items(): | |
| setattr(future_event, field, value) | |
| updated_events.append(future_event) | |
| await db.commit() | |
| return updated_events | |
| async def delete_recurring_event( | |
| event_id: int, | |
| delete_future: bool = True, | |
| current_user: User = Depends(get_current_active_user), | |
| db: AsyncSession = Depends(get_db) | |
| ) -> Dict[str, bool]: | |
| """Delete a recurring event and optionally its future occurrences""" | |
| stmt = select(Event).where( | |
| Event.id == event_id, | |
| Event.user_id == current_user.id | |
| ) | |
| result = await db.execute(stmt) | |
| event = result.scalar_one_or_none() | |
| if not event: | |
| raise HTTPException( | |
| status_code=404, | |
| detail="Event not found or you don't have permission to delete it" | |
| ) | |
| if delete_future and event.is_recurring: | |
| delete_stmt = delete(Event).where( | |
| Event.recurrence_group == event.recurrence_group, | |
| Event.sequence_number >= event.sequence_number, | |
| Event.user_id == current_user.id | |
| ) | |
| await db.execute(delete_stmt) | |
| else: | |
| await db.delete(event) | |
| await db.commit() | |
| return {"success": True} | |
| async def get_upcoming_recurring_events( | |
| days: int = 30, | |
| current_user: User = Depends(get_current_active_user), | |
| db: AsyncSession = Depends(get_db) | |
| ) -> List[Dict[str, Any]]: | |
| """Get upcoming recurring events for the next N days""" | |
| if days <= 0 or days > 365: | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Days parameter must be between 1 and 365" | |
| ) | |
| end_date = datetime.utcnow() + timedelta(days=days) | |
| stmt = select(Event).where( | |
| Event.user_id == current_user.id, | |
| Event.start_time <= end_date, | |
| Event.is_recurring == True | |
| ).order_by(Event.start_time) | |
| result = await db.execute(stmt) | |
| return result.scalars().all() |