zcbot/db/migrations/versions/20260819_0900_0035_software...

126 lines
4.5 KiB
Python

"""Add persistent professional-software workspaces and job lineage.
Revision ID: 0035
Revises: 0034
Create Date: 2026-08-19
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
revision: str = "0035"
down_revision: str | None = "0034"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.create_table(
"software_workspaces",
sa.Column("workspace_id", postgresql.UUID(as_uuid=True), primary_key=True),
sa.Column(
"user_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("users.user_id", ondelete="CASCADE"),
nullable=False,
),
sa.Column("capability", sa.Text(), nullable=False),
sa.Column(
"home_node_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey("software_nodes.node_id", ondelete="SET NULL"),
nullable=True,
),
sa.Column(
"head_job_id",
postgresql.UUID(as_uuid=True),
sa.ForeignKey(
"software_jobs.job_id",
name="fk_software_workspaces_head_job_id",
ondelete="SET NULL",
),
nullable=True,
),
sa.Column("status", sa.Text(), nullable=False, server_default="pending"),
sa.Column("size_bytes", sa.BigInteger(), nullable=False, server_default="0"),
sa.Column("state_manifest", postgresql.JSONB(), nullable=False, server_default="[]"),
sa.Column("last_used_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("last_reported_at", sa.DateTime(timezone=True), nullable=True),
sa.Column("retention_until", sa.DateTime(timezone=True), nullable=True),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
)
op.create_index(
"ix_software_workspaces_user_status",
"software_workspaces",
["user_id", "status"],
)
op.create_index(
"ix_software_workspaces_node_status",
"software_workspaces",
["home_node_id", "status"],
)
op.add_column(
"software_jobs",
sa.Column("workspace_id", postgresql.UUID(as_uuid=True), nullable=True),
)
op.add_column(
"software_jobs",
sa.Column("source_job_id", postgresql.UUID(as_uuid=True), nullable=True),
)
op.add_column(
"software_jobs",
sa.Column("local_manifest", postgresql.JSONB(), nullable=False, server_default="[]"),
)
op.add_column(
"software_jobs",
sa.Column("preview_manifest", postgresql.JSONB(), nullable=False, server_default="[]"),
)
op.add_column(
"software_jobs",
sa.Column("export_status", sa.Text(), nullable=False, server_default="none"),
)
op.add_column(
"software_jobs",
sa.Column("export_outputs", postgresql.JSONB(), nullable=False, server_default="[]"),
)
op.create_foreign_key(
"fk_software_jobs_workspace_id",
"software_jobs",
"software_workspaces",
["workspace_id"],
["workspace_id"],
ondelete="SET NULL",
)
op.create_foreign_key(
"fk_software_jobs_source_job_id",
"software_jobs",
"software_jobs",
["source_job_id"],
["job_id"],
ondelete="SET NULL",
)
op.create_index("ix_software_jobs_workspace_created", "software_jobs", ["workspace_id", "created_at"])
def downgrade() -> None:
op.drop_index("ix_software_jobs_workspace_created", table_name="software_jobs")
op.drop_constraint("fk_software_jobs_source_job_id", "software_jobs", type_="foreignkey")
op.drop_constraint("fk_software_jobs_workspace_id", "software_jobs", type_="foreignkey")
op.drop_column("software_jobs", "export_outputs")
op.drop_column("software_jobs", "export_status")
op.drop_column("software_jobs", "preview_manifest")
op.drop_column("software_jobs", "local_manifest")
op.drop_column("software_jobs", "source_job_id")
op.drop_column("software_jobs", "workspace_id")
op.drop_constraint(
"fk_software_workspaces_head_job_id",
"software_workspaces",
type_="foreignkey",
)
op.drop_index("ix_software_workspaces_node_status", table_name="software_workspaces")
op.drop_index("ix_software_workspaces_user_status", table_name="software_workspaces")
op.drop_table("software_workspaces")