almost finish migrate new circumstances
This commit is contained in:
298
rollback_image_paths.py
Normal file
298
rollback_image_paths.py
Normal file
@ -0,0 +1,298 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
データベースパス更新ロールバックスクリプト
|
||||
|
||||
バックアップファイルからパス情報を復元します。
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import django
|
||||
from pathlib import Path
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# Django settings setup
|
||||
BASE_DIR = Path(__file__).resolve().parent
|
||||
sys.path.append(str(BASE_DIR))
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
|
||||
django.setup()
|
||||
|
||||
from django.conf import settings
|
||||
from rog.models import GoalImages, CheckinImages
|
||||
from django.db import transaction
|
||||
import logging
|
||||
|
||||
# ロギング設定
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler(f'rollback_log_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class PathRollbackService:
|
||||
"""パスロールバックサービス"""
|
||||
|
||||
def __init__(self, backup_file):
|
||||
self.backup_file = backup_file
|
||||
self.rollback_stats = {
|
||||
'total_goal_images': 0,
|
||||
'restored_goal_images': 0,
|
||||
'total_checkin_images': 0,
|
||||
'restored_checkin_images': 0,
|
||||
'failed_restores': []
|
||||
}
|
||||
|
||||
def load_backup_data(self):
|
||||
"""バックアップデータを読み込み"""
|
||||
try:
|
||||
with open(self.backup_file, 'r', encoding='utf-8') as f:
|
||||
backup_data = json.load(f)
|
||||
|
||||
logger.info(f"バックアップファイル読み込み成功: {self.backup_file}")
|
||||
logger.info(f"バックアップ日時: {backup_data.get('backup_timestamp', 'Unknown')}")
|
||||
logger.info(f"GoalImages: {len(backup_data.get('goal_images', []))}件")
|
||||
logger.info(f"CheckinImages: {len(backup_data.get('checkin_images', []))}件")
|
||||
|
||||
return backup_data
|
||||
|
||||
except FileNotFoundError:
|
||||
raise Exception(f"バックアップファイルが見つかりません: {self.backup_file}")
|
||||
except json.JSONDecodeError:
|
||||
raise Exception(f"バックアップファイルの形式が不正です: {self.backup_file}")
|
||||
except Exception as e:
|
||||
raise Exception(f"バックアップファイル読み込みエラー: {str(e)}")
|
||||
|
||||
def rollback_goal_images(self, goal_images_backup):
|
||||
"""GoalImagesをロールバック"""
|
||||
logger.info("=== GoalImagesロールバック開始 ===")
|
||||
|
||||
self.rollback_stats['total_goal_images'] = len(goal_images_backup)
|
||||
restored_count = 0
|
||||
|
||||
with transaction.atomic():
|
||||
for backup_item in goal_images_backup:
|
||||
try:
|
||||
goal_img = GoalImages.objects.get(id=backup_item['id'])
|
||||
original_path = backup_item['original_path']
|
||||
|
||||
goal_img.goalimage = original_path
|
||||
goal_img.save()
|
||||
restored_count += 1
|
||||
|
||||
if restored_count <= 5: # 最初の5件のみログ出力
|
||||
logger.info(f"✅ GoalImage ID={goal_img.id}: パス復元完了")
|
||||
|
||||
except GoalImages.DoesNotExist:
|
||||
logger.warning(f"⚠️ GoalImage ID={backup_item['id']} が存在しません(削除済み)")
|
||||
self.rollback_stats['failed_restores'].append({
|
||||
'type': 'goal',
|
||||
'id': backup_item['id'],
|
||||
'reason': 'Record not found'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"❌ GoalImage ID={backup_item['id']} 復元エラー: {str(e)}")
|
||||
self.rollback_stats['failed_restores'].append({
|
||||
'type': 'goal',
|
||||
'id': backup_item['id'],
|
||||
'reason': str(e)
|
||||
})
|
||||
|
||||
self.rollback_stats['restored_goal_images'] = restored_count
|
||||
logger.info(f"GoalImagesロールバック完了: {restored_count}件復元")
|
||||
|
||||
def rollback_checkin_images(self, checkin_images_backup):
|
||||
"""CheckinImagesをロールバック"""
|
||||
logger.info("=== CheckinImagesロールバック開始 ===")
|
||||
|
||||
self.rollback_stats['total_checkin_images'] = len(checkin_images_backup)
|
||||
restored_count = 0
|
||||
|
||||
with transaction.atomic():
|
||||
for backup_item in checkin_images_backup:
|
||||
try:
|
||||
checkin_img = CheckinImages.objects.get(id=backup_item['id'])
|
||||
original_path = backup_item['original_path']
|
||||
|
||||
checkin_img.checkinimage = original_path
|
||||
checkin_img.save()
|
||||
restored_count += 1
|
||||
|
||||
if restored_count <= 5: # 最初の5件のみログ出力
|
||||
logger.info(f"✅ CheckinImage ID={checkin_img.id}: パス復元完了")
|
||||
|
||||
except CheckinImages.DoesNotExist:
|
||||
logger.warning(f"⚠️ CheckinImage ID={backup_item['id']} が存在しません(削除済み)")
|
||||
self.rollback_stats['failed_restores'].append({
|
||||
'type': 'checkin',
|
||||
'id': backup_item['id'],
|
||||
'reason': 'Record not found'
|
||||
})
|
||||
except Exception as e:
|
||||
logger.error(f"❌ CheckinImage ID={backup_item['id']} 復元エラー: {str(e)}")
|
||||
self.rollback_stats['failed_restores'].append({
|
||||
'type': 'checkin',
|
||||
'id': backup_item['id'],
|
||||
'reason': str(e)
|
||||
})
|
||||
|
||||
self.rollback_stats['restored_checkin_images'] = restored_count
|
||||
logger.info(f"CheckinImagesロールバック完了: {restored_count}件復元")
|
||||
|
||||
def generate_rollback_report(self):
|
||||
"""ロールバックレポートを生成"""
|
||||
logger.info("=== ロールバックレポート生成 ===")
|
||||
|
||||
total_restored = self.rollback_stats['restored_goal_images'] + self.rollback_stats['restored_checkin_images']
|
||||
total_processed = self.rollback_stats['total_goal_images'] + self.rollback_stats['total_checkin_images']
|
||||
|
||||
report = {
|
||||
'rollback_timestamp': datetime.now().isoformat(),
|
||||
'backup_file': self.backup_file,
|
||||
'summary': {
|
||||
'total_processed': total_processed,
|
||||
'total_restored': total_restored,
|
||||
'goal_images_restored': self.rollback_stats['restored_goal_images'],
|
||||
'checkin_images_restored': self.rollback_stats['restored_checkin_images'],
|
||||
'failed_restores': len(self.rollback_stats['failed_restores']),
|
||||
'success_rate': (total_restored / max(total_processed, 1) * 100)
|
||||
},
|
||||
'failed_restores': self.rollback_stats['failed_restores']
|
||||
}
|
||||
|
||||
# レポートファイルの保存
|
||||
report_file = f'rollback_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.json'
|
||||
with open(report_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, ensure_ascii=False, indent=2)
|
||||
|
||||
# コンソール出力
|
||||
print("\n" + "="*60)
|
||||
print("🔄 ロールバックレポート")
|
||||
print("="*60)
|
||||
print(f"📄 バックアップファイル: {self.backup_file}")
|
||||
print(f"📊 処理総数: {total_processed:,}件")
|
||||
print(f"✅ 復元成功: {total_restored:,}件 ({report['summary']['success_rate']:.1f}%)")
|
||||
print(f" - ゴール画像: {self.rollback_stats['restored_goal_images']:,}件")
|
||||
print(f" - チェックイン画像: {self.rollback_stats['restored_checkin_images']:,}件")
|
||||
print(f"❌ 失敗: {len(self.rollback_stats['failed_restores'])}件")
|
||||
print(f"📄 詳細レポート: {report_file}")
|
||||
|
||||
if len(self.rollback_stats['failed_restores']) > 0:
|
||||
print("\n⚠️ 失敗した復元:")
|
||||
for failure in self.rollback_stats['failed_restores'][:5]:
|
||||
print(f" - {failure['type']} ID={failure['id']}: {failure['reason']}")
|
||||
if len(self.rollback_stats['failed_restores']) > 5:
|
||||
print(f" ... 他 {len(self.rollback_stats['failed_restores']) - 5} 件")
|
||||
|
||||
return report
|
||||
|
||||
def run_rollback(self):
|
||||
"""メインロールバック処理"""
|
||||
logger.info("🔄 ロールバック開始")
|
||||
print("🔄 データベースパスをロールバックします...")
|
||||
|
||||
try:
|
||||
# 1. バックアップデータ読み込み
|
||||
backup_data = self.load_backup_data()
|
||||
|
||||
# 2. GoalImagesロールバック
|
||||
if backup_data.get('goal_images'):
|
||||
self.rollback_goal_images(backup_data['goal_images'])
|
||||
|
||||
# 3. CheckinImagesロールバック
|
||||
if backup_data.get('checkin_images'):
|
||||
self.rollback_checkin_images(backup_data['checkin_images'])
|
||||
|
||||
# 4. レポート生成
|
||||
report = self.generate_rollback_report()
|
||||
|
||||
logger.info("✅ ロールバック完了")
|
||||
print("\n✅ ロールバックが完了しました!")
|
||||
|
||||
return report
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"💥 ロールバック中に重大なエラーが発生: {str(e)}")
|
||||
print(f"\n💥 ロールバックエラー: {str(e)}")
|
||||
raise
|
||||
|
||||
def list_backup_files():
|
||||
"""利用可能なバックアップファイルをリスト表示"""
|
||||
backup_files = []
|
||||
for file in Path('.').glob('path_update_backup_*.json'):
|
||||
try:
|
||||
with open(file, 'r', encoding='utf-8') as f:
|
||||
backup_data = json.load(f)
|
||||
|
||||
timestamp = backup_data.get('backup_timestamp', 'Unknown')
|
||||
goal_count = len(backup_data.get('goal_images', []))
|
||||
checkin_count = len(backup_data.get('checkin_images', []))
|
||||
|
||||
backup_files.append({
|
||||
'file': str(file),
|
||||
'timestamp': timestamp,
|
||||
'goal_count': goal_count,
|
||||
'checkin_count': checkin_count
|
||||
})
|
||||
except:
|
||||
continue
|
||||
|
||||
return backup_files
|
||||
|
||||
def main():
|
||||
"""メイン関数"""
|
||||
print("="*60)
|
||||
print("🔄 データベースパスロールバックツール")
|
||||
print("="*60)
|
||||
|
||||
# バックアップファイルのリスト表示
|
||||
backup_files = list_backup_files()
|
||||
|
||||
if not backup_files:
|
||||
print("❌ バックアップファイルが見つかりません。")
|
||||
print(" path_update_backup_*.json ファイルが存在することを確認してください。")
|
||||
return
|
||||
|
||||
print("利用可能なバックアップファイル:")
|
||||
for i, backup in enumerate(backup_files, 1):
|
||||
print(f"{i}. {backup['file']}")
|
||||
print(f" 日時: {backup['timestamp']}")
|
||||
print(f" GoalImages: {backup['goal_count']:,}件, CheckinImages: {backup['checkin_count']:,}件")
|
||||
print()
|
||||
|
||||
# バックアップファイル選択
|
||||
try:
|
||||
choice = input(f"ロールバックするファイルを選択してください [1-{len(backup_files)}]: ").strip()
|
||||
choice_idx = int(choice) - 1
|
||||
|
||||
if choice_idx < 0 or choice_idx >= len(backup_files):
|
||||
print("❌ 無効な選択です。")
|
||||
return
|
||||
|
||||
selected_backup = backup_files[choice_idx]['file']
|
||||
|
||||
except (ValueError, KeyboardInterrupt):
|
||||
print("❌ ロールバックをキャンセルしました。")
|
||||
return
|
||||
|
||||
# 確認プロンプト
|
||||
print(f"\n⚠️ 以下のファイルからロールバックします:")
|
||||
print(f" {selected_backup}")
|
||||
print()
|
||||
print("このロールバックにより、現在のS3パス情報は元のローカルパスに戻ります。")
|
||||
|
||||
confirm = input("ロールバックを実行しますか? [y/N]: ").strip().lower()
|
||||
if confirm not in ['y', 'yes']:
|
||||
print("ロールバックをキャンセルしました。")
|
||||
return
|
||||
|
||||
# ロールバック実行
|
||||
rollback_service = PathRollbackService(selected_backup)
|
||||
rollback_service.run_rollback()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user