from datetime import date from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from ..database import get_db from ..models.news import ProcessedNews router = APIRouter() def _serialize(n: ProcessedNews) -> dict: return { "id": n.id, "title_zh": n.title_zh, "summary": n.summary, "opinion": n.opinion, "keywords": n.keywords or [], "importance_score": n.importance_score, "importance_reason": n.importance_reason, "category": n.category, "is_featured": n.is_featured, "featured_rank": n.featured_rank, "source_name": n.source_name or "", "source_url": n.source_url or "", "published_at": n.published_at.isoformat() if n.published_at else None, "processed_at": n.processed_at.isoformat() if n.processed_at else None, } @router.get("/featured") async def get_featured( news_date: Optional[str] = Query(default=None, alias="date"), db: AsyncSession = Depends(get_db), ): target = date.fromisoformat(news_date) if news_date else date.today() stmt = ( select(ProcessedNews) .where(ProcessedNews.is_featured == True) .where(func.date(ProcessedNews.processed_at) == target) .order_by(ProcessedNews.featured_rank) ) result = await db.execute(stmt) items = result.scalars().all() return {"date": str(target), "items": [_serialize(n) for n in items]} @router.get("") async def get_news( news_date: Optional[str] = Query(default=None, alias="date"), category: Optional[str] = Query(default=None), page: int = Query(default=1, ge=1), page_size: int = Query(default=20, ge=1, le=100), db: AsyncSession = Depends(get_db), ): target = date.fromisoformat(news_date) if news_date else date.today() stmt = select(ProcessedNews).where(func.date(ProcessedNews.processed_at) == target) if category: stmt = stmt.where(ProcessedNews.category == category) count_stmt = select(func.count()).select_from(stmt.subquery()) total = (await db.execute(count_stmt)).scalar_one() stmt = stmt.order_by(ProcessedNews.importance_score.desc()).offset((page - 1) * page_size).limit(page_size) result = await db.execute(stmt) items = result.scalars().all() return {"date": str(target), "total": total, "page": page, "items": [_serialize(n) for n in items]} @router.get("/dates") async def get_dates(db: AsyncSession = Depends(get_db)): stmt = select( func.date(ProcessedNews.processed_at).label("d"), func.count(ProcessedNews.id).label("cnt"), ).group_by("d").order_by(func.date(ProcessedNews.processed_at).desc()).limit(30) result = await db.execute(stmt) return [{"date": str(row.d), "count": row.cnt} for row in result] @router.get("/{news_id}") async def get_news_detail(news_id: int, db: AsyncSession = Depends(get_db)): stmt = select(ProcessedNews).where(ProcessedNews.id == news_id) result = await db.execute(stmt) news = result.scalar_one_or_none() if not news: raise HTTPException(status_code=404, detail="Not found") return _serialize(news)