feat: Add comment system and outlets data to News API

- Add comment models and service with CRUD operations
- Add comment endpoints (GET, POST, count)
- Add outlets-extracted.json with people/topics/companies data
- Fix database connection in comment_service to use centralized get_database()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jungwoo choi
2025-10-10 18:52:12 +09:00
parent 3ce504e0b1
commit deb52e51f2
4 changed files with 3851 additions and 0 deletions

View File

@ -1,11 +1,26 @@
from fastapi import APIRouter, HTTPException, Query from fastapi import APIRouter, HTTPException, Query
from typing import Optional from typing import Optional
from app.services.article_service import ArticleService from app.services.article_service import ArticleService
from app.services.comment_service import CommentService
from app.models.article import ArticleList, Article, ArticleSummary from app.models.article import ArticleList, Article, ArticleSummary
from app.models.comment import Comment, CommentCreate, CommentList
from typing import List from typing import List
import json
import os
router = APIRouter() router = APIRouter()
# Load outlets data
OUTLETS_FILE = os.path.join(os.path.dirname(__file__), '../../outlets-extracted.json')
outlets_data = None
def load_outlets():
global outlets_data
if outlets_data is None:
with open(OUTLETS_FILE, 'r', encoding='utf-8') as f:
outlets_data = json.load(f)
return outlets_data
@router.get("/{language}/articles", response_model=ArticleList) @router.get("/{language}/articles", response_model=ArticleList)
async def get_articles( async def get_articles(
language: str, language: str,
@ -65,3 +80,46 @@ async def get_categories(language: str):
raise HTTPException(status_code=400, detail=f"Unsupported language: {language}") raise HTTPException(status_code=400, detail=f"Unsupported language: {language}")
return await ArticleService.get_categories(language) return await ArticleService.get_categories(language)
@router.get("/outlets")
async def get_outlets(category: Optional[str] = Query(None, description="Filter by category: people, topics, companies")):
"""Get outlets list - people, topics, companies"""
data = load_outlets()
if category:
if category in ['people', 'topics', 'companies']:
return {category: data[category]}
else:
raise HTTPException(status_code=400, detail=f"Invalid category: {category}. Must be one of: people, topics, companies")
return data
@router.get("/outlets/{outlet_id}")
async def get_outlet_by_id(outlet_id: str):
"""Get specific outlet by ID"""
data = load_outlets()
# Search in all categories
for category in ['people', 'topics', 'companies']:
for outlet in data[category]:
if outlet['id'] == outlet_id:
return outlet
raise HTTPException(status_code=404, detail=f"Outlet not found: {outlet_id}")
# Comment endpoints
@router.get("/comments", response_model=CommentList)
async def get_comments(article_id: str = Query(..., alias="articleId")):
"""Get comments for an article"""
return await CommentService.get_comments_by_article(article_id)
@router.post("/comments", response_model=Comment)
async def create_comment(comment: CommentCreate):
"""Create a new comment"""
return await CommentService.create_comment(comment)
@router.get("/articles/{article_id}/comment-count")
async def get_comment_count(article_id: str):
"""Get comment count for an article"""
count = await CommentService.get_comment_count(article_id)
return {"count": count}

View File

@ -0,0 +1,22 @@
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
class CommentBase(BaseModel):
articleId: str
nickname: str
content: str
class CommentCreate(CommentBase):
pass
class Comment(CommentBase):
id: str
createdAt: datetime
class Config:
from_attributes = True
class CommentList(BaseModel):
comments: list[Comment]
total: int

View File

@ -0,0 +1,53 @@
from app.core.database import get_database
from app.models.comment import Comment, CommentCreate, CommentList
from datetime import datetime
from typing import List
import uuid
class CommentService:
@classmethod
async def create_comment(cls, comment_data: CommentCreate) -> Comment:
"""Create a new comment"""
db = get_database()
collection = db.comments
comment_dict = {
"id": str(uuid.uuid4()),
"articleId": comment_data.articleId,
"nickname": comment_data.nickname,
"content": comment_data.content,
"createdAt": datetime.utcnow()
}
await collection.insert_one(comment_dict)
return Comment(**comment_dict)
@classmethod
async def get_comments_by_article(cls, article_id: str) -> CommentList:
"""Get all comments for an article"""
db = get_database()
collection = db.comments
cursor = collection.find(
{"articleId": article_id},
{"_id": 0}
).sort("createdAt", -1)
comments = await cursor.to_list(length=None)
total = len(comments)
return CommentList(
comments=[Comment(**comment) for comment in comments],
total=total
)
@classmethod
async def get_comment_count(cls, article_id: str) -> int:
"""Get comment count for an article"""
db = get_database()
collection = db.comments
count = await collection.count_documents({"articleId": article_id})
return count

File diff suppressed because it is too large Load Diff