Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixAPI/scripts/monitor_migration.py
2026-01-08 10:26:35 +00:00

236 lines
9.8 KiB
Python
Executable file

#!/usr/bin/env python3
"""
اسکریپت مانیتور پیشرفت Migration
این اسکریپت وضعیت migration را نشان می‌دهد
"""
import json
import sys
import os
from pathlib import Path
from datetime import datetime
from typing import Dict, Any
def load_checkpoint(checkpoint_file: str = 'migration_checkpoint.json') -> Dict[str, Any]:
"""بارگذاری checkpoint"""
checkpoint_path = Path(checkpoint_file)
if not checkpoint_path.exists():
return {}
try:
with open(checkpoint_path, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"❌ خطا در خواندن checkpoint: {e}")
return {}
def get_mysql_table_count(mysql_config: Dict[str, Any]) -> Dict[str, int]:
"""دریافت تعداد ردیف‌های جداول MySQL"""
try:
from sqlalchemy import create_engine, text
mysql_dsn = f"mysql+pymysql://{mysql_config['user']}:{mysql_config['password']}@{mysql_config['host']}:{mysql_config['port']}/{mysql_config['database']}"
engine = create_engine(mysql_dsn, echo=False, pool_pre_ping=True, connect_args={'connect_timeout': 5})
with engine.connect() as conn:
result = conn.execute(text("""
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = :schema
AND table_name != 'alembic_version'
ORDER BY table_name
"""), {'schema': mysql_config['database']})
counts = {}
for row in result:
counts[row[0]] = row[1] if row[1] else 0
engine.dispose()
return counts
except Exception as e:
print(f"⚠️ خطا در اتصال به MySQL: {e}")
return {}
def get_postgres_table_count(postgres_config: Dict[str, Any]) -> Dict[str, int]:
"""دریافت تعداد ردیف‌های جداول PostgreSQL"""
try:
from sqlalchemy import create_engine, text
from urllib.parse import quote_plus
postgres_dsn = f"postgresql+psycopg2://{postgres_config['user']}:{quote_plus(postgres_config['password'])}@{postgres_config['host']}:{postgres_config['port']}/{postgres_config['database']}"
engine = create_engine(postgres_dsn, echo=False, pool_pre_ping=True, connect_args={'connect_timeout': 5})
with engine.connect() as conn:
result = conn.execute(text("""
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
AND table_type = 'BASE TABLE'
AND table_name != 'alembic_version'
ORDER BY table_name
"""))
counts = {}
for row in result:
table_name = row[0]
count_result = conn.execute(text(f'SELECT COUNT(*) FROM "{table_name}"'))
counts[table_name] = count_result.scalar()
engine.dispose()
return counts
except Exception as e:
print(f"⚠️ خطا در اتصال به PostgreSQL: {e}")
return {}
def format_number(num: int) -> str:
"""فرمت عدد با کاما"""
return f"{num:,}"
def calculate_progress(checkpoint_data: Dict[str, Any], mysql_counts: Dict[str, int], postgres_counts: Dict[str, int]) -> Dict[str, Any]:
"""محاسبه پیشرفت"""
tables_data = checkpoint_data.get('tables', {})
total_tables = len(mysql_counts)
completed_tables = sum(1 for t in tables_data.values() if t.get('completed', False))
total_rows_mysql = sum(mysql_counts.values())
total_rows_postgres = sum(postgres_counts.values())
completed_tables_list = []
in_progress_tables = []
failed_tables = []
for table_name, table_data in tables_data.items():
if table_data.get('completed', False):
completed_tables_list.append(table_name)
elif table_data.get('rows_migrated', 0) > 0:
in_progress_tables.append({
'name': table_name,
'migrated': table_data.get('rows_migrated', 0),
'total': mysql_counts.get(table_name, 0),
'last_id': table_data.get('last_id'),
})
# جداول کامل شده اما در checkpoint نیستند
for table_name in postgres_counts:
if table_name not in tables_data and postgres_counts[table_name] > 0:
if postgres_counts[table_name] == mysql_counts.get(table_name, 0):
completed_tables_list.append(table_name)
completed_tables += 1
progress_percent = (completed_tables / total_tables * 100) if total_tables > 0 else 0
rows_progress_percent = (total_rows_postgres / total_rows_mysql * 100) if total_rows_mysql > 0 else 0
return {
'total_tables': total_tables,
'completed_tables': completed_tables,
'progress_percent': progress_percent,
'total_rows_mysql': total_rows_mysql,
'total_rows_postgres': total_rows_postgres,
'rows_progress_percent': rows_progress_percent,
'completed_tables_list': completed_tables_list,
'in_progress_tables': in_progress_tables,
'failed_tables': failed_tables,
}
def print_status(checkpoint_data: Dict[str, Any], progress: Dict[str, Any]):
"""نمایش وضعیت"""
print("="*70)
print("📊 وضعیت Migration")
print("="*70)
if checkpoint_data.get('started_at'):
started = datetime.fromisoformat(checkpoint_data['started_at'])
print(f"🕐 شروع: {started.strftime('%Y-%m-%d %H:%M:%S')}")
if checkpoint_data.get('updated_at'):
updated = datetime.fromisoformat(checkpoint_data['updated_at'])
elapsed = datetime.now() - updated
print(f"🕐 آخرین بروزرسانی: {updated.strftime('%Y-%m-%d %H:%M:%S')} ({elapsed.total_seconds():.0f} ثانیه پیش)")
print()
print(f"📋 جداول:")
print(f" ✅ کامل شده: {progress['completed_tables']}/{progress['total_tables']} ({progress['progress_percent']:.1f}%)")
print(f" ⏳ در حال انجام: {len(progress['in_progress_tables'])}")
print()
print(f"📊 ردیف‌ها:")
print(f" MySQL: {format_number(progress['total_rows_mysql'])}")
print(f" PostgreSQL: {format_number(progress['total_rows_postgres'])} ({progress['rows_progress_percent']:.1f}%)")
print()
print("="*70)
print(f"🎯 پیشرفت کلی: {progress['progress_percent']:.1f}%")
print("="*70)
if progress['in_progress_tables']:
print()
print("⏳ جداول در حال انجام:")
for table in progress['in_progress_tables'][:5]: # فقط 5 تا اول
table_progress = (table['migrated'] / table['total'] * 100) if table['total'] > 0 else 0
print(f" 📊 {table['name']}: {format_number(table['migrated'])}/{format_number(table['total'])} ({table_progress:.1f}%)")
if len(progress['in_progress_tables']) > 5:
print(f" ... و {len(progress['in_progress_tables']) - 5} جدول دیگر")
print()
def main():
import argparse
parser = argparse.ArgumentParser(description='مانیتور پیشرفت Migration')
parser.add_argument('--checkpoint-file', default='migration_checkpoint.json', help='Checkpoint file path')
parser.add_argument('--mysql-host', default='185.8.172.57', help='MySQL host')
parser.add_argument('--mysql-user', default='root', help='MySQL user')
parser.add_argument('--mysql-password', default='136431', help='MySQL password')
parser.add_argument('--mysql-database', default='hesabixpy', help='MySQL database')
parser.add_argument('--postgres-host', default='localhost', help='PostgreSQL host')
parser.add_argument('--postgres-user', default='hesabix', help='PostgreSQL user')
parser.add_argument('--postgres-password', default='@@babaK24055', help='PostgreSQL password')
parser.add_argument('--postgres-database', default='hesabix', help='PostgreSQL database')
parser.add_argument('--watch', action='store_true', help='Watch mode - نمایش مداوم')
parser.add_argument('--interval', type=int, default=5, help='Interval برای watch mode (ثانیه)')
args = parser.parse_args()
mysql_config = {
'host': args.mysql_host,
'user': args.mysql_user,
'password': args.mysql_password,
'database': args.mysql_database,
'port': 3306,
}
postgres_config = {
'host': args.postgres_host,
'user': args.postgres_user,
'password': args.postgres_password,
'database': args.postgres_database,
'port': 5432,
}
if args.watch:
import time
while True:
os.system('clear')
checkpoint_data = load_checkpoint(args.checkpoint_file)
mysql_counts = get_mysql_table_count(mysql_config)
postgres_counts = get_postgres_table_count(postgres_config)
progress = calculate_progress(checkpoint_data, mysql_counts, postgres_counts)
print_status(checkpoint_data, progress)
print(f"\n⏳ بروزرسانی بعدی در {args.interval} ثانیه... (Ctrl+C برای خروج)")
time.sleep(args.interval)
else:
checkpoint_data = load_checkpoint(args.checkpoint_file)
if not checkpoint_data:
print("⚠️ Checkpoint file یافت نشد. Migration شروع نشده است.")
sys.exit(1)
mysql_counts = get_mysql_table_count(mysql_config)
postgres_counts = get_postgres_table_count(postgres_config)
progress = calculate_progress(checkpoint_data, mysql_counts, postgres_counts)
print_status(checkpoint_data, progress)
if __name__ == '__main__':
main()