locate-the-news / backend / app / schemas.py
schemas.py
Raw
from pydantic import BaseModel, HttpUrl
from datetime import datetime
from typing import List, Optional

# --- Base Schemas ---

class LocationBase(BaseModel):
    """
    Base schema for a single geocoded location.
    """
    entity_name: Optional[str] = None
    latitude: float
    longitude: float
    entity_type: Optional[str] = None
    geocoder_source: Optional[str] = None

    class Config:
        from_attributes = True # Allows Pydantic to read ORM objects

class ArticleBase(BaseModel):
    """
    Base schema for an article being processed.
    """
    title: Optional[str] = None
    link: Optional[str] = None
    summary: Optional[str] = None
    published_date: Optional[datetime] = None
    source_name: Optional[str] = None

    class Config:
        from_attributes = True

# --- API Response Schemas ---

class ArticleInDB(ArticleBase):
    """
    Schema for an article when retrieved from the DB (includes ID).
    """
    id: int
    is_processed: bool = False
    processing_error: Optional[str] = None

# --- Event Schema (The main output for the map) ---

class EventLocation(BaseModel):
    """
    Simplified location schema used in the final Event output.
    This represents the cluster point on the map.
    """
    latitude: float
    longitude: float
    entity_name: Optional[str] = None

    class Config:
        from_attributes = True
        
class Event(BaseModel):
    """
    The final representation of a news event to be displayed on the map.
    This structure is optimized for the front-end (MapView.jsx).
    
    NOTE: In the prototype, one Article will map to one Event for simplicity.
    Later, this will be used for clustered news (many Articles -> one Event).
    """
    id: int
    title: Optional[str] = None
    link: Optional[str] = None
    summary: Optional[str] = None
    published_date: Optional[datetime] = None
    source_name: Optional[str] = None
    priority: Optional[str] = "medium"  # low, medium, high, critical
    
    # Sentiment Analysis fields
    sentiment_score: Optional[float] = 0.0  # -1 to 1
    sentiment_label: Optional[str] = "neutral"  # positive, neutral, negative
    
    # The primary location for the event marker
    location: Optional[EventLocation] = None
    
    # (Optional future feature: list of all articles in the cluster)
    # articles: List[ArticleBase] = []

    class Config:
        from_attributes = True