diff --git a/migrations/versions/2937635f309a_add_turn_tracking_fields_to_game_model.py b/migrations/versions/2937635f309a_add_turn_tracking_fields_to_game_model.py index d54e837..ea75b21 100644 --- a/migrations/versions/2937635f309a_add_turn_tracking_fields_to_game_model.py +++ b/migrations/versions/2937635f309a_add_turn_tracking_fields_to_game_model.py @@ -17,12 +17,22 @@ depends_on = None def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - # Split into two batch operations to avoid SQLite circular dependency error - with op.batch_alter_table('games', schema=None) as batch_op: - batch_op.add_column(sa.Column('current_turn_team_id', sa.Integer(), nullable=True)) - batch_op.add_column(sa.Column('turn_order', sa.JSON(), nullable=True)) - batch_op.add_column(sa.Column('is_steal_mode', sa.Boolean(), nullable=True)) + # Check which columns already exist to make migration idempotent + conn = op.get_bind() + columns = [row[1] for row in conn.execute(sa.text("PRAGMA table_info('games')"))] + + cols_to_add = [] + if 'current_turn_team_id' not in columns: + cols_to_add.append(sa.Column('current_turn_team_id', sa.Integer(), nullable=True)) + if 'turn_order' not in columns: + cols_to_add.append(sa.Column('turn_order', sa.JSON(), nullable=True)) + if 'is_steal_mode' not in columns: + cols_to_add.append(sa.Column('is_steal_mode', sa.Boolean(), nullable=True)) + + if cols_to_add: + with op.batch_alter_table('games', schema=None) as batch_op: + for col in cols_to_add: + batch_op.add_column(col) with op.batch_alter_table('games', schema=None) as batch_op: batch_op.create_foreign_key('fk_games_current_turn_team', 'teams', ['current_turn_team_id'], ['id']) diff --git a/migrations/versions/9a119272b516_add_question_ownership_and_sharing.py b/migrations/versions/9a119272b516_add_question_ownership_and_sharing.py index c066b54..ece56b7 100644 --- a/migrations/versions/9a119272b516_add_question_ownership_and_sharing.py +++ b/migrations/versions/9a119272b516_add_question_ownership_and_sharing.py @@ -18,31 +18,52 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('question_shares', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('question_id', sa.Integer(), nullable=False), - sa.Column('shared_with_user_id', sa.Integer(), nullable=False), - sa.Column('shared_by_user_id', sa.Integer(), nullable=False), - sa.Column('created_at', sa.DateTime(), nullable=True), - sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ), - sa.ForeignKeyConstraint(['shared_by_user_id'], ['users.id'], ), - sa.ForeignKeyConstraint(['shared_with_user_id'], ['users.id'], ), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('question_id', 'shared_with_user_id', name='unique_question_share') - ) - # Split into separate batch operations to avoid SQLite circular dependency - # with the existing current_turn_team_id foreign key - with op.batch_alter_table('games', schema=None) as batch_op: - batch_op.add_column(sa.Column('completed_at', sa.DateTime(), nullable=True)) - batch_op.add_column(sa.Column('winners', sa.JSON(), nullable=True)) + conn = op.get_bind() - with op.batch_alter_table('games', schema=None) as batch_op: - batch_op.drop_column('turn_order') - batch_op.drop_column('is_steal_mode') + # Create question_shares table if it doesn't exist + tables = [row[0] for row in conn.execute(sa.text("SELECT name FROM sqlite_master WHERE type='table'"))] + if 'question_shares' not in tables: + op.create_table('question_shares', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('question_id', sa.Integer(), nullable=False), + sa.Column('shared_with_user_id', sa.Integer(), nullable=False), + sa.Column('shared_by_user_id', sa.Integer(), nullable=False), + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(['question_id'], ['questions.id'], ), + sa.ForeignKeyConstraint(['shared_by_user_id'], ['users.id'], ), + sa.ForeignKeyConstraint(['shared_with_user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('question_id', 'shared_with_user_id', name='unique_question_share') + ) - with op.batch_alter_table('questions', schema=None) as batch_op: - batch_op.add_column(sa.Column('created_by', sa.Integer(), nullable=True)) - batch_op.create_foreign_key('fk_questions_created_by', 'users', ['created_by'], ['id']) + # Check existing columns to make migration idempotent + game_columns = [row[1] for row in conn.execute(sa.text("PRAGMA table_info('games')"))] + question_columns = [row[1] for row in conn.execute(sa.text("PRAGMA table_info('questions')"))] + + # Add new game columns if missing + game_cols_to_add = [] + if 'completed_at' not in game_columns: + game_cols_to_add.append(sa.Column('completed_at', sa.DateTime(), nullable=True)) + if 'winners' not in game_columns: + game_cols_to_add.append(sa.Column('winners', sa.JSON(), nullable=True)) + + if game_cols_to_add: + with op.batch_alter_table('games', schema=None) as batch_op: + for col in game_cols_to_add: + batch_op.add_column(col) + + # Drop old game columns if they still exist + game_cols_to_drop = [c for c in ['turn_order', 'is_steal_mode'] if c in game_columns] + if game_cols_to_drop: + with op.batch_alter_table('games', schema=None) as batch_op: + for col_name in game_cols_to_drop: + batch_op.drop_column(col_name) + + # Add created_by to questions if missing + if 'created_by' not in question_columns: + with op.batch_alter_table('questions', schema=None) as batch_op: + batch_op.add_column(sa.Column('created_by', sa.Integer(), nullable=True)) + batch_op.create_foreign_key('fk_questions_created_by', 'users', ['created_by'], ['id']) # ### end Alembic commands ###