""" Migration script from MongoDB/SQLite to PostgreSQL This script migrates all data to the new PostgreSQL database """ import os import json from datetime import datetime from dotenv import load_dotenv load_dotenv() # Import PostgreSQL models from postgresql_models import ( get_db, init_db, Song, Profile, Plan, ProfileSong, PlanSong ) def migrate_from_json(json_path='data.json'): """Migrate data from data.json backup file""" print(f"šŸ“ Looking for {json_path}...") if not os.path.exists(json_path): backend_path = os.path.join('backend', json_path) if os.path.exists(backend_path): json_path = backend_path else: print(f"āŒ {json_path} not found") return False print(f"āœ… Found {json_path}") # Initialize database print("šŸ”§ Initializing PostgreSQL database...") init_db() db = get_db() try: with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) print(f"šŸ“Š Loaded data from {json_path}") # Migrate profiles profiles = data.get('profiles', []) print(f"\nšŸ‘„ Migrating {len(profiles)} profiles...") profile_id_map = {} # Old ID -> New ID mapping for p in profiles: name = p.get('name', '').strip() if not name: fname = p.get('first_name', '').strip() lname = p.get('last_name', '').strip() name = f"{fname} {lname}".strip() if not name: continue # Check if profile already exists existing = db.query(Profile).filter(Profile.name == name).first() if existing: print(f" ā­ļø Profile '{name}' already exists (ID: {existing.id})") profile_id_map[str(p.get('id', name))] = existing.id continue profile = Profile( name=name, email=p.get('email', ''), phone=p.get('contact_number', ''), role=p.get('role', 'Worship Leader'), notes=p.get('notes', '') ) db.add(profile) db.flush() # Get the ID without committing old_id = str(p.get('id', name)) profile_id_map[old_id] = profile.id print(f" āœ… Created profile: {name} (ID: {profile.id})") db.commit() print(f"āœ… Migrated {len(profile_id_map)} profiles") # Migrate songs songs = data.get('songs', []) print(f"\nšŸŽµ Migrating {len(songs)} songs...") song_id_map = {} # Old ID -> New ID mapping for s in songs: title = s.get('title', '').strip() if not title: continue # Check if song already exists existing = db.query(Song).filter(Song.title == title).first() if existing: print(f" ā­ļø Song '{title}' already exists (ID: {existing.id})") song_id_map[str(s.get('id', title))] = existing.id continue song = Song( title=title, artist=s.get('artist') or s.get('singer') or s.get('band') or 'Unknown', source=s.get('source', 'Manual'), lyrics=s.get('lyrics') or s.get('content') or '', chords=s.get('chords', ''), key=s.get('key', ''), tempo=s.get('tempo', ''), time_signature=s.get('time_signature', ''), notes=s.get('notes', ''), tags=s.get('tags', '') ) db.add(song) db.flush() old_id = str(s.get('id', title)) song_id_map[old_id] = song.id print(f" āœ… Created song: {title} (ID: {song.id})") db.commit() print(f"āœ… Migrated {len(song_id_map)} songs") # Migrate profile songs (if they exist in data) profile_songs = data.get('profile_songs', []) if profile_songs: print(f"\n⭐ Migrating {len(profile_songs)} profile-song links...") for ps in profile_songs: old_profile_id = str(ps.get('profile_id')) old_song_id = str(ps.get('song_id')) if old_profile_id in profile_id_map and old_song_id in song_id_map: profile_song = ProfileSong( profile_id=profile_id_map[old_profile_id], song_id=song_id_map[old_song_id] ) db.add(profile_song) db.commit() print(f"āœ… Migrated {len(profile_songs)} profile-song links") print("\n" + "="*50) print("āœ… Migration completed successfully!") print("="*50) print(f" Profiles: {len(profile_id_map)}") print(f" Songs: {len(song_id_map)}") print("="*50) return True except Exception as e: print(f"\nāŒ Migration failed: {e}") import traceback traceback.print_exc() db.rollback() return False finally: db.close() def migrate_from_mongodb(): """Migrate data from existing MongoDB database""" try: from mongodb_models import get_db as get_mongo_db print("šŸ”„ Attempting to migrate from MongoDB...") mongo_db = get_mongo_db() pg_db = get_db() # Migrate profiles mongo_profiles = list(mongo_db.profiles.find()) print(f"\nšŸ‘„ Migrating {len(mongo_profiles)} profiles from MongoDB...") profile_id_map = {} for mp in mongo_profiles: existing = pg_db.query(Profile).filter(Profile.name == mp.get('name')).first() if existing: profile_id_map[mp['_id']] = existing.id continue profile = Profile( name=mp.get('name', 'Unknown'), email=mp.get('email', ''), phone=mp.get('contact_number', ''), notes=mp.get('notes', '') ) pg_db.add(profile) pg_db.flush() profile_id_map[mp['_id']] = profile.id print(f" āœ… {profile.name}") pg_db.commit() # Migrate songs mongo_songs = list(mongo_db.songs.find()) print(f"\nšŸŽµ Migrating {len(mongo_songs)} songs from MongoDB...") song_id_map = {} for ms in mongo_songs: existing = pg_db.query(Song).filter(Song.title == ms.get('title')).first() if existing: song_id_map[ms['_id']] = existing.id continue song = Song( title=ms.get('title', 'Untitled'), artist=ms.get('artist') or ms.get('singer') or ms.get('band') or 'Unknown', lyrics=ms.get('lyrics', ''), chords=ms.get('chords', '') ) pg_db.add(song) pg_db.flush() song_id_map[ms['_id']] = song.id print(f" āœ… {song.title}") pg_db.commit() print("\nāœ… MongoDB migration completed!") pg_db.close() return True except ImportError: print("āš ļø MongoDB not available, skipping MongoDB migration") return False except Exception as e: print(f"āŒ MongoDB migration failed: {e}") return False if __name__ == '__main__': print("="*50) print("PostgreSQL Migration Script") print("="*50) print() # Try JSON migration first if migrate_from_json(): print("\nāœ… Migration from JSON successful!") else: print("\nāš ļø JSON migration skipped or failed") # Try MongoDB migration if migrate_from_mongodb(): print("\nāœ… Migration from MongoDB successful!") else: print("\nāš ļø No data sources available for migration") print("\nšŸ’” Next steps:") print(" 1. Verify data in PostgreSQL") print(" 2. Update backend/.env with PostgreSQL connection string") print(" 3. Start the Flask app with: python app.py")