| from pydantic import BaseModel, HttpUrl, field_validator, Field, ConfigDict |
| from typing import Optional, List |
| from datetime import datetime |
| from email.utils import parsedate_to_datetime |
|
|
| class Article(BaseModel): |
| """News article model""" |
| model_config = ConfigDict(populate_by_name=True) |
|
|
| title: str |
| id: Optional[str] = Field(None, alias="$id") |
| description: Optional[str] = "" |
| url: Optional[str] = None |
| |
| image_url: Optional[str] = "" |
| published_at: datetime |
| source: Optional[str] = "" |
| category: Optional[str] = "" |
| audio_url: Optional[str] = None |
| text_summary: Optional[str] = None |
| |
| |
| likes: int = 0 |
| dislikes: int = Field(default=0, validation_alias="dislike") |
| views: int = 0 |
| |
| @field_validator('published_at', mode='before') |
| @classmethod |
| def parse_datetime(cls, v): |
| """Parse datetime from various formats including RFC 2822 (RSS feeds)""" |
| if isinstance(v, datetime): |
| return v |
| if isinstance(v, str): |
| try: |
| |
| |
| return parsedate_to_datetime(v) |
| except: |
| try: |
| |
| return datetime.fromisoformat(v.replace('Z', '+00:00')) |
| except: |
| try: |
| |
| from dateutil import parser |
| return parser.parse(v) |
| except: |
| |
| return datetime.now() |
| return v |
|
|
| class NewsResponse(BaseModel): |
| """Response model for news endpoints""" |
| success: bool |
| category: str |
| count: int |
| articles: List[Article] |
| cached: bool = False |
| source: Optional[str] = None |
| message: Optional[str] = None |
|
|
| class SearchResponse(BaseModel): |
| """Response model for search endpoints""" |
| success: bool |
| query: str |
| count: int |
| articles: List[Article] |
|
|
| class ViewCountRequest(BaseModel): |
| """Request model for view count increment""" |
| article_url: HttpUrl |
|
|
| class ViewCountResponse(BaseModel): |
| """Response model for view count""" |
| success: bool |
| article_url: str |
| view_count: int |
|
|
| class ErrorResponse(BaseModel): |
| """Error response model""" |
| success: bool = False |
| error: str |
| detail: Optional[str] = None |
|
|