Files
state-hub/migrations/versions/s6n7o8p9q0r1_workstream_planning_metadata.py

97 lines
3.6 KiB
Python

"""add workstream planning metadata and typed dependency targets
Revision ID: s6n7o8p9q0r1
Revises: r5m6n7o8p9q0
Create Date: 2026-05-04
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID
revision = "s6n7o8p9q0r1"
down_revision = "r5m6n7o8p9q0"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.add_column("workstreams", sa.Column("planning_priority", sa.String(length=20), nullable=True))
op.add_column("workstreams", sa.Column("planning_order", sa.Integer(), nullable=True))
op.create_index("ix_workstreams_planning_priority", "workstreams", ["planning_priority"])
op.create_index("ix_workstreams_planning_order", "workstreams", ["planning_order"])
op.drop_constraint("uq_ws_dep_pair", "workstream_dependencies", type_="unique")
op.add_column("workstream_dependencies", sa.Column("to_task_id", UUID(as_uuid=True), nullable=True))
op.add_column(
"workstream_dependencies",
sa.Column("relationship_type", sa.String(length=40), nullable=False, server_default="blocks"),
)
op.alter_column(
"workstream_dependencies",
"to_workstream_id",
existing_type=UUID(as_uuid=True),
nullable=True,
)
op.create_foreign_key(
"fk_ws_dep_to_task_id",
"workstream_dependencies",
"tasks",
["to_task_id"],
["id"],
ondelete="CASCADE",
)
op.create_index("ix_workstream_dependencies_to_task_id", "workstream_dependencies", ["to_task_id"])
op.create_index(
"ix_workstream_dependencies_relationship_type",
"workstream_dependencies",
["relationship_type"],
)
op.create_check_constraint(
"ck_ws_dep_exactly_one_target",
"workstream_dependencies",
"(to_workstream_id IS NOT NULL AND to_task_id IS NULL) "
"OR (to_workstream_id IS NULL AND to_task_id IS NOT NULL)",
)
op.create_index(
"uq_ws_dep_workstream_target",
"workstream_dependencies",
["from_workstream_id", "to_workstream_id", "relationship_type"],
unique=True,
postgresql_where=sa.text("to_workstream_id IS NOT NULL"),
)
op.create_index(
"uq_ws_dep_task_target",
"workstream_dependencies",
["from_workstream_id", "to_task_id", "relationship_type"],
unique=True,
postgresql_where=sa.text("to_task_id IS NOT NULL"),
)
def downgrade() -> None:
op.drop_index("uq_ws_dep_task_target", table_name="workstream_dependencies")
op.drop_index("uq_ws_dep_workstream_target", table_name="workstream_dependencies")
op.drop_constraint("ck_ws_dep_exactly_one_target", "workstream_dependencies", type_="check")
op.drop_index("ix_workstream_dependencies_relationship_type", table_name="workstream_dependencies")
op.drop_index("ix_workstream_dependencies_to_task_id", table_name="workstream_dependencies")
op.drop_constraint("fk_ws_dep_to_task_id", "workstream_dependencies", type_="foreignkey")
op.alter_column(
"workstream_dependencies",
"to_workstream_id",
existing_type=UUID(as_uuid=True),
nullable=False,
)
op.drop_column("workstream_dependencies", "relationship_type")
op.drop_column("workstream_dependencies", "to_task_id")
op.create_unique_constraint(
"uq_ws_dep_pair",
"workstream_dependencies",
["from_workstream_id", "to_workstream_id"],
)
op.drop_index("ix_workstreams_planning_order", table_name="workstreams")
op.drop_index("ix_workstreams_planning_priority", table_name="workstreams")
op.drop_column("workstreams", "planning_order")
op.drop_column("workstreams", "planning_priority")