Spaces:
Running
Running
SHAFI
solved the pulse no-articles-shown issue by adjusting pydantic and solving version hell
2ab9d2f | 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) | |
| # --- CRITICAL FIX: All fields are Optional with safe defaults --- | |
| # Previously, 'title: str' and 'published_at: datetime' were strict. | |
| # A single null value from Appwrite would crash the ENTIRE request with | |
| # a 500, returning zero articles. Now the backend is forgiving. | |
| title: Optional[str] = "Untitled Article" | |
| id: Optional[str] = Field(None, alias="$id") # Appwrite ID | |
| description: Optional[str] = "" | |
| url: Optional[str] = "#" | |
| image_url: Optional[str] = "" | |
| published_at: Optional[datetime] = None | |
| source: Optional[str] = "" | |
| category: Optional[str] = "" | |
| audio_url: Optional[str] = None | |
| text_summary: Optional[str] = None | |
| url_hash: Optional[str] = None | |
| author: Optional[str] = None | |
| # Engagement Stats (Side-loaded) | |
| likes: int = 0 | |
| dislikes: int = Field(default=0, validation_alias="dislike") # Alias for DB 'dislike' | |
| views: int = 0 | |
| def parse_datetime(cls, v): | |
| """Parse datetime from various formats including RFC 2822 (RSS feeds)""" | |
| if v is None: | |
| return None | |
| if isinstance(v, datetime): | |
| return v | |
| if isinstance(v, str): | |
| try: | |
| # Try RFC 2822 format (used by RSS feeds like Google News) | |
| return parsedate_to_datetime(v) | |
| except: | |
| try: | |
| # Try ISO format | |
| return datetime.fromisoformat(v.replace('Z', '+00:00')) | |
| except: | |
| try: | |
| # Fallback to dateutil parser | |
| from dateutil import parser | |
| return parser.parse(v) | |
| except: | |
| # Last resort: return None (no crash) | |
| return None | |
| 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 # "redis", "appwrite", "empty", or "api" | |
| message: Optional[str] = None # User-friendly message for empty states | |
| 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 | |