#!/usr/bin/env python3 """ Location2025完全移行プログラム 7,641件の未移行ロケーションデータをLocation2025テーブルに移行 """ import os import sys from datetime import datetime # Django設定の初期化 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') sys.path.append('/opt/app') try: import django django.setup() from django.contrib.gis.geos import Point from django.db import models from rog.models import Location, Location2025, NewEvent2 except ImportError as e: print(f"Django import error: {e}") print("このスクリプトはDjangoコンテナ内で実行してください") sys.exit(1) def migrate_location_to_location2025(): """Location から Location2025 への完全移行""" print("=== Location2025完全移行開始 ===") try: # 現在の状況確認 total_location = Location.objects.count() current_location2025 = Location2025.objects.count() remaining = total_location - current_location2025 print(f"移行対象: {remaining}件 (全{total_location}件中{current_location2025}件移行済み)") if remaining <= 0: print("✅ すべてのLocationデータが既にLocation2025に移行済みです") return True # イベント確認(高山2以外の処理) locations_by_event = Location.objects.values('event_name').annotate( count=models.Count('id') ).order_by('-count') print("イベント別未移行データ:") for event_data in locations_by_event: event_name = event_data['event_name'] count = event_data['count'] # 既に移行済みのデータ数確認 try: event = NewEvent2.objects.get(event_code=event_name) migrated = Location2025.objects.filter(event_id=event.id).count() remaining_for_event = count - migrated print(f" {event_name}: {remaining_for_event}件未移行 (全{count}件)") except NewEvent2.DoesNotExist: print(f" {event_name}: NewEvent2未登録のため移行不可 ({count}件)") # バッチ移行処理 batch_size = 100 total_migrated = 0 # 高山2イベントのLocationデータを取得 takayama_locations = Location.objects.filter(event_name='高山2') if takayama_locations.exists(): # 高山2のNewEvent2エントリを取得または作成 try: takayama_event = NewEvent2.objects.filter(event_code='高山2').first() if not takayama_event: print("⚠️ 高山2イベントをNewEvent2に作成中...") takayama_event = NewEvent2.objects.create( event_code='高山2', event_name='岐阜ロゲin高山2', event_date=datetime(2025, 2, 11).date(), start_time=datetime(2025, 2, 11, 10, 0).time(), goal_time=datetime(2025, 2, 11, 15, 0).time(), explanation='移行により自動作成されたイベント' ) print(f"✅ 高山2イベント作成完了 (ID: {takayama_event.id})") else: print(f"✅ 高山2イベント (ID: {takayama_event.id}) 使用") except Exception as e: print(f"❌ 高山2イベント処理エラー: {e}") return False # 既存のLocation2025データと重複チェック existing_location2025_ids = set( Location2025.objects.filter(event_id=takayama_event.id).values_list('original_location_id', flat=True) ) # 未移行のLocationデータを取得 pending_locations = takayama_locations.exclude(id__in=existing_location2025_ids) pending_count = pending_locations.count() print(f"高山2イベント: {pending_count}件の未移行データを処理中...") # バッチ処理でLocation2025に移行 for i in range(0, pending_count, batch_size): batch_locations = list(pending_locations[i:i+batch_size]) location2025_objects = [] for location in batch_locations: # PostGIS Pointオブジェクト作成 point_geom = Point(float(location.longitude), float(location.latitude)) location2025_obj = Location2025( cp_number=location.cp_number, point=point_geom, score=location.score, event_id=takayama_event.id, original_location_id=location.id, create_time=location.create_time or datetime.now(), update_time=datetime.now() ) location2025_objects.append(location2025_obj) # 一括挿入 Location2025.objects.bulk_create(location2025_objects, ignore_conflicts=True) total_migrated += len(location2025_objects) print(f"移行進捗: {total_migrated}/{pending_count}件完了") # 移行結果確認 final_location2025_count = Location2025.objects.count() print(f"\n✅ 移行完了: Location2025テーブルに{final_location2025_count}件のデータ") print(f"今回移行: {total_migrated}件") # API互換性確認 print("\n=== API互換性確認 ===") test_checkpoints = Location2025.objects.filter( event_id=takayama_event.id )[:5] if test_checkpoints.exists(): print("✅ get_checkpoint_list API用サンプルデータ:") for cp in test_checkpoints: print(f" CP{cp.cp_number}: ({cp.point.x}, {cp.point.y}) - {cp.score}点") return True except Exception as e: print(f"❌ 移行エラー: {e}") return False def verify_migration_results(): """移行結果の検証""" print("\n=== 移行結果検証 ===") try: # データ数確認 location_count = Location.objects.count() location2025_count = Location2025.objects.count() print(f"Location (旧): {location_count}件") print(f"Location2025 (新): {location2025_count}件") if location2025_count >= location_count: print("✅ 完全移行成功") else: remaining = location_count - location2025_count print(f"⚠️ {remaining}件が未移行") # イベント別確認 events_with_data = Location2025.objects.values('event_id').annotate( count=models.Count('id') ) print("\nLocation2025イベント別データ数:") for event_data in events_with_data: try: event = NewEvent2.objects.get(id=event_data['event_id']) print(f" {event.event_code}: {event_data['count']}件") except NewEvent2.DoesNotExist: print(f" イベントID {event_data['event_id']}: {event_data['count']}件 (イベント情報なし)") return True except Exception as e: print(f"❌ 検証エラー: {e}") return False def main(): """メイン処理""" print("=== Location2025完全移行プログラム ===") print("目標: 残り7,641件のLocationデータをLocation2025に移行") # 移行実行 success = migrate_location_to_location2025() if success: # 結果検証 verify_migration_results() print("\n🎉 Location2025移行プログラム完了") else: print("\n❌ 移行に失敗しました") return 1 return 0 if __name__ == "__main__": exit(main())