""" Event Repository Data access layer for events """ from typing import List, Optional, Dict, Any from datetime import date, datetime from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, and_, or_, func from uuid import UUID import structlog from app.models.events import Event, EventTemplate from shared.database.repository import BaseRepository logger = structlog.get_logger() class EventRepository(BaseRepository[Event]): """Repository for event management""" def __init__(self, session: AsyncSession): super().__init__(Event, session) async def get_events_by_date_range( self, tenant_id: UUID, start_date: date, end_date: date, event_types: List[str] = None, confirmed_only: bool = False ) -> List[Event]: """ Get events within a date range. Args: tenant_id: Tenant UUID start_date: Start date (inclusive) end_date: End date (inclusive) event_types: Optional filter by event types confirmed_only: Only return confirmed events Returns: List of Event objects """ try: query = select(Event).where( and_( Event.tenant_id == tenant_id, Event.event_date >= start_date, Event.event_date <= end_date ) ) if event_types: query = query.where(Event.event_type.in_(event_types)) if confirmed_only: query = query.where(Event.is_confirmed == True) query = query.order_by(Event.event_date) result = await self.session.execute(query) events = result.scalars().all() logger.debug("Retrieved events by date range", tenant_id=str(tenant_id), start_date=start_date.isoformat(), end_date=end_date.isoformat(), count=len(events)) return list(events) except Exception as e: logger.error("Failed to get events by date range", tenant_id=str(tenant_id), error=str(e)) return [] async def get_events_for_date( self, tenant_id: UUID, event_date: date ) -> List[Event]: """ Get all events for a specific date. Args: tenant_id: Tenant UUID event_date: Date to get events for Returns: List of Event objects """ try: query = select(Event).where( and_( Event.tenant_id == tenant_id, Event.event_date == event_date ) ).order_by(Event.start_time) result = await self.session.execute(query) events = result.scalars().all() return list(events) except Exception as e: logger.error("Failed to get events for date", tenant_id=str(tenant_id), error=str(e)) return [] async def get_upcoming_events( self, tenant_id: UUID, days_ahead: int = 30, limit: int = 100 ) -> List[Event]: """ Get upcoming events. Args: tenant_id: Tenant UUID days_ahead: Number of days to look ahead limit: Maximum number of events to return Returns: List of upcoming Event objects """ try: from datetime import date, timedelta today = date.today() future_date = today + timedelta(days=days_ahead) query = select(Event).where( and_( Event.tenant_id == tenant_id, Event.event_date >= today, Event.event_date <= future_date ) ).order_by(Event.event_date).limit(limit) result = await self.session.execute(query) events = result.scalars().all() return list(events) except Exception as e: logger.error("Failed to get upcoming events", tenant_id=str(tenant_id), error=str(e)) return [] async def create_event(self, event_data: Dict[str, Any]) -> Event: """Create a new event""" try: event = Event(**event_data) self.session.add(event) await self.session.flush() logger.info("Created event", event_id=str(event.id), event_name=event.event_name, event_date=event.event_date.isoformat()) return event except Exception as e: logger.error("Failed to create event", error=str(e)) raise async def update_event_actual_impact( self, event_id: UUID, actual_impact_multiplier: float, actual_sales_increase_percent: float ) -> Optional[Event]: """ Update event with actual impact after it occurs. Args: event_id: Event UUID actual_impact_multiplier: Actual demand multiplier observed actual_sales_increase_percent: Actual sales increase percentage Returns: Updated Event or None """ try: event = await self.get(event_id) if not event: return None event.actual_impact_multiplier = actual_impact_multiplier event.actual_sales_increase_percent = actual_sales_increase_percent await self.session.flush() logger.info("Updated event actual impact", event_id=str(event_id), actual_multiplier=actual_impact_multiplier) return event except Exception as e: logger.error("Failed to update event actual impact", event_id=str(event_id), error=str(e)) return None async def get_events_by_type( self, tenant_id: UUID, event_type: str, limit: int = 100 ) -> List[Event]: """Get events by type""" try: query = select(Event).where( and_( Event.tenant_id == tenant_id, Event.event_type == event_type ) ).order_by(Event.event_date.desc()).limit(limit) result = await self.session.execute(query) events = result.scalars().all() return list(events) except Exception as e: logger.error("Failed to get events by type", tenant_id=str(tenant_id), event_type=event_type, error=str(e)) return [] class EventTemplateRepository(BaseRepository[EventTemplate]): """Repository for event template management""" def __init__(self, session: AsyncSession): super().__init__(EventTemplate, session) async def get_active_templates(self, tenant_id: UUID) -> List[EventTemplate]: """Get all active event templates for a tenant""" try: query = select(EventTemplate).where( and_( EventTemplate.tenant_id == tenant_id, EventTemplate.is_active == True ) ).order_by(EventTemplate.template_name) result = await self.session.execute(query) templates = result.scalars().all() return list(templates) except Exception as e: logger.error("Failed to get active templates", tenant_id=str(tenant_id), error=str(e)) return [] async def create_template(self, template_data: Dict[str, Any]) -> EventTemplate: """Create a new event template""" try: template = EventTemplate(**template_data) self.session.add(template) await self.session.flush() logger.info("Created event template", template_id=str(template.id), template_name=template.template_name) return template except Exception as e: logger.error("Failed to create event template", error=str(e)) raise