"""Add the independent shared-library collaboration module. Revision ID: 20260714_01 Revises: 20260713_02 """ import sqlalchemy as sa from alembic import op from sqlalchemy.dialects import postgresql revision = "20260714_01" down_revision = "20260713_02" branch_labels = None depends_on = None def upgrade() -> None: uuid_type = postgresql.UUID(as_uuid=True) op.create_table( "collaboration_folders", sa.Column("id", uuid_type, primary_key=True), sa.Column("study_id", uuid_type, sa.ForeignKey("studies.id"), nullable=False), sa.Column("parent_id", uuid_type, sa.ForeignKey("collaboration_folders.id", ondelete="SET NULL")), sa.Column("name", sa.String(120), nullable=False), sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"), sa.Column("created_by", uuid_type, sa.ForeignKey("users.id"), nullable=False), sa.Column("deleted_at", sa.DateTime(timezone=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_collaboration_folders_study_parent", "collaboration_folders", ["study_id", "parent_id"]) op.create_table( "collaboration_files", sa.Column("id", uuid_type, primary_key=True), sa.Column("study_id", uuid_type, sa.ForeignKey("studies.id"), nullable=False), sa.Column("folder_id", uuid_type, sa.ForeignKey("collaboration_folders.id", ondelete="SET NULL")), sa.Column("title", sa.String(255), nullable=False), sa.Column("file_type", sa.String(16), nullable=False), sa.Column("extension", sa.String(16), nullable=False), sa.Column("status", sa.String(20), nullable=False, server_default="ACTIVE"), sa.Column("owner_id", uuid_type, sa.ForeignKey("users.id"), nullable=False), sa.Column("current_revision_id", uuid_type), sa.Column("generation", sa.Integer(), nullable=False, server_default="1"), sa.Column("deleted_at", sa.DateTime(timezone=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_collaboration_files_study_folder", "collaboration_files", ["study_id", "folder_id"]) op.create_index("ix_collaboration_files_study_status", "collaboration_files", ["study_id", "status"]) op.create_table( "collaboration_members", sa.Column("id", uuid_type, primary_key=True), sa.Column("file_id", uuid_type, sa.ForeignKey("collaboration_files.id", ondelete="CASCADE"), nullable=False), sa.Column("user_id", uuid_type, sa.ForeignKey("users.id"), nullable=False), sa.Column("role", sa.String(16), nullable=False), sa.Column("invited_by", uuid_type, sa.ForeignKey("users.id"), nullable=False), 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()), sa.UniqueConstraint("file_id", "user_id", name="uq_collaboration_member_file_user"), ) op.create_index("ix_collaboration_members_user", "collaboration_members", ["user_id"]) op.create_table( "collaboration_revisions", sa.Column("id", uuid_type, primary_key=True), sa.Column("file_id", uuid_type, sa.ForeignKey("collaboration_files.id", ondelete="CASCADE"), nullable=False), sa.Column("revision_no", sa.Integer(), nullable=False), sa.Column("parent_revision_id", uuid_type, sa.ForeignKey("collaboration_revisions.id", ondelete="SET NULL")), sa.Column("file_uri", sa.String(500), nullable=False), sa.Column("original_filename", sa.String(255), nullable=False), sa.Column("file_hash", sa.String(128), nullable=False), sa.Column("file_size", sa.BigInteger(), nullable=False), sa.Column("mime_type", sa.String(100), nullable=False), sa.Column("source", sa.String(24), nullable=False), sa.Column("change_summary", sa.Text()), sa.Column("created_by", uuid_type, sa.ForeignKey("users.id")), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint("file_id", "revision_no", name="uq_collaboration_revision_file_no"), ) op.create_index("ix_collaboration_revisions_file_created", "collaboration_revisions", ["file_id", "created_at"]) op.create_foreign_key( "fk_collaboration_files_current_revision", "collaboration_files", "collaboration_revisions", ["current_revision_id"], ["id"], ondelete="SET NULL", ) op.create_table( "collaboration_sessions", sa.Column("id", uuid_type, primary_key=True), sa.Column("file_id", uuid_type, sa.ForeignKey("collaboration_files.id", ondelete="CASCADE"), nullable=False), sa.Column("base_revision_id", uuid_type, sa.ForeignKey("collaboration_revisions.id"), nullable=False), sa.Column("document_key", sa.String(128), nullable=False), sa.Column("generation", sa.Integer(), nullable=False), sa.Column("status", sa.String(20), nullable=False, server_default="ACTIVE"), sa.Column("started_by", uuid_type, sa.ForeignKey("users.id"), nullable=False), sa.Column("active_users", sa.Text()), sa.Column("last_callback_at", sa.DateTime(timezone=True)), sa.Column("closed_at", sa.DateTime(timezone=True)), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint("document_key", name="uq_collaboration_session_document_key"), ) op.create_index("ix_collaboration_sessions_file_status", "collaboration_sessions", ["file_id", "status"]) op.create_table( "collaboration_callback_receipts", sa.Column("id", uuid_type, primary_key=True), sa.Column("session_id", uuid_type, sa.ForeignKey("collaboration_sessions.id", ondelete="CASCADE"), nullable=False), sa.Column("fingerprint", sa.String(128), nullable=False), sa.Column("callback_status", sa.Integer(), nullable=False), sa.Column("result", sa.String(24), nullable=False), sa.Column("revision_id", uuid_type, sa.ForeignKey("collaboration_revisions.id", ondelete="SET NULL")), sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.UniqueConstraint("session_id", "fingerprint", name="uq_collaboration_callback_session_fingerprint"), ) def downgrade() -> None: op.drop_table("collaboration_callback_receipts") op.drop_index("ix_collaboration_sessions_file_status", table_name="collaboration_sessions") op.drop_table("collaboration_sessions") op.drop_constraint("fk_collaboration_files_current_revision", "collaboration_files", type_="foreignkey") op.drop_index("ix_collaboration_revisions_file_created", table_name="collaboration_revisions") op.drop_table("collaboration_revisions") op.drop_index("ix_collaboration_members_user", table_name="collaboration_members") op.drop_table("collaboration_members") op.drop_index("ix_collaboration_files_study_status", table_name="collaboration_files") op.drop_index("ix_collaboration_files_study_folder", table_name="collaboration_files") op.drop_table("collaboration_files") op.drop_index("ix_collaboration_folders_study_parent", table_name="collaboration_folders") op.drop_table("collaboration_folders")