# -*- coding: utf-8 -*- """News and explain FastAPI surface.""" from __future__ import annotations from typing import Any from fastapi import Depends, FastAPI, Query from backend.apps.cors import add_cors_middleware from backend.data.market_store import MarketStore from backend.domains import news as news_domain def get_market_store() -> MarketStore: """Get the MarketStore singleton dependency.""" return MarketStore.get_instance() def create_app() -> FastAPI: """Create the news/explain service app.""" app = FastAPI( title="EvoTraders News Service", description="Read-only news enrichment and explain service surface extracted from the monolith", version="0.1.0", ) add_cors_middleware(app) @app.get("/health") async def health_check() -> dict[str, str]: return {"status": "healthy", "service": "news-service"} @app.get("/api/enriched-news") async def api_get_enriched_news( ticker: str = Query(..., min_length=1), start_date: str | None = Query(None), end_date: str | None = Query(None), limit: int = Query(100, ge=1, le=1000), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_enriched_news( store, ticker=ticker, start_date=start_date, end_date=end_date, limit=limit, refresh_if_stale=False, ) @app.get("/api/news-for-date") async def api_get_news_for_date( ticker: str = Query(..., min_length=1), date: str = Query(...), limit: int = Query(20, ge=1, le=100), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_news_for_date( store, ticker=ticker, date=date, limit=limit, refresh_if_stale=False, ) @app.get("/api/news-timeline") async def api_get_news_timeline( ticker: str = Query(..., min_length=1), start_date: str = Query(...), end_date: str = Query(...), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_news_timeline( store, ticker=ticker, start_date=start_date, end_date=end_date, refresh_if_stale=False, ) @app.get("/api/categories") async def api_get_categories( ticker: str = Query(..., min_length=1), start_date: str | None = Query(None), end_date: str | None = Query(None), limit: int = Query(200, ge=1, le=1000), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_news_categories( store, ticker=ticker, start_date=start_date, end_date=end_date, limit=limit, refresh_if_stale=False, ) @app.get("/api/similar-days") async def api_get_similar_days( ticker: str = Query(..., min_length=1), date: str = Query(...), n_similar: int = Query(5, ge=1, le=20), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_similar_days_payload( store, ticker=ticker, date=date, n_similar=n_similar, refresh_if_stale=False, ) @app.get("/api/stories/{ticker}") async def api_get_story( ticker: str, as_of_date: str = Query(...), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_story_payload( store, ticker=ticker, as_of_date=as_of_date, refresh_if_stale=False, ) @app.get("/api/range-explain") async def api_get_range_explain( ticker: str = Query(..., min_length=1), start_date: str = Query(...), end_date: str = Query(...), article_ids: list[str] = Query(default=[]), limit: int = Query(100, ge=1, le=500), store: MarketStore = Depends(get_market_store), ) -> dict[str, Any]: return news_domain.get_range_explain_payload( store, ticker=ticker, start_date=start_date, end_date=end_date, article_ids=article_ids, limit=limit, refresh_if_stale=False, ) return app app = create_app() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8002)