"""
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)}")