""" Migrate notifications from legacy database. This script migrates notification data from the VBScript database to the new notifications plugin schema. Usage: python -m scripts.migration.migrate_notifications --source """ import argparse import logging from datetime import datetime from sqlalchemy import create_engine, text from sqlalchemy.orm import sessionmaker logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) def get_notification_type_mapping(target_session): """Get mapping of type names to IDs in target database.""" result = target_session.execute(text( "SELECT notificationtypeid, typename FROM notificationtypes" )) return {row.typename.lower(): row.notificationtypeid for row in result} def run_migration(source_conn_str, target_conn_str, dry_run=False): """ Run notification migration. Args: source_conn_str: Source database connection string target_conn_str: Target database connection string dry_run: If True, don't commit changes """ source_engine = create_engine(source_conn_str) target_engine = create_engine(target_conn_str) SourceSession = sessionmaker(bind=source_engine) TargetSession = sessionmaker(bind=target_engine) source_session = SourceSession() target_session = TargetSession() try: # Get type mappings type_mapping = get_notification_type_mapping(target_session) # Default type if not found default_type_id = type_mapping.get('general', 1) # Fetch notifications from source # Adjust column names based on actual legacy schema notifications = source_session.execute(text(""" SELECT n.*, nt.typename FROM notifications n LEFT JOIN notificationtypes nt ON n.notificationtypeid = nt.notificationtypeid """)) migrated = 0 errors = 0 for notif in notifications: notif_dict = dict(notif._mapping) try: # Map notification type type_name = (notif_dict.get('typename') or 'general').lower() type_id = type_mapping.get(type_name, default_type_id) # Insert into target target_session.execute(text(""" INSERT INTO notifications ( title, message, notificationtypeid, startdate, enddate, ispinned, showbanner, allday, linkurl, affectedsystems, isactive, createddate ) VALUES ( :title, :message, :notificationtypeid, :startdate, :enddate, :ispinned, :showbanner, :allday, :linkurl, :affectedsystems, :isactive, :createddate ) """), { 'title': notif_dict.get('title', 'Untitled'), 'message': notif_dict.get('message', ''), 'notificationtypeid': type_id, 'startdate': notif_dict.get('startdate', datetime.utcnow()), 'enddate': notif_dict.get('enddate'), 'ispinned': notif_dict.get('ispinned', False), 'showbanner': notif_dict.get('showbanner', True), 'allday': notif_dict.get('allday', True), 'linkurl': notif_dict.get('linkurl'), 'affectedsystems': notif_dict.get('affectedsystems'), 'isactive': notif_dict.get('isactive', True), 'createddate': notif_dict.get('createddate', datetime.utcnow()), }) migrated += 1 except Exception as e: logger.error(f"Error migrating notification: {e}") errors += 1 if dry_run: logger.info("Dry run - rolling back changes") target_session.rollback() else: target_session.commit() logger.info(f"Migration complete: {migrated} migrated, {errors} errors") finally: source_session.close() target_session.close() def main(): parser = argparse.ArgumentParser(description='Migrate notifications') parser.add_argument('--source', required=True, help='Source database connection string') parser.add_argument('--target', help='Target database connection string') parser.add_argument('--dry-run', action='store_true', help='Dry run without committing') args = parser.parse_args() target = args.target if not target: import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) from shopdb import create_app app = create_app() target = app.config['SQLALCHEMY_DATABASE_URI'] run_migration(args.source, target, args.dry_run) if __name__ == '__main__': main()