
- Replace authentication system with automatic default user creation - Update all API endpoints to work without authentication - Modify user endpoints to work with default user - Update README.md to reflect the open access model - Fix linting issues and ensure code quality
283 lines
8.4 KiB
Python
283 lines
8.4 KiB
Python
from typing import List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api import deps
|
|
from app.models.user import User
|
|
from app.models.news import NewsArticle, NewsSource, NewsCategory, SavedArticle
|
|
from app.schemas.news import (
|
|
NewsSource as NewsSourceSchema,
|
|
NewsCategory as NewsCategorySchema,
|
|
SavedArticle as SavedArticleSchema,
|
|
SavedArticleCreate,
|
|
NewsResponse,
|
|
)
|
|
from app.services.news import (
|
|
get_news_articles,
|
|
fetch_and_store_news,
|
|
get_saved_articles_for_user,
|
|
save_article_for_user,
|
|
delete_saved_article,
|
|
)
|
|
from app.services.user import get_user_preference
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=NewsResponse)
|
|
async def get_news(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 25,
|
|
keywords: Optional[str] = None,
|
|
sources: Optional[str] = None,
|
|
categories: Optional[str] = None,
|
|
countries: Optional[str] = None,
|
|
languages: Optional[str] = None,
|
|
refresh: bool = False,
|
|
background_tasks: BackgroundTasks = None,
|
|
):
|
|
"""
|
|
Retrieve news articles with optional filtering.
|
|
|
|
If refresh is True, the API will fetch fresh news from Mediastack.
|
|
Otherwise, it will return news articles from the database.
|
|
"""
|
|
# Parse filters
|
|
source_ids = None
|
|
if sources:
|
|
source_names = [s.strip() for s in sources.split(",")]
|
|
source_query = db.query(NewsSource).filter(NewsSource.name.in_(source_names))
|
|
source_ids = [source.id for source in source_query.all()]
|
|
|
|
category_ids = None
|
|
if categories:
|
|
category_names = [c.strip() for c in categories.split(",")]
|
|
category_query = db.query(NewsCategory).filter(NewsCategory.name.in_(category_names))
|
|
category_ids = [category.id for category in category_query.all()]
|
|
|
|
country_list = None
|
|
if countries:
|
|
country_list = [c.strip() for c in countries.split(",")]
|
|
|
|
language_list = None
|
|
if languages:
|
|
language_list = [lang.strip() for lang in languages.split(",")]
|
|
|
|
# If refresh is requested, fetch news from the API
|
|
if refresh and background_tasks:
|
|
background_tasks.add_task(
|
|
fetch_and_store_news,
|
|
db,
|
|
keywords=keywords,
|
|
sources=sources,
|
|
categories=categories,
|
|
countries=countries,
|
|
languages=languages,
|
|
)
|
|
|
|
# Get news from the database
|
|
articles, total = get_news_articles(
|
|
db,
|
|
skip=skip,
|
|
limit=limit,
|
|
keywords=keywords,
|
|
source_ids=source_ids,
|
|
category_ids=category_ids,
|
|
countries=country_list,
|
|
languages=language_list,
|
|
)
|
|
|
|
return {"items": articles, "total": total}
|
|
|
|
|
|
@router.get("/personalized", response_model=NewsResponse)
|
|
async def get_personalized_news(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 25,
|
|
refresh: bool = False,
|
|
background_tasks: BackgroundTasks = None,
|
|
current_user: User = Depends(deps.get_current_user),
|
|
):
|
|
"""
|
|
Retrieve news articles based on the user's preferences.
|
|
"""
|
|
# Get user preferences
|
|
preferences = get_user_preference(db, current_user.id)
|
|
if not preferences:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="User preferences not found",
|
|
)
|
|
|
|
# If refresh is requested, fetch news from the API
|
|
if refresh and background_tasks:
|
|
background_tasks.add_task(
|
|
fetch_and_store_news,
|
|
db,
|
|
keywords=preferences.keywords,
|
|
sources=preferences.sources,
|
|
categories=preferences.categories,
|
|
countries=preferences.countries,
|
|
languages=preferences.languages,
|
|
)
|
|
|
|
# Parse filters from preferences
|
|
source_ids = None
|
|
if preferences.sources:
|
|
source_names = [s.strip() for s in preferences.sources.split(",")]
|
|
source_query = db.query(NewsSource).filter(NewsSource.name.in_(source_names))
|
|
source_ids = [source.id for source in source_query.all()]
|
|
|
|
category_ids = None
|
|
if preferences.categories:
|
|
category_names = [c.strip() for c in preferences.categories.split(",")]
|
|
category_query = db.query(NewsCategory).filter(NewsCategory.name.in_(category_names))
|
|
category_ids = [category.id for category in category_query.all()]
|
|
|
|
country_list = None
|
|
if preferences.countries:
|
|
country_list = [c.strip() for c in preferences.countries.split(",")]
|
|
|
|
language_list = None
|
|
if preferences.languages:
|
|
language_list = [lang.strip() for lang in preferences.languages.split(",")]
|
|
|
|
# Get news from the database
|
|
articles, total = get_news_articles(
|
|
db,
|
|
skip=skip,
|
|
limit=limit,
|
|
keywords=preferences.keywords,
|
|
source_ids=source_ids,
|
|
category_ids=category_ids,
|
|
countries=country_list,
|
|
languages=language_list,
|
|
)
|
|
|
|
return {"items": articles, "total": total}
|
|
|
|
|
|
@router.get("/refresh", response_model=NewsResponse)
|
|
async def refresh_news(
|
|
db: Session = Depends(deps.get_db),
|
|
keywords: Optional[str] = None,
|
|
sources: Optional[str] = None,
|
|
categories: Optional[str] = None,
|
|
countries: Optional[str] = None,
|
|
languages: Optional[str] = None,
|
|
limit: int = 100,
|
|
):
|
|
"""
|
|
Fetch fresh news from Mediastack API and return them.
|
|
|
|
This endpoint will fetch news from the API and store them in the database.
|
|
"""
|
|
articles = await fetch_and_store_news(
|
|
db,
|
|
keywords=keywords,
|
|
sources=sources,
|
|
categories=categories,
|
|
countries=countries,
|
|
languages=languages,
|
|
limit=limit,
|
|
)
|
|
|
|
return {"items": articles, "total": len(articles)}
|
|
|
|
|
|
@router.get("/sources", response_model=List[NewsSourceSchema])
|
|
async def get_sources(
|
|
db: Session = Depends(deps.get_db),
|
|
):
|
|
"""
|
|
Get a list of news sources.
|
|
"""
|
|
sources = db.query(NewsSource).all()
|
|
return sources
|
|
|
|
|
|
@router.get("/categories", response_model=List[NewsCategorySchema])
|
|
async def get_categories(
|
|
db: Session = Depends(deps.get_db),
|
|
):
|
|
"""
|
|
Get a list of news categories.
|
|
"""
|
|
categories = db.query(NewsCategory).all()
|
|
return categories
|
|
|
|
|
|
@router.get("/saved", response_model=List[SavedArticleSchema])
|
|
async def get_saved_articles(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
current_user: User = Depends(deps.get_current_user),
|
|
):
|
|
"""
|
|
Get articles saved by the current user.
|
|
"""
|
|
saved_articles, total = get_saved_articles_for_user(
|
|
db, user_id=current_user.id, skip=skip, limit=limit
|
|
)
|
|
return saved_articles
|
|
|
|
|
|
@router.post("/saved", response_model=SavedArticleSchema)
|
|
async def save_article(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
article_in: SavedArticleCreate,
|
|
current_user: User = Depends(deps.get_current_user),
|
|
):
|
|
"""
|
|
Save an article for the current user.
|
|
"""
|
|
# Check if article exists
|
|
article = db.query(NewsArticle).filter(NewsArticle.id == article_in.article_id).first()
|
|
if not article:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Article not found",
|
|
)
|
|
|
|
# Check if already saved
|
|
existing = (
|
|
db.query(SavedArticle)
|
|
.filter(
|
|
SavedArticle.user_id == current_user.id,
|
|
SavedArticle.article_id == article_in.article_id,
|
|
)
|
|
.first()
|
|
)
|
|
|
|
if existing:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Article already saved",
|
|
)
|
|
|
|
saved_article = save_article_for_user(
|
|
db, user_id=current_user.id, article_id=article_in.article_id, notes=article_in.notes
|
|
)
|
|
return saved_article
|
|
|
|
|
|
@router.delete("/saved/{saved_article_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
|
|
async def remove_saved_article(
|
|
saved_article_id: int,
|
|
db: Session = Depends(deps.get_db),
|
|
current_user: User = Depends(deps.get_current_user),
|
|
):
|
|
"""
|
|
Remove a saved article for the current user.
|
|
"""
|
|
success = delete_saved_article(db, user_id=current_user.id, saved_article_id=saved_article_id)
|
|
if not success:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Saved article not found",
|
|
)
|
|
return None |