Files

80 lines
2.6 KiB
Python
Raw Permalink Normal View History

"""Articles router."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_current_user, get_db
from app.models.article import CleanedArticle
from app.models.user import User
from app.schemas.article import ArticleListParams, ArticleOut
from app.schemas.common import MessageResponse, PaginatedResponse
router = APIRouter(prefix="/articles", tags=["articles"])
@router.get("", response_model=PaginatedResponse)
async def list_articles(
params: ArticleListParams = Depends(),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""List cleaned articles with filters."""
query = select(CleanedArticle)
if params.feed_id:
query = query.where(CleanedArticle.feed_id == params.feed_id)
if params.category:
query = query.where(CleanedArticle.category == params.category)
if params.tag:
query = query.where(CleanedArticle.tags.contains([params.tag]))
if params.search:
query = query.where(
CleanedArticle.title.ilike(f"%{params.search}%")
| CleanedArticle.ai_summary.ilike(f"%{params.search}%")
)
if params.is_read is not None:
# CleanedArticle doesn't have is_read in current schema; placeholder
pass
# Count
count_query = select(func.count()).select_from(query.subquery())
total = (await db.execute(count_query)).scalar_one()
# Paginate
query = (
query.offset(params.skip)
.limit(params.limit)
.order_by(CleanedArticle.published_at.desc().nulls_last())
)
result = await db.execute(query)
items = result.scalars().all()
return {
"total": total,
"items": [ArticleOut.model_validate(item) for item in items],
}
@router.get("/{article_id}", response_model=ArticleOut)
async def get_article(
article_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get a single cleaned article."""
article = await db.get(CleanedArticle, article_id)
if not article:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Article not found")
return ArticleOut.model_validate(article)
@router.put("/{article_id}/read", response_model=MessageResponse)
async def mark_article_read(
article_id: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Mark an article as read (placeholder)."""
# In Phase 1, cleaned_articles doesn't have is_read field yet
return {"message": "Article marked as read"}