- Fetch bill actions from Congress.gov and populate the action timeline - Add nightly batch task and beat schedule for active bill actions - Add admin reprocess endpoint for per-bill debugging - Add BriefPanel with "What Changed" view and version history - Add External API Health section with per-source latency testing - Redesign Manual Controls as health panel with status dots and descriptions - Add Resume Analysis task for stalled LLM jobs - Add Backfill Dates & Links task for bills with null metadata - Fix LLM provider/model DB overrides being ignored (env vars used instead) - Fix Gemini 404: gemini-1.5-pro deprecated → gemini-2.0-flash - Fix Anthropic models list: use REST API directly (SDK too old for .models) - Replace test-LLM full analysis with lightweight ping (max_tokens=20) - Add has_document field to BillDetail; show "No bill text published" state - Fix "Introduced: —" showing for bills with null introduced_date - Add bills_missing_sponsor and bills_missing_metadata to admin stats - Add GovInfo health check using /collections endpoint (fixes 500 from /packages) Authored-By: Jack Levy
222 lines
7.1 KiB
Python
222 lines
7.1 KiB
Python
from datetime import date, datetime
|
|
from typing import Any, Generic, Optional, TypeVar
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# ── Notifications ──────────────────────────────────────────────────────────────
|
|
|
|
class NotificationSettingsResponse(BaseModel):
|
|
ntfy_topic_url: str = ""
|
|
ntfy_token: str = ""
|
|
ntfy_enabled: bool = False
|
|
rss_token: Optional[str] = None
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class NotificationSettingsUpdate(BaseModel):
|
|
ntfy_topic_url: Optional[str] = None
|
|
ntfy_token: Optional[str] = None
|
|
ntfy_enabled: Optional[bool] = None
|
|
|
|
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
|
|
latest_trend: Optional["MemberTrendScoreSchema"] = 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}
|
|
|
|
|
|
class MemberTrendScoreSchema(BaseModel):
|
|
score_date: date
|
|
newsapi_count: int
|
|
gnews_count: int
|
|
gtrends_score: float
|
|
composite_score: float
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class MemberNewsArticleSchema(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}
|
|
|
|
|
|
# ── 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] = []
|
|
has_document: bool = False
|
|
|
|
|
|
# ── 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
|