""" Events router - provides news events with locations for the map """ from fastapi import APIRouter, Depends, Query, HTTPException, BackgroundTasks from sqlalchemy.orm import Session from typing import List, Optional from datetime import datetime, timedelta from ..database import get_db from ..schemas import Event, EventLocation from ..models import Article, LocationBase, Priority from ..services.event_processor import EventProcessor from ..utils.logger import setup_logger logger = setup_logger(__name__) router = APIRouter() @router.get("/events", response_model=List[Event]) def get_events( min_lat: Optional[float] = Query(None, description="Minimum latitude for bounding box"), max_lat: Optional[float] = Query(None, description="Maximum latitude for bounding box"), min_lng: Optional[float] = Query(None, description="Minimum longitude for bounding box"), max_lng: Optional[float] = Query(None, description="Maximum longitude for bounding box"), priority: Optional[str] = Query(None, description="Filter by priority (low, medium, high, critical)"), sentiment: Optional[str] = Query(None, description="Filter by sentiment (positive, neutral, negative)"), limit: Optional[int] = Query(100, description="Maximum number of events to return"), db: Session = Depends(get_db) ): """ Get news events with locations for display on the map. Supports filtering by bounding box, priority, and sentiment. """ try: processor = EventProcessor(db) # Get events from database events = processor.get_processed_events( limit=limit, min_lat=min_lat, max_lat=max_lat, min_lng=min_lng, max_lng=max_lng ) # Filter by priority if provided if priority: events = [e for e in events if e.priority == priority.lower()] # Filter by sentiment if provided if sentiment: events = [e for e in events if e.sentiment_label == sentiment.lower()] logger.info(f"Returning {len(events)} events") return events except Exception as e: logger.error(f"Error fetching events: {e}") raise HTTPException(status_code=500, detail=f"Error fetching events: {str(e)}") @router.get("/events/{event_id}", response_model=Event) def get_event(event_id: int, db: Session = Depends(get_db)): """ Get a single event by ID. """ try: article = db.query(Article).filter( Article.id == event_id, Article.is_processed == True ).first() if not article: raise HTTPException(status_code=404, detail="Event not found") if not article.locations: raise HTTPException(status_code=404, detail="Event has no location data") location = article.locations[0] return Event( id=article.id, title=article.title, link=article.link, summary=article.summary, published_date=article.published_date, source_name=article.source_name, priority=article.priority.value, sentiment_score=article.sentiment_score, sentiment_label=article.sentiment_label, location=EventLocation( latitude=location.latitude, longitude=location.longitude, entity_name=location.entity_name ) ) except HTTPException: raise except Exception as e: logger.error(f"Error fetching event {event_id}: {e}") raise HTTPException(status_code=500, detail=f"Error fetching event: {str(e)}") @router.post("/refresh") def refresh_news( background_tasks: BackgroundTasks, page_size: Optional[int] = Query(50, description="Number of articles to fetch"), db: Session = Depends(get_db) ): """ Manually trigger news refresh from NewsAPI. Processes articles in the background. """ def process_news(): try: processor = EventProcessor(db) articles = processor.fetch_and_process_news(page_size=page_size) logger.info(f"Background task: Processed {len(articles)} articles") except Exception as e: logger.error(f"Background task error: {e}") background_tasks.add_task(process_news) return { "status": "success", "message": f"News refresh started. Processing up to {page_size} articles in the background." } @router.get("/stats") def get_stats(db: Session = Depends(get_db)): """ Get statistics about the news database. """ try: total_articles = db.query(Article).count() processed_articles = db.query(Article).filter(Article.is_processed == True).count() total_locations = db.query(LocationBase).count() # Sentiment distribution positive = db.query(Article).filter(Article.sentiment_label == 'positive').count() neutral = db.query(Article).filter(Article.sentiment_label == 'neutral').count() negative = db.query(Article).filter(Article.sentiment_label == 'negative').count() # Priority distribution critical = db.query(Article).filter(Article.priority == Priority.CRITICAL).count() high = db.query(Article).filter(Article.priority == Priority.HIGH).count() medium = db.query(Article).filter(Article.priority == Priority.MEDIUM).count() low = db.query(Article).filter(Article.priority == Priority.LOW).count() return { "total_articles": total_articles, "processed_articles": processed_articles, "total_locations": total_locations, "sentiment": { "positive": positive, "neutral": neutral, "negative": negative }, "priority": { "critical": critical, "high": high, "medium": medium, "low": low } } except Exception as e: logger.error(f"Error fetching stats: {e}") raise HTTPException(status_code=500, detail=f"Error fetching stats: {str(e)}")