inital
This commit is contained in:
96
backend/app/api/news.py
Normal file
96
backend/app/api/news.py
Normal file
@@ -0,0 +1,96 @@
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy import select, func, distinct
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ..database import get_db
|
||||
from ..models.news import ProcessedNews, RawNews
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _serialize(n: ProcessedNews) -> dict:
|
||||
raw = n.raw_news
|
||||
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 (raw.source.name if raw and raw.source else ""),
|
||||
"source_url": n.source_url or (raw.url if raw else ""),
|
||||
"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)
|
||||
.join(ProcessedNews.raw_news)
|
||||
.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)
|
||||
.join(ProcessedNews.raw_news)
|
||||
.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).join(ProcessedNews.raw_news).where(ProcessedNews.id == news_id)
|
||||
result = await db.execute(stmt)
|
||||
news = result.scalar_one_or_none()
|
||||
if not news:
|
||||
from fastapi import HTTPException
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
return _serialize(news)
|
||||
Reference in New Issue
Block a user