""" Migration: Create unique_items table and refactor item tracking This creates a proper architecture where: 1. unique_items table stores individual item instances with their properties 2. inventory/dropped_items reference unique_item_id instead of duplicating data 3. When item is picked up, only the reference changes (dropped_items -> inventory) 4. When item decays/breaks, delete from unique_items (cascades to references) """ import asyncio from api.database import DatabaseSession, engine, metadata from sqlalchemy import text async def migrate(): """Create unique_items table and refactor references""" async with DatabaseSession() as session: print("Starting migration: Create unique_items table...") # Step 1: Create unique_items table try: await session.execute(text(""" CREATE TABLE IF NOT EXISTS unique_items ( id SERIAL PRIMARY KEY, item_id VARCHAR NOT NULL, durability INTEGER, max_durability INTEGER, tier INTEGER DEFAULT 1, unique_stats JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """)) print("✓ Created unique_items table") except Exception as e: print(f"✗ Error creating unique_items table: {e}") return # Step 2: Add unique_item_id to inventory (nullable for now) try: await session.execute(text( "ALTER TABLE inventory ADD COLUMN IF NOT EXISTS unique_item_id INTEGER REFERENCES unique_items(id) ON DELETE CASCADE" )) print("✓ Added unique_item_id to inventory") except Exception as e: print(f"✗ unique_item_id may already exist in inventory: {e}") # Step 3: Add unique_item_id to dropped_items (nullable for now) try: await session.execute(text( "ALTER TABLE dropped_items ADD COLUMN IF NOT EXISTS unique_item_id INTEGER REFERENCES unique_items(id) ON DELETE CASCADE" )) print("✓ Added unique_item_id to dropped_items") except Exception as e: print(f"✗ unique_item_id may already exist in dropped_items: {e}") # Step 4: Migrate existing inventory items with durability to unique_items print("\nMigrating existing inventory items to unique_items...") result = await session.execute(text(""" SELECT id, item_id, durability, max_durability, tier, unique_stats FROM inventory WHERE durability IS NOT NULL OR tier IS NOT NULL OR unique_stats IS NOT NULL """)) inventory_items = result.fetchall() migrated_count = 0 for inv_item in inventory_items: # Create unique_item entry result = await session.execute(text(""" INSERT INTO unique_items (item_id, durability, max_durability, tier, unique_stats) VALUES (:item_id, :durability, :max_durability, :tier, :unique_stats) RETURNING id """), { 'item_id': inv_item.item_id, 'durability': inv_item.durability, 'max_durability': inv_item.max_durability, 'tier': inv_item.tier, 'unique_stats': inv_item.unique_stats }) unique_item_id = result.fetchone()[0] # Update inventory to reference it await session.execute(text(""" UPDATE inventory SET unique_item_id = :unique_item_id WHERE id = :inv_id """), { 'unique_item_id': unique_item_id, 'inv_id': inv_item.id }) migrated_count += 1 print(f"✓ Migrated {migrated_count} inventory items to unique_items") # Step 5: Migrate existing dropped_items with durability to unique_items print("\nMigrating existing dropped items to unique_items...") result = await session.execute(text(""" SELECT id, item_id, durability, max_durability, tier, unique_stats FROM dropped_items WHERE durability IS NOT NULL OR tier IS NOT NULL OR unique_stats IS NOT NULL """)) dropped_items_list = result.fetchall() migrated_dropped = 0 for dropped_item in dropped_items_list: # Create unique_item entry result = await session.execute(text(""" INSERT INTO unique_items (item_id, durability, max_durability, tier, unique_stats) VALUES (:item_id, :durability, :max_durability, :tier, :unique_stats) RETURNING id """), { 'item_id': dropped_item.item_id, 'durability': dropped_item.durability, 'max_durability': dropped_item.max_durability, 'tier': dropped_item.tier, 'unique_stats': dropped_item.unique_stats }) unique_item_id = result.fetchone()[0] # Update dropped_items to reference it await session.execute(text(""" UPDATE dropped_items SET unique_item_id = :unique_item_id WHERE id = :dropped_id """), { 'unique_item_id': unique_item_id, 'dropped_id': dropped_item.id }) migrated_dropped += 1 print(f"✓ Migrated {migrated_dropped} dropped items to unique_items") # Step 6: Drop old columns from inventory (keep for backward compatibility for now) # We'll drop these in a future migration after verifying everything works print("\n⚠️ Old durability/tier columns still exist for backward compatibility") print(" They can be safely removed in a future migration") await session.commit() print("\n✅ Migration completed successfully!") print(f"\n📊 Summary:") print(f" - Created unique_items table") print(f" - Migrated {migrated_count} inventory items") print(f" - Migrated {migrated_dropped} dropped items") print(f" - Total unique items: {migrated_count + migrated_dropped}") if __name__ == "__main__": asyncio.run(migrate())