from sqlalchemy import ( Column, String, Integer, Date, DateTime, Text, ForeignKey, Index, UniqueConstraint ) from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.database import Base class Bill(Base): __tablename__ = "bills" # Natural key: "{congress}-{bill_type_lower}-{bill_number}" e.g. "119-hr-1234" bill_id = Column(String, primary_key=True) congress_number = Column(Integer, nullable=False) bill_type = Column(String(10), nullable=False) # hr, s, hjres, sjres, hconres, sconres, hres, sres bill_number = Column(Integer, nullable=False) title = Column(Text) short_title = Column(Text) sponsor_id = Column(String, ForeignKey("members.bioguide_id"), nullable=True) introduced_date = Column(Date) latest_action_date = Column(Date) latest_action_text = Column(Text) status = Column(String(100)) chamber = Column(String(50)) congress_url = Column(String) govtrack_url = Column(String) bill_category = Column(String(20), nullable=True) # substantive | commemorative | administrative cosponsors_fetched_at = Column(DateTime(timezone=True)) # Ingestion tracking last_checked_at = Column(DateTime(timezone=True)) actions_fetched_at = Column(DateTime(timezone=True)) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) sponsor = relationship("Member", back_populates="bills", foreign_keys=[sponsor_id]) actions = relationship("BillAction", back_populates="bill", order_by="desc(BillAction.action_date)") documents = relationship("BillDocument", back_populates="bill") briefs = relationship("BillBrief", back_populates="bill", order_by="desc(BillBrief.created_at)") news_articles = relationship("NewsArticle", back_populates="bill", order_by="desc(NewsArticle.published_at)") trend_scores = relationship("TrendScore", back_populates="bill", order_by="desc(TrendScore.score_date)") committee_bills = relationship("CommitteeBill", back_populates="bill") notes = relationship("BillNote", back_populates="bill", cascade="all, delete-orphan") cosponsors = relationship("BillCosponsor", back_populates="bill", cascade="all, delete-orphan") __table_args__ = ( Index("ix_bills_congress_number", "congress_number"), Index("ix_bills_latest_action_date", "latest_action_date"), Index("ix_bills_introduced_date", "introduced_date"), Index("ix_bills_chamber", "chamber"), Index("ix_bills_sponsor_id", "sponsor_id"), ) class BillAction(Base): __tablename__ = "bill_actions" id = Column(Integer, primary_key=True, autoincrement=True) bill_id = Column(String, ForeignKey("bills.bill_id", ondelete="CASCADE"), nullable=False) action_date = Column(Date) action_text = Column(Text) action_type = Column(String(100)) chamber = Column(String(50)) created_at = Column(DateTime(timezone=True), server_default=func.now()) bill = relationship("Bill", back_populates="actions") __table_args__ = ( Index("ix_bill_actions_bill_id", "bill_id"), Index("ix_bill_actions_action_date", "action_date"), ) class BillDocument(Base): __tablename__ = "bill_documents" id = Column(Integer, primary_key=True, autoincrement=True) bill_id = Column(String, ForeignKey("bills.bill_id", ondelete="CASCADE"), nullable=False) doc_type = Column(String(50)) # bill_text | committee_report | amendment doc_version = Column(String(50)) # Introduced, Enrolled, etc. govinfo_url = Column(String) raw_text = Column(Text) fetched_at = Column(DateTime(timezone=True)) created_at = Column(DateTime(timezone=True), server_default=func.now()) bill = relationship("Bill", back_populates="documents") briefs = relationship("BillBrief", back_populates="document") __table_args__ = ( Index("ix_bill_documents_bill_id", "bill_id"), ) class BillCosponsor(Base): __tablename__ = "bill_cosponsors" id = Column(Integer, primary_key=True, autoincrement=True) bill_id = Column(String, ForeignKey("bills.bill_id", ondelete="CASCADE"), nullable=False) bioguide_id = Column(String, ForeignKey("members.bioguide_id", ondelete="SET NULL"), nullable=True) name = Column(String(200)) party = Column(String(50)) state = Column(String(10)) sponsored_date = Column(Date, nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) bill = relationship("Bill", back_populates="cosponsors") __table_args__ = ( Index("ix_bill_cosponsors_bill_id", "bill_id"), Index("ix_bill_cosponsors_bioguide_id", "bioguide_id"), )