Files
echoes-of-the-ash/migrations/migrate_unique_items_table.py
2025-11-07 15:27:13 +01:00

148 lines
6.3 KiB
Python

"""
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())