"""Create independent market-data integrity check and issue tables.""" from collections.abc import Sequence import sqlalchemy as sa from alembic import op revision: str = "0003_market_integrity_checks" down_revision: str | None = "0002_selection_results" branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None def upgrade() -> None: """Create only check metadata; market facts remain untouched.""" op.create_table( "market_integrity_check", sa.Column("id", sa.String(36), primary_key=True), sa.Column("status", sa.String(24), nullable=False), sa.Column("window_start", sa.Date()), sa.Column("window_end", sa.Date()), sa.Column("target_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("checked_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("issue_count", sa.Integer(), nullable=False, server_default="0"), sa.Column("error_type", sa.String(64)), sa.Column("error_message", sa.Text()), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.Column( "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.Column("finished_at", sa.DateTime(timezone=True)), ) op.create_index( "ix_market_integrity_check_status_created_at", "market_integrity_check", ["status", "created_at"], ) # A partial unique index makes the running claim atomic under concurrent # POST requests; stale recovery first moves an abandoned row to failed. op.create_index( "uq_market_integrity_check_running", "market_integrity_check", ["status"], unique=True, postgresql_where=sa.text("status = 'running'"), ) op.create_table( "market_integrity_issue", sa.Column( "check_id", sa.String(36), sa.ForeignKey("market_integrity_check.id", ondelete="CASCADE"), nullable=False, ), sa.Column("issue_key", sa.String(64), nullable=False), sa.Column("item_kind", sa.String(24), nullable=False), sa.Column("item_key", sa.String(128), nullable=False), sa.Column("issue_type", sa.String(64), nullable=False), sa.Column("message", sa.Text(), nullable=False), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), sa.PrimaryKeyConstraint("check_id", "issue_key"), ) op.create_index("ix_market_integrity_issue_check_id", "market_integrity_issue", ["check_id"]) def downgrade() -> None: """Drop only the integrity report tables in dependency-safe order.""" op.drop_index("ix_market_integrity_issue_check_id", table_name="market_integrity_issue") op.drop_table("market_integrity_issue") op.drop_index("uq_market_integrity_check_running", table_name="market_integrity_check") op.drop_index( "ix_market_integrity_check_status_created_at", table_name="market_integrity_check", ) op.drop_table("market_integrity_check")