"""News endpoints — recent scored articles with filtering.""" from __future__ import annotations from fastapi import APIRouter, Depends, Query, Request from services.api_gateway.auth.middleware import get_current_user from sqlalchemy import select, desc, func router = APIRouter(prefix="/api/news", tags=["news"]) @router.get("") async def list_news( request: Request, _user: dict = Depends(get_current_user), ticker: str | None = Query(default=None), source: str | None = Query(default=None), min_score: float | None = Query(default=None, ge=-1.0, le=1.0), max_score: float | None = Query(default=None, ge=-1.0, le=1.0), page: int = Query(default=1, ge=1), per_page: int = Query(default=20, ge=1, le=100), page_size: int | None = Query(default=None, ge=1, le=100), ) -> dict: """Recent scored articles with optional filters.""" from shared.models.news import Article, ArticleSentiment effective_per_page = page_size if page_size is not None else per_page db = request.app.state.db_session_factory async with db() as session: # Base query joining articles with sentiments query = ( select(Article, ArticleSentiment) .join(ArticleSentiment, Article.id == ArticleSentiment.article_id) .order_by(desc(Article.fetched_at)) ) count_query = ( select(func.count()) .select_from(Article) .join(ArticleSentiment, Article.id == ArticleSentiment.article_id) ) if ticker: query = query.where(ArticleSentiment.ticker == ticker.upper()) count_query = count_query.where( ArticleSentiment.ticker == ticker.upper() ) if source: query = query.where(Article.source == source) count_query = count_query.where(Article.source == source) if min_score is not None: query = query.where(ArticleSentiment.score >= min_score) count_query = count_query.where(ArticleSentiment.score >= min_score) if max_score is not None: query = query.where(ArticleSentiment.score <= max_score) count_query = count_query.where(ArticleSentiment.score <= max_score) total = (await session.execute(count_query)).scalar() or 0 offset = (page - 1) * effective_per_page query = query.offset(offset).limit(effective_per_page) result = await session.execute(query) rows = result.all() return { "articles": [ { "id": str(article.id), "source": article.source, "url": article.url, "title": article.title, "published_at": ( article.published_at.isoformat() if article.published_at else article.fetched_at.isoformat() ), "fetched_at": article.fetched_at.isoformat(), "ticker": sentiment.ticker, "sentiment_score": sentiment.score, "confidence": sentiment.confidence, "model_used": sentiment.model_used, } for article, sentiment in rows ], "total": total, "page": page, "page_size": effective_per_page, "per_page": effective_per_page, "pages": (total + effective_per_page - 1) // effective_per_page if effective_per_page else 0, }