"""Dolt/SEC fundamentals schema — workstream A Revision ID: 026 Revises: 025 Create Date: 2026-07-21 00:00:00.000000 Foundational schema for the Dolt bulk-data integration (workstream A): the batch import-run audit table, the SEC-sourced immutable fundamental snapshots (CIK-keyed, one row per accession), the Dolt earnings calendar/history, and the SEC issuer identity columns on ``tickers``. No data is populated here — the importers land in a later phase. ``fundamental_data`` is left untouched; its cutover is gated separately (phase A5). ``data_import_runs`` is created first because the other two tables carry an ``import_run_id`` FK to it. """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa revision: str = "026" down_revision: Union[str, None] = "025" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( "data_import_runs", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("source", sa.String(length=32), nullable=False), sa.Column("revision", sa.String(length=64), nullable=True), sa.Column("status", sa.String(length=16), nullable=False), sa.Column("source_max_date", sa.Date(), nullable=True), sa.Column("row_counts_json", sa.Text(), nullable=True), sa.Column("validation_json", sa.Text(), nullable=True), sa.Column("started_at", sa.DateTime(timezone=True), nullable=False), sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True), sa.Column("error_details", sa.Text(), nullable=True), ) op.create_index( "ix_data_import_runs_source_started", "data_import_runs", ["source", "started_at"] ) op.create_table( "fundamental_snapshots", sa.Column("id", sa.Integer(), primary_key=True), sa.Column("cik", sa.String(length=10), nullable=False), sa.Column("accession", sa.String(length=25), nullable=False), sa.Column("form", sa.String(length=12), nullable=False), sa.Column("filed_date", sa.Date(), nullable=False), sa.Column("accepted_at", sa.DateTime(timezone=True), nullable=False), sa.Column("period_start", sa.Date(), nullable=True), sa.Column("period_end", sa.Date(), nullable=False), sa.Column("fiscal_year", sa.Integer(), nullable=False), sa.Column("fiscal_period", sa.String(length=4), nullable=False), # duration facts — cumulative YTD/FY sa.Column("revenue", sa.Float(), nullable=True), sa.Column("net_income", sa.Float(), nullable=True), sa.Column("operating_income", sa.Float(), nullable=True), sa.Column("diluted_eps", sa.Float(), nullable=True), sa.Column("cfo", sa.Float(), nullable=True), sa.Column("capex", sa.Float(), nullable=True), sa.Column("depreciation_amortization", sa.Float(), nullable=True), # balance-sheet facts — period-end sa.Column("cash_and_st_investments", sa.Float(), nullable=True), sa.Column("total_debt", sa.Float(), nullable=True), sa.Column("shares_outstanding", sa.Float(), nullable=True), sa.Column("shares_outstanding_date", sa.Date(), nullable=True), sa.Column( "import_run_id", sa.Integer(), sa.ForeignKey("data_import_runs.id", ondelete="SET NULL"), nullable=True, ), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.UniqueConstraint("accession", name="uq_fundamental_snapshots_accession"), ) op.create_index( "ix_fundamental_snapshots_cik_period", "fundamental_snapshots", ["cik", "fiscal_year", "fiscal_period"], ) op.create_index( "ix_fundamental_snapshots_cik_period_end", "fundamental_snapshots", ["cik", "period_end"], ) op.create_table( "earnings_events", sa.Column("id", sa.Integer(), primary_key=True), sa.Column( "ticker_id", sa.Integer(), sa.ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False, ), sa.Column("announce_date", sa.Date(), nullable=False), sa.Column("session", sa.String(length=10), nullable=False), sa.Column("period_end", sa.Date(), nullable=True), sa.Column("eps_estimate", sa.Float(), nullable=True), sa.Column("eps_actual", sa.Float(), nullable=True), sa.Column("source", sa.String(length=32), nullable=False), sa.Column( "import_run_id", sa.Integer(), sa.ForeignKey("data_import_runs.id", ondelete="SET NULL"), nullable=True, ), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), sa.UniqueConstraint("ticker_id", "announce_date", name="uq_earnings_ticker_announce"), ) op.create_index( "ix_earnings_events_announce_date", "earnings_events", ["announce_date"] ) # SEC issuer identity on tickers (nullable; the only ticker<->issuer join point). op.add_column("tickers", sa.Column("cik", sa.String(length=10), nullable=True)) op.add_column("tickers", sa.Column("sic", sa.String(length=4), nullable=True)) op.add_column( "tickers", sa.Column("sic_description", sa.String(length=160), nullable=True) ) def downgrade() -> None: op.drop_column("tickers", "sic_description") op.drop_column("tickers", "sic") op.drop_column("tickers", "cik") op.drop_index("ix_earnings_events_announce_date", table_name="earnings_events") op.drop_table("earnings_events") op.drop_index( "ix_fundamental_snapshots_cik_period_end", table_name="fundamental_snapshots" ) op.drop_index( "ix_fundamental_snapshots_cik_period", table_name="fundamental_snapshots" ) op.drop_table("fundamental_snapshots") op.drop_index( "ix_data_import_runs_source_started", table_name="data_import_runs" ) op.drop_table("data_import_runs")