from datetime import date, datetime from typing import Any, Generic, Optional, TypeVar from pydantic import BaseModel T = TypeVar("T") class PaginatedResponse(BaseModel, Generic[T]): items: list[T] total: int page: int per_page: int pages: int # ── Member ──────────────────────────────────────────────────────────────────── class MemberSchema(BaseModel): bioguide_id: str name: str first_name: Optional[str] = None last_name: Optional[str] = None party: Optional[str] = None state: Optional[str] = None chamber: Optional[str] = None district: Optional[str] = None photo_url: Optional[str] = None official_url: Optional[str] = None congress_url: Optional[str] = None birth_year: Optional[str] = None address: Optional[str] = None phone: Optional[str] = None terms_json: Optional[list[Any]] = None leadership_json: Optional[list[Any]] = None sponsored_count: Optional[int] = None cosponsored_count: Optional[int] = None model_config = {"from_attributes": True} # ── Bill Brief ──────────────────────────────────────────────────────────────── class BriefSchema(BaseModel): id: int brief_type: str = "full" summary: Optional[str] = None key_points: Optional[list[Any]] = None risks: Optional[list[Any]] = None deadlines: Optional[list[dict[str, Any]]] = None topic_tags: Optional[list[str]] = None llm_provider: Optional[str] = None llm_model: Optional[str] = None govinfo_url: Optional[str] = None created_at: Optional[datetime] = None model_config = {"from_attributes": True} # ── Bill Action ─────────────────────────────────────────────────────────────── class BillActionSchema(BaseModel): id: int action_date: Optional[date] = None action_text: Optional[str] = None action_type: Optional[str] = None chamber: Optional[str] = None model_config = {"from_attributes": True} # ── News Article ────────────────────────────────────────────────────────────── class NewsArticleSchema(BaseModel): id: int source: Optional[str] = None headline: Optional[str] = None url: Optional[str] = None published_at: Optional[datetime] = None relevance_score: Optional[float] = None model_config = {"from_attributes": True} # ── Trend Score ─────────────────────────────────────────────────────────────── class TrendScoreSchema(BaseModel): score_date: date newsapi_count: int gnews_count: int gtrends_score: float composite_score: float model_config = {"from_attributes": True} # ── Bill ────────────────────────────────────────────────────────────────────── class BillSchema(BaseModel): bill_id: str congress_number: int bill_type: str bill_number: int title: Optional[str] = None short_title: Optional[str] = None introduced_date: Optional[date] = None latest_action_date: Optional[date] = None latest_action_text: Optional[str] = None status: Optional[str] = None chamber: Optional[str] = None congress_url: Optional[str] = None sponsor: Optional[MemberSchema] = None latest_brief: Optional[BriefSchema] = None latest_trend: Optional[TrendScoreSchema] = None updated_at: Optional[datetime] = None model_config = {"from_attributes": True} class BillDetailSchema(BillSchema): actions: list[BillActionSchema] = [] news_articles: list[NewsArticleSchema] = [] trend_scores: list[TrendScoreSchema] = [] briefs: list[BriefSchema] = [] # ── Follow ──────────────────────────────────────────────────────────────────── class FollowCreate(BaseModel): follow_type: str # bill | member | topic follow_value: str class FollowSchema(BaseModel): id: int user_id: int follow_type: str follow_value: str created_at: datetime model_config = {"from_attributes": True} # ── Settings ────────────────────────────────────────────────────────────────── # ── Auth ────────────────────────────────────────────────────────────────────── class UserCreate(BaseModel): email: str password: str class UserResponse(BaseModel): id: int email: str is_admin: bool notification_prefs: dict created_at: Optional[datetime] = None model_config = {"from_attributes": True} class TokenResponse(BaseModel): access_token: str token_type: str = "bearer" user: "UserResponse" # ── Settings ────────────────────────────────────────────────────────────────── class SettingUpdate(BaseModel): key: str value: str class SettingsResponse(BaseModel): llm_provider: str llm_model: str congress_poll_interval_minutes: int newsapi_enabled: bool pytrends_enabled: bool