#!/usr/bin/env python3 """ Fix MTG sets endpoint - File-based changes only. This script: 1. Reorders routes in card_router.py so /sets comes before /{card_name} 2. Adds image_url column to MtgSet model 3. Updates service layer to return image_url """ import re from pathlib import Path def fix_route_ordering(): """Reorder routes in card_router.py so /sets comes before /{card_name}.""" route_file = Path('/home/wall-o/projects/mtgonline/backend/app/routers/card_router.py') content = route_file.read_text() # Find the current route order # The issue: /{card_name} is defined before /sets # We need to move /sets and /sets/{set_code} before /{card_name} # Current order (problematic): # 1. /search # 2. /{card_name} # 3. /set/{set_code} # 4. /types # 5. /rarities # 6. /sets # 7. /sets/{set_code} # 8. /statistics # New order (correct): # 1. /search # 2. /set/{set_code} # 3. /types # 4. /rarities # 5. /sets # 6. /sets/{set_code} # 7. /statistics # 8. /{card_name} (wildcard last) # Extract route blocks using regex # Pattern to find decorator + function pattern = r'(@router\.\w+\([^)]+\)\s*\nasync def \w+.*?)(?=@router\.\w+\(|$)' matches = re.findall(pattern, content, re.DOTALL) # Identify each route routes = {} for match in matches: # Extract route path route_match = re.search(r'@router\.(\w+)\(([^)]+)\)', match) if route_match: method = route_match.group(1) args = route_match.group(2) routes[f"{method}:{args}"] = match print("Current routes:") for key in routes: print(f" {key}") # Define the correct order correct_order = [ "get:/search", "get:/set/{set_code}", "get:/types", "get:/rarities", "get:/sets", "get:/sets/{set_code}", "get:/statistics", "get:/{card_name}", ] # Verify all routes are present for route_key in correct_order: if route_key not in routes: print(f"✗ Missing route: {route_key}") return False print("✓ All routes found") # Rebuild the file content new_content = content[:content.index('@router.get("/search")')] for route_key in correct_order: new_content += routes[route_key] # Add the router variable new_content += "\n\nrouter = APIRouter(prefix=\"/mtg/cards\", tags=[\"MTG Cards\"])\n" route_file.write_text(new_content) print("✓ Routes reordered") def update_mtg_set_model(): """Update MtgSet model to include image_url.""" model_file = Path('/home/wall-o/projects/mtgonline/backend/app/models/mtg_models.py') content = model_file.read_text() # Add image_url column after mtgo_code if 'image_url' in content: print("✓ image_url already in MtgSet model") return # Find the Mtgo_code line insert_point = "mtgo_code = Column(String(10), nullable=True)" new_column = '\n image_url = Column(Text, nullable=True)' content = content.replace(insert_point, insert_point + new_column) model_file.write_text(content) print("✓ image_url added to MtgSet model") def update_service_layer(): """Update service layer to return image_url.""" service_file = Path('/home/wall-o/projects/mtgonline/backend/app/services/card_database.py') content = service_file.read_text() if 'image_url' not in content: # Update get_sets() function old_get_sets = ''' stmt = select(MtgSet).order_by(MtgSet.release_date.desc()) result = await db.execute(stmt) rows = result.fetchall() return [ { "id": s.id, "code": s.code, "name": s.name, "release_date": s.release_date.isoformat() if s.release_date else None, "total_size": s.total_size, "base_set_size": s.base_set_size, } for s in rows ]''' new_get_sets = ''' stmt = select(MtgSet).order_by(MtgSet.release_date.desc()) result = await db.execute(stmt) rows = result.fetchall() return [ { "id": s.id, "code": s.code, "name": s.name, "release_date": s.release_date.isoformat() if s.release_date else None, "total_size": s.total_size, "base_set_size": s.base_set_size, "image_url": s.image_url, } for s in rows ]''' if old_get_sets in content: content = content.replace(old_get_sets, new_get_sets) print("✓ Updated get_sets() to include image_url") # Update get_set_by_code() function old_get_by_code = ''' return { "id": mtg_set.id, "code": mtg_set.code, "name": mtg_set.name, "type": mtg_set.type, "release_date": mtg_set.release_date.isoformat() if mtg_set.release_date else None, "base_set_size": mtg_set.base_set_size, "total_size": mtg_set.total_size, "is_foil_only": mtg_set.is_foil_only, "is_non_foil_only": mtg_set.is_non_foil_only, "digital": mtg_set.digital, "icon_svg_url": mtg_set.icon_svg_url, "parent_code": mtg_set.parent_code, "mtgo_code": mtg_set.mtgo_code, }''' new_get_by_code = ''' return { "id": mtg_set.id, "code": mtg_set.code, "name": mtg_set.name, "type": mtg_set.type, "release_date": mtg_set.release_date.isoformat() if mtg_set.release_date else None, "base_set_size": mtg_set.base_set_size, "total_size": mtg_set.total_size, "is_foil_only": mtg_set.is_foil_only, "is_non_foil_only": mtg_set.is_non_foil_only, "digital": mtg_set.digital, "icon_svg_url": mtg_set.icon_svg_url, "parent_code": mtg_set.parent_code, "mtgo_code": mtg_set.mtgo_code, "image_url": mtg_set.image_url, }''' if old_get_by_code in content: content = content.replace(old_get_by_code, new_get_by_code) print("✓ Updated get_set_by_code() to include image_url") service_file.write_text(content) def main(): """Run all file-based fixes.""" print("=== MTG SETS ENDPOINT FIX (FILE CHANGES) ===\n") # 1. Fix route ordering print("1. Route Reordering") fix_route_ordering() print() # 2. Update model print("2. Model Update") update_mtg_set_model() print() # 3. Update service layer print("3. Service Layer Update") update_service_layer() print() print("=== FILE CHANGES COMPLETE ===") print("\nNext steps:") print("1. Add image_url column to mtg_sets table in database") print("2. Restart the backend Docker container") print("3. Test /api/mtg/cards/sets endpoint") print("4. Run refresh_mtg.py to populate image_url data") print("5. Test /api/mtg/cards/sets/{set_code} endpoint") if __name__ == "__main__": main()