mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-24 07:58:05 +00:00
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""News router -- Finviz scrape + Google News RSS + Yahoo Finance RSS."""
|
|
|
|
import asyncio
|
|
import logging
|
|
from typing import List
|
|
|
|
from fastapi import APIRouter, HTTPException, Query
|
|
|
|
from server.models.schemas import NewsItem
|
|
from server.services.news_aggregator import merge_news_for_router
|
|
|
|
router = APIRouter()
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@router.get(
|
|
"/{ticker}",
|
|
response_model=List[NewsItem],
|
|
summary="Aggregated news (Finviz + Google + Yahoo)",
|
|
)
|
|
async def get_news(ticker: str):
|
|
"""Return recent articles: Finviz table, Google News RSS, Yahoo headline RSS."""
|
|
try:
|
|
merged = await asyncio.to_thread(
|
|
merge_news_for_router, ticker.upper(), 40
|
|
)
|
|
return [NewsItem(**item) for item in merged]
|
|
except Exception as exc:
|
|
logger.exception("News fetch failed for %s", ticker)
|
|
raise HTTPException(status_code=500, detail=f"News fetch failed: {exc}") from exc
|
|
|
|
|
|
@router.get(
|
|
"/{ticker}/ai-summary",
|
|
summary="AI-summarized news (optional)",
|
|
)
|
|
async def ai_news_summary(
|
|
ticker: str,
|
|
api_key: str = Query("", description="Google Gemini API key (optional)"),
|
|
):
|
|
"""Fetch merged news and optionally summarize with Gemini."""
|
|
try:
|
|
all_items = await asyncio.to_thread(
|
|
merge_news_for_router, ticker.upper(), 30
|
|
)
|
|
headlines: List[str] = []
|
|
for item in all_items:
|
|
title = (item.get("title") or "").strip()
|
|
if title:
|
|
headlines.append(title)
|
|
|
|
headlines = headlines[:20]
|
|
all_items = all_items[:20]
|
|
|
|
if not api_key or not api_key.strip():
|
|
return {
|
|
"ticker": ticker.upper(),
|
|
"summary": None,
|
|
"headlines": headlines,
|
|
"items": all_items,
|
|
}
|
|
|
|
from server.services.gemini_service import get_gemini_model, _generate_with_retry
|
|
|
|
model = get_gemini_model(api_key)
|
|
headline_text = "\n".join(f"- {h}" for h in headlines)
|
|
prompt = f"""You are a financial news analyst. Below are the latest headlines for {ticker.upper()}.
|
|
Provide a concise 3-5 sentence executive summary of the overall sentiment and key themes.
|
|
|
|
Headlines:
|
|
{headline_text}"""
|
|
response = await asyncio.to_thread(
|
|
_generate_with_retry, model, prompt, {"temperature": 0.2, "max_output_tokens": 512}
|
|
)
|
|
summary = (response.text or "").strip() if response else ""
|
|
|
|
return {
|
|
"ticker": ticker.upper(),
|
|
"summary": summary,
|
|
"headlines": headlines,
|
|
"items": all_items,
|
|
}
|
|
except Exception as exc:
|
|
logger.exception("AI news summary failed for %s", ticker)
|
|
raise HTTPException(status_code=500, detail=f"AI news summary failed: {exc}") from exc
|